app

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 1, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

internal/app/download.go

internal/app/preview.go

internal/app/security.go

internal/app/server.go

Index

Constants

View Source
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

func ValidateFontDirectory(dir string) error

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

func ValidateFontFile(path string, maxFileSize int64) error

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:

  1. Reads the converted directory contents
  2. Checks each file's modification time
  3. Removes files older than cache time
  4. 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

func (c *Config) Validate() error

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,
}

func (*FontError) Error

func (e *FontError) Error() string

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

func (s *Server) Shutdown(ctx context.Context) error

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

func (s *Server) Start() error

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL