openapi: 3.0.3
info:
  title: Online Font Converter API
  description: |
    A RESTful API for converting font files between various formats.
    
    ## Features
    - Multi-file font conversion with support for TTF, OTF, WOFF, WOFF2, EOT, SVG, and UFO formats
    - Real-time progress updates via Server-Sent Events (SSE)
    - Batch conversion with ZIP or TAR archive downloads
    - Service health monitoring and dependency status
    
    ## Rate Limits
    - Maximum 5 concurrent jobs per IP address
    - Maximum 25 MB per file
    - Maximum 50 MB per batch
    - Maximum 20 files per batch
    
    ## Workflow
    1. Submit a conversion job via POST /api/convert with font files and target formats
    2. Monitor progress via GET /api/jobs/{id} or SSE stream at /api/jobs/{id}/events
    3. Download results via GET /api/jobs/{id}/download when status is COMPLETE
  version: 1.0.0-ktor
  contact:
    name: Online Font Converter
    url: https://onlinefontconverter.com
  license:
    name: See LICENSE.txt
servers:
  - url: https://onlinefontconverter.com
    description: Local development server
  - url: https://onlinefontconverter.com
    description: Production server

tags:
  - name: status
    description: Service health and capabilities
  - name: formats
    description: Supported font formats and conversion matrix
  - name: jobs
    description: Font conversion job operations

