Documentation
¶
Overview ¶
internal/app/download.go
internal/app/preview.go
internal/app/security.go
internal/app/server.go
Index ¶
- Constants
- func ValidateFontDirectory(dir string) error
- func ValidateFontFile(path string, maxFileSize int64) error
- type CleanupManager
- type Config
- type ConversionJob
- type ConversionProgress
- type FontError
- type FontPreview
- type FontProcessError
- type FontVariant
- type PaginatedFontResponse
- type PreviewGenerator
- func (pg *PreviewGenerator) Close()
- func (pg *PreviewGenerator) CountFonts(fontDir string) (int, error)
- func (pg *PreviewGenerator) GetProgressChan() chan string
- func (pg *PreviewGenerator) ProcessFonts(fontDir string) ([]FontPreview, error)
- func (pg *PreviewGenerator) ProcessFontsPaginated(fontDir string, page, pageSize int) (*PaginatedFontResponse, error)
- type ScanProgressCallback
- type Server
Constants ¶
const ( // DefaultPort is the default HTTP port the server will listen on if not specified via environment variable. DefaultPort = "8080" // DefaultMaxConcurrent is the default maximum number of concurrent font processing operations. // This limits resource usage during batch font conversions. DefaultMaxConcurrent = 4 // DefaultPreviewCacheTime is the default duration for caching font preview files. // This helps reduce server load by allowing browsers to cache font previews. DefaultPreviewCacheTime = 24 * time.Hour // DefaultFontSize is the default font size in points used for generating font previews. DefaultFontSize = 48.0 // DefaultMaxFileSize is the default maximum allowed size for font files (50MB). // Files larger than this will be rejected to prevent resource exhaustion. DefaultMaxFileSize = 50 * 1024 * 1024 // 50MB )
Variables ¶
This section is empty.
Functions ¶
func ValidateFontDirectory ¶
ValidateFontDirectory performs comprehensive security and accessibility checks on a directory path. It validates that the directory exists, is accessible, and is safe to process for fonts. This function helps prevent directory traversal attacks and ensures the application only processes legitimate font directories.
Security checks performed:
- Prevents access to root directories (/, C:\, etc.)
- Validates directory exists and is readable
- Ensures path is actually a directory, not a file
- Checks for valid directory names
Parameters:
- dir: The directory path to validate (can be relative or absolute)
Returns:
- nil if directory is valid and safe to process
- error describing the validation failure
Example usage:
if err := ValidateFontDirectory("/path/to/fonts"); err != nil {
return fmt.Errorf("invalid font directory: %v", err)
}
func ValidateFontFile ¶
ValidateFontFile performs validation on a font file including size checks. This function ensures a font file is safe to process by validating the path, checking file existence and accessibility, verifying it's a regular file, and enforcing size limits to prevent resource exhaustion.
SECURITY: This function uses Lstat to detect symlinks BEFORE following them, preventing symlink-based attacks. It also validates the file from an open file descriptor to prevent TOCTOU (time-of-check-time-of-use) race conditions.
Validation checks performed:
- Path safety validation using isPathAllowed
- Symlink detection and rejection (prevents symlink attacks)
- File descriptor-based validation (prevents TOCTOU races)
- Regular file type confirmation (not directory or special file)
- File size limit enforcement
- Empty file detection
Parameters:
- path: Path to the font file to validate
- maxFileSize: Maximum allowed file size in bytes
Returns:
- nil if file is valid and safe to process
- error describing the validation failure
Types ¶
type CleanupManager ¶
type CleanupManager struct {
// contains filtered or unexported fields
}
CleanupManager handles automatic cleanup of temporary and cached files. It manages the removal of old converted font files based on cache time settings and provides scheduled cleanup functionality to prevent disk space accumulation.
The manager operates on the "converted" directory within the static directory, removing files older than the configured PreviewCacheTime.
func NewCleanupManager ¶
func NewCleanupManager(config *Config) *CleanupManager
NewCleanupManager creates a new CleanupManager instance with the given configuration. The manager will use the configuration to determine cache time limits and directory locations for cleanup operations.
Parameters:
- config: Configuration containing cache time and directory settings
Returns:
- A new CleanupManager instance ready for use
func (*CleanupManager) CleanOldFiles ¶
func (cm *CleanupManager) CleanOldFiles() error
CleanOldFiles removes font files older than the configured cache time. This method scans the converted directory and deletes files whose modification time is older than the PreviewCacheTime setting. It helps prevent disk space accumulation from temporary font conversions.
The cleanup process:
- Reads the converted directory contents
- Checks each file's modification time
- Removes files older than cache time
- Logs successful removals and errors
Returns:
- nil if cleanup completed (even if some files couldn't be removed)
- error if the converted directory cannot be read
func (*CleanupManager) ScheduleCleanup ¶
func (cm *CleanupManager) ScheduleCleanup(ctx context.Context)
ScheduleCleanup starts a background goroutine that performs periodic cleanup. The cleanup runs every 6 hours and continues until the context is cancelled. This method should be called once during application startup.
The scheduled cleanup:
- Runs automatically every 6 hours
- Respects context cancellation for graceful shutdown
- Logs cleanup failures but continues running
- Stops the ticker when context is done
Parameters:
- ctx: Context for cancellation and graceful shutdown
type Config ¶
type Config struct {
Port string // HTTP port to listen on
StaticDir string // Directory containing static web assets
LogDir string // Directory for log files
MaxConcurrent int // Maximum concurrent font processing operations
PreviewCacheTime time.Duration // Duration to cache font previews in browsers
FontSize float64 // Font size for preview generation
MaxFileSize int64 // Maximum allowed font file size in bytes
}
Config holds all configuration settings for the font preview server. It includes server settings, directory paths, processing limits, and caching options. Configuration values can be set via environment variables or will use sensible defaults.
Environment variables supported:
- PORT: Server port (default: 8080)
- MAX_CONCURRENT: Maximum concurrent processing operations (default: 4)
- MAX_FILE_SIZE: Maximum font file size in bytes (default: 50MB)
func LoadConfig ¶
func LoadConfig() *Config
LoadConfig creates and returns a new Config instance with values loaded from environment variables or defaults. It automatically creates necessary directories and validates configuration settings.
Environment variables supported:
- PORT: HTTP server port (default: "8080")
- MAX_CONCURRENT: Maximum concurrent processing operations (default: 4)
- MAX_FILE_SIZE: Maximum font file size in bytes (default: 50MB)
Returns:
- A fully configured Config instance ready for use
Example usage:
config := LoadConfig() server := NewServer(NewPreviewGenerator(config))
func (*Config) Validate ¶
Validate checks the configuration for valid values and creates necessary directories. It ensures all required directories exist and configuration values are within acceptable ranges.
Validation checks performed:
- Port is not empty
- MaxConcurrent is at least 1
- FontSize is positive
- Required directories (StaticDir, LogDir) exist or can be created
Returns:
- nil if configuration is valid
- error describing the first validation failure encountered
Example usage:
config := LoadConfig()
if err := config.Validate(); err != nil {
log.Fatal("Invalid configuration:", err)
}
type ConversionJob ¶
type ConversionJob struct {
// contains filtered or unexported fields
}
ConversionJob represents a single font conversion job used internally by the worker pool. It contains all necessary information to convert a font from one format to another, including source file details and target output path.
Fields:
- variant: The FontVariant being processed
- sourceFile: Absolute path to the source font file
- sourceFormat: File extension of the source format (e.g., ".ttf", ".woff2")
- outputPath: Absolute path where the converted file should be saved
type ConversionProgress ¶
type ConversionProgress struct {
Total int `json:"total"` // Total fonts to process
Current int `json:"current"` // Fonts processed so far
CurrentFont string `json:"currentFont"` // Currently processing font name
Stage string `json:"stage"` // Current processing stage
}
ConversionProgress represents the progress of font conversion operations. It provides real-time updates about batch font processing status and is used for progress reporting via server-sent events to the web interface.
JSON fields:
- total: Total number of fonts to process
- current: Number of fonts processed so far
- currentFont: Name of the font currently being processed
- stage: Description of the current processing stage
type FontError ¶
type FontError struct {
Op string // Operation that failed
Path string // File path if relevant
Err error // Original error
}
FontError represents a custom error type for font-related operations. It wraps the underlying error with additional context about the operation that failed and the file path if applicable.
Example usage:
err := &FontError{
Op: "read",
Path: "/path/to/font.ttf",
Err: originalErr,
}
type FontPreview ¶
type FontPreview struct {
Name string `json:"name"` // Display name of the font
Preview string `json:"preview"` // URL for preview display
Formats map[string]string `json:"formats"` // Map of extensions to download URLs
}
FontPreview represents a font and its preview information for the web interface. It contains the font name, a preview URL for display, and download URLs for all available format variations of the font.
JSON fields:
- name: The display name of the font (without extension)
- preview: URL to the best available format for preview (WOFF2 preferred)
- formats: Map of file extensions to download URLs (e.g., ".ttf" -> "/download?path=...")
type FontProcessError ¶
type FontProcessError struct {
Op string // Operation being performed
Path string // File path if applicable
Err error // Underlying error
}
FontProcessError represents a custom error type for font processing operations. It provides detailed context about font conversion failures, including the specific operation that failed, the file path involved, and the underlying error.
Example usage:
err := &FontProcessError{
Op: "woff2_compress",
Path: "/path/to/font.ttf",
Err: originalErr,
}
func (*FontProcessError) Error ¶
func (e *FontProcessError) Error() string
type FontVariant ¶
type FontVariant struct {
Name string // Base name of the font
Location map[string]string // Map of extension -> download URL
PreviewPath string // URL to preferred preview format
// contains filtered or unexported fields
}
FontVariant represents a font with its different format variations during processing. It tracks all available formats of a single font family and maintains the best preview path for web display. This is used internally during font discovery and conversion.
THREAD SAFETY: This struct uses a mutex to protect concurrent access to Location map. Use SetLocation() and GetLocation() for thread-safe access.
Fields:
- Name: Base name of the font (without extension)
- Location: Map of file extensions to download URLs (protected by mu)
- PreviewPath: URL to the preferred format for preview (WOFF2 > WOFF > TTF/OTF)
func (*FontVariant) GetLocation ¶
func (v *FontVariant) GetLocation(ext string) (string, bool)
GetLocation safely gets a format location for thread-safe concurrent access.
func (*FontVariant) GetLocationsCopy ¶
func (v *FontVariant) GetLocationsCopy() map[string]string
GetLocationsCopy returns a copy of the Location map for safe iteration.
func (*FontVariant) GetPreviewPath ¶
func (v *FontVariant) GetPreviewPath() string
GetPreviewPath safely gets the preview path.
func (*FontVariant) SetLocation ¶
func (v *FontVariant) SetLocation(ext, url string)
SetLocation safely sets a format location for thread-safe concurrent access.
func (*FontVariant) SetPreviewPath ¶
func (v *FontVariant) SetPreviewPath(path string)
SetPreviewPath safely sets the preview path.
type PaginatedFontResponse ¶
type PaginatedFontResponse struct {
Fonts []FontPreview `json:"fonts"` // Current page of font previews
TotalCount int `json:"totalCount"` // Total number of fonts found
Page int `json:"page"` // Current page number (0-based)
PageSize int `json:"pageSize"` // Number of fonts per page
HasMore bool `json:"hasMore"` // Whether there are more pages
}
PaginatedFontResponse represents a paginated response for font processing. Used to return a subset of fonts along with pagination metadata to improve performance when dealing with large font collections.
type PreviewGenerator ¶
type PreviewGenerator struct {
// contains filtered or unexported fields
}
PreviewGenerator handles font preview generation and conversion operations. It manages concurrent font processing, caching, and progress reporting. The generator can convert between TTF, OTF, WOFF, and WOFF2 formats using external tools (woff2_compress and woff2_decompress).
Key features:
- Concurrent processing with configurable worker pool
- Progress reporting via channels for real-time updates
- Caching to avoid redundant conversions
- Graceful cancellation support
- Safe cleanup of resources
func NewPreviewGenerator ¶
func NewPreviewGenerator(config *Config) *PreviewGenerator
NewPreviewGenerator creates a new PreviewGenerator instance with the given configuration. It initializes the worker pool, progress channel, and sets up cancellation context. The generator is ready to process fonts immediately after creation.
Parameters:
- config: Configuration settings including MaxConcurrent worker limit
Returns:
- A fully initialized PreviewGenerator ready for font processing
Example usage:
config := LoadConfig()
generator := NewPreviewGenerator(config)
defer generator.Close()
previews, err := generator.ProcessFonts("/path/to/fonts")
func (*PreviewGenerator) Close ¶
func (pg *PreviewGenerator) Close()
Close cleans up resources used by the generator and stops all ongoing operations. It cancels the context to stop any running conversions, closes the progress channel, and clears the preview cache. This method should be called when the generator is no longer needed to prevent resource leaks.
Note: After calling Close, the generator should not be used for further operations.
Example usage:
generator := NewPreviewGenerator(config) defer generator.Close()
func (*PreviewGenerator) CountFonts ¶
func (pg *PreviewGenerator) CountFonts(fontDir string) (int, error)
CountFonts counts the total number of font variants (grouped by base name) in a directory. This matches the processing logic where fonts with the same base name but different formats are grouped together as a single FontVariant.
Parameters:
- fontDir: Path to the directory containing font files
Returns:
- int: Total number of font variants found
- error: Any error encountered during scanning
func (*PreviewGenerator) GetProgressChan ¶
func (pg *PreviewGenerator) GetProgressChan() chan string
GetProgressChan returns the progress channel for receiving real-time processing updates. This channel emits progress messages during font processing operations and is used by the HTTP server to provide server-sent events to the web interface.
Returns:
- A receive-only channel that emits progress update strings
Example usage:
progressChan := generator.GetProgressChan()
go func() {
for msg := range progressChan {
fmt.Printf("Progress: %s\n", msg)
}
}()
func (*PreviewGenerator) ProcessFonts ¶
func (pg *PreviewGenerator) ProcessFonts(fontDir string) ([]FontPreview, error)
ProcessFonts processes all fonts in the given directory and converts them to web-compatible formats. It discovers font files, converts them between formats as needed (TTF/OTF to WOFF2, WOFF2 to TTF), and returns a list of FontPreview objects for the web interface.
The function performs the following operations:
- Validates the input directory for security and accessibility
- Scans for font files (.ttf, .otf, .woff, .woff2)
- Creates missing format conversions (TTF/OTF → WOFF2, WOFF2 → TTF)
- Reports progress via the progress channel
- Returns structured data for the web interface
Parameters:
- fontDir: Path to the directory containing font files to process
Returns:
- []FontPreview: List of processed fonts with download URLs for all formats
- error: Any error encountered during processing
Error conditions:
- Directory does not exist or is not accessible
- No font files found in directory
- Font conversion tool failures
- File system errors during processing
Example usage:
previews, err := generator.ProcessFonts("/path/to/fonts")
if err != nil {
return fmt.Errorf("font processing failed: %v", err)
}
for _, preview := range previews {
fmt.Printf("Font: %s, Formats: %v\n", preview.Name, preview.Formats)
}
func (*PreviewGenerator) ProcessFontsPaginated ¶
func (pg *PreviewGenerator) ProcessFontsPaginated(fontDir string, page, pageSize int) (*PaginatedFontResponse, error)
ProcessFontsPaginated processes fonts with pagination support for better performance. This method processes only a subset of fonts based on page and pageSize parameters, allowing for progressive loading of large font collections.
Parameters:
- fontDir: Path to the directory containing font files to process
- page: Page number (0-based) to retrieve
- pageSize: Number of fonts per page (0 means no pagination - process all)
Returns:
- PaginatedFontResponse: Paginated response with fonts and metadata
- error: Any error encountered during processing
type ScanProgressCallback ¶
type ScanProgressCallback func(fontsFound, filesChecked int)
ScanProgressCallback reports progress during directory scanning. It receives the number of fonts found so far and the total files checked.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server represents the HTTP server for the font preview application. It handles all web requests including font processing, downloads, and static file serving. The server uses a PreviewGenerator for font conversion operations and maintains configuration settings for timeouts, directories, and processing limits.
func NewServer ¶
func NewServer(generator *PreviewGenerator) *Server
NewServer creates a new Server instance with the given PreviewGenerator. It loads the default configuration and returns a ready-to-use server. The server will handle HTTP requests for font processing, downloads, and web UI.
Parameters:
- generator: A PreviewGenerator instance for handling font conversions
Returns:
- A configured Server instance ready to start serving requests
Example usage:
config := LoadConfig() generator := NewPreviewGenerator(config) server := NewServer(generator) err := server.Start()
func (*Server) Shutdown ¶
Shutdown gracefully shuts down the server with a timeout. It waits for all in-flight requests to complete or until the timeout expires.
func (*Server) Start ¶
Start starts the HTTP server and begins listening for requests. It configures MIME types for font files, sets up all HTTP routes, and starts the server with appropriate timeouts for font processing operations.
The server will listen on the port specified in the configuration (default: 8080) and handle the following routes:
- / : Main web interface
- /generate : Font processing endpoint
- /progress : Server-sent events for processing progress
- /download : Individual font file downloads
- /download-all : Bulk font downloads
- /static/ : Static assets (CSS, JS, images)
Returns:
- An error if the server fails to start or encounters a fatal error
Note: This method blocks until the server is stopped or encounters an error.