paths:
  /api/status:
    get:
      summary: Get service status
      description: Returns service health, dependency status, and capabilities
      operationId: getStatus
      tags:
        - status
      responses:
        '200':
          description: Service is healthy and ready
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'
        '503':
          description: Service is unavailable (missing dependencies)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusResponse'

  /api/formats:
    get:
      summary: Get supported formats
      description: Returns all supported font formats and their conversion capabilities
      operationId: getFormats
      tags:
        - formats
      responses:
        '200':
          description: Supported formats and conversion matrix
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormatsResponse'

  /api/convert:
    post:
      summary: Submit a font conversion job
      description: |
        Upload font files and specify target formats for conversion.
        Returns a job ID for tracking progress.
      operationId: submitConversion
      tags:
        - jobs
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - files
                - targetFormats
              properties:
                files:
                  type: array
                  description: Font files to convert
                  items:
                    type: string
                    format: binary
                targetFormats:
                  type: string
                  description: Comma-separated list of target formats (e.g., "TTF,WOFF,WOFF2")
                  example: "TTF,WOFF,WOFF2"
                output:
                  type: string
                  description: Archive format for results
                  enum: [zip, tar]
                  default: zip
                sessionId:
                  type: string
                  description: Optional session ID for updating an existing job
      responses:
        '202':
          description: Job accepted for processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConvertResponse'
        '400':
          description: Invalid request (no files, invalid formats, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: File or batch size exceeds limits
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PayloadTooLargeError'
        '429':
          description: Too many concurrent jobs for this IP
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/jobs/{id}:
    get:
      summary: Get job status
      description: Returns the current status and progress of a conversion job
      operationId: getJobStatus
      tags:
        - jobs
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Job status retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
        '400':
          description: Invalid job ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Job not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/jobs/{id}/conversions:
    get:
      summary: Get detailed conversion information
      description: Returns detailed status for each file-to-format conversion in the job
      operationId: getJobConversions
      tags:
        - jobs
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Conversion details retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobConversionsResponse'
        '400':
          description: Invalid job ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Job not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/jobs/{id}/events:
    get:
      summary: Stream job progress events
      description: Server-Sent Events (SSE) stream providing real-time job progress updates
      operationId: getJobEvents
      tags:
        - jobs
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: SSE stream of job progress events
          content:
            text/event-stream:
              schema:
                type: object
                description: Stream of JobEvent objects

  /api/jobs/{id}/download:
    get:
      summary: Download conversion results
      description: Download the archive containing all converted font files
      operationId: downloadJobResults
      tags:
        - jobs
      parameters:
        - name: id
          in: path
          required: true
          description: Job ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Archive file download
          content:
            application/zip:
              schema:
                type: string
                format: binary
            application/x-tar:
              schema:
                type: string
                format: binary
        '400':
          description: Invalid job ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Job not found or not complete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    StatusResponse:
      type: object
      properties:
        service:
          type: string
          example: "Online Font Converter"
        version:
          type: string
          example: "1.0.0-ktor"
        healthy:
          type: boolean
          description: Whether all dependencies are satisfied
        formats:
          $ref: '#/components/schemas/SupportedFormats'
        limits:
          $ref: '#/components/schemas/LimitsInfo'
        dependencies:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/DependencyInfo'
        blockedReason:
          type: string
          nullable: true
          description: Reason why service is unavailable (if unhealthy)
        timestamp:
          type: string
          format: date-time

    SupportedFormats:
      type: object
      properties:
        input:
          type: array
          items:
            type: string
        output:
          type: array
          items:
            type: string

    LimitsInfo:
      type: object
      properties:
        maxFileSize:
          type: integer
          format: int64
          description: Maximum file size in bytes
          example: 26214400
        maxBatchSize:
          type: integer
          format: int64
          description: Maximum total batch size in bytes
          example: 52428800
        maxFilesPerBatch:
          type: integer
          description: Maximum number of files per batch
          example: 20
        maxConcurrentJobsPerIp:
          type: integer
          description: Maximum concurrent jobs per IP address
          example: 5

    DependencyInfo:
      type: object
      properties:
        installed:
          type: boolean
        version:
          type: string
        notes:
          type: string

    FormatsResponse:
      type: object
      properties:
        formats:
          type: array
          items:
            $ref: '#/components/schemas/FormatInfo'

    FormatInfo:
      type: object
      properties:
        code:
          type: string
          description: Format code (e.g., "TTF", "WOFF")
        category:
          type: string
          description: Format category (e.g., "OUTLINE", "WEB")
        inputSupported:
          type: boolean
          description: Can be used as input format
        outputSupported:
          type: boolean
          description: Can be used as output format
        defaultTargets:
          type: array
          items:
            type: string
          description: Recommended default target formats
        supportedTargets:
          type: array
          items:
            type: string
          description: All supported target formats for conversion

    ConvertResponse:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
        status:
          type: string
          enum: [QUEUED, PROCESSING, COMPLETE, FAILED]
        message:
          type: string
        statusUrl:
          type: string
          description: URL to poll for job status
        eventsUrl:
          type: string
          description: URL for SSE stream of job events

    JobStatusResponse:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
        status:
          type: string
          enum: [QUEUED, PROCESSING, COMPLETE, FAILED]
        progress:
          $ref: '#/components/schemas/JobProgress'
        submittedAt:
          type: string
          format: date-time
        totalConversions:
          type: integer
        completedConversions:
          type: integer
        successfulConversions:
          type: integer
        failedConversions:
          type: integer
        skippedConversions:
          type: integer
        sourceFileCount:
          type: integer
        requestedFormatCount:
          type: integer
        sourceFileNames:
          type: array
          items:
            type: string
        downloadUrl:
          type: string
          nullable: true
          description: Download URL (only available when status is COMPLETE)
        error:
          type: string
          nullable: true

    JobProgress:
      type: object
      properties:
        uploadPct:
          type: integer
          minimum: 0
          maximum: 100
        processingPct:
          type: integer
          minimum: 0
          maximum: 100
        packagePct:
          type: integer
          minimum: 0
          maximum: 100
        currentPhase:
          type: string

    JobConversionsResponse:
      type: object
      properties:
        jobId:
          type: string
          format: uuid
        conversions:
          type: array
          items:
            $ref: '#/components/schemas/ConversionDetail'

    ConversionDetail:
      type: object
      properties:
        sourceFileId:
          type: string
          format: uuid
        sourceName:
          type: string
        targetFormat:
          type: string
        status:
          type: string
          enum: [PENDING, IN_PROGRESS, SUCCEEDED, FAILED, SKIPPED]
        errorCode:
          type: string
          nullable: true
        errorMessage:
          type: string
          nullable: true
        errorDetails:
          type: string
          nullable: true

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
          nullable: true

    PayloadTooLargeError:
      type: object
      properties:
        errorCode:
          type: string
          enum: [FILE_TOO_LARGE, BATCH_TOO_LARGE]
        error:
          type: string
        maxFileSize:
          type: integer
          format: int64
          nullable: true
        fileName:
          type: string
          nullable: true
        fileSize:
          type: integer
          format: int64
          nullable: true
        maxBatchSize:
          type: integer
          format: int64
          nullable: true
        currentBatchSize:
          type: integer
          format: int64
          nullable: true
