import { config } from '../env'; const errorSchema = (example: string) => ({ type: 'object', properties: { error: { type: 'string', example }, }, }); const jsonContent = (schema: object) => ({ 'application/json': { schema }, }); export const handleSwaggerJson = async (): Promise => { const spec = { openapi: '3.0.0', info: { title: 'TeleUploader API', version: '1.0.0', description: 'Telegram-backed file uploader API with redirect-based downloads.', }, servers: [ { url: '/', description: 'Current environment', }, ], paths: { '/health': { get: { summary: 'Health Check', description: 'Checks database connectivity status.', responses: { '200': { description: 'Database is healthy', content: jsonContent({ type: 'object', properties: { status: { type: 'string', example: 'ok' }, }, }), }, '500': { description: 'Database or server is unhealthy', content: jsonContent({ type: 'object', properties: { status: { type: 'string', example: 'error' }, error: { type: 'string', example: 'DB Connection Failed' }, }, }), }, }, }, }, '/api/upload': { post: { summary: 'Upload File', description: 'Uploads a file to Telegram storage via multipart/form-data or JSON base64.', requestBody: { required: true, content: { 'multipart/form-data': { schema: { type: 'object', required: ['file'], properties: { file: { type: 'string', format: 'binary', description: 'File binary payload.', }, fileName: { type: 'string', description: 'Optional file name override.', }, }, }, }, 'application/json': { schema: { type: 'object', required: ['file'], properties: { file: { type: 'string', description: 'Base64 encoded file content.', }, fileName: { type: 'string', default: 'file', description: 'Optional file name.', }, }, }, }, }, }, responses: { '200': { description: 'Successful upload metadata.', content: jsonContent({ type: 'object', properties: { public_id: { type: 'string', example: 'xYz123' }, telegram_file_id: { type: 'string', example: 'BQACAgQAAxkBA...' }, telegram_file_unique_id: { type: 'string', example: 'AgAD8w...' }, storage_chat_id: { type: 'integer', example: -1001234567890 }, storage_message_id: { type: 'integer', example: 42 }, file_name: { type: 'string', example: 'document.pdf' }, mime_type: { type: 'string', example: 'application/pdf' }, size_bytes: { type: 'integer', example: 1048576 }, file_type: { type: 'string', example: 'document' }, uploader_id: { type: 'integer', example: 0 }, created_at: { type: 'string', format: 'date-time', example: '2026-05-18T10:00:00.000Z', }, download_url: { type: 'string', example: `${config.baseUrl}/f/xYz123`, }, }, }), }, '400': { description: 'Bad request.', content: jsonContent(errorSchema('No file provided')), }, '500': { description: 'Internal server error.', content: jsonContent(errorSchema('Upload failed')), }, }, }, }, '/f/{public_id}': { get: { summary: 'Redirect to Telegram File URL', description: 'Gets a fresh Telegram download URL and redirects with 302. Rate-limited by IP.', parameters: [ { name: 'public_id', in: 'path', required: true, description: 'Permanent public file ID.', schema: { type: 'string' }, }, ], responses: { '302': { description: 'Redirect to Telegram CDN URL.', headers: { Location: { schema: { type: 'string', example: 'https://api.telegram.org/file/botTOKEN/documents/file_0.pdf', }, }, }, }, '404': { description: 'File not found.', content: jsonContent(errorSchema('File not found')), }, '429': { description: 'Rate limit exceeded.', content: jsonContent(errorSchema('Rate limit exceeded')), }, '500': { description: 'Internal server error.', content: jsonContent(errorSchema('Server error')), }, }, }, }, '/file/{public_id}/info': { get: { summary: 'Get File Info', description: 'Gets saved file metadata by public ID.', parameters: [ { name: 'public_id', in: 'path', required: true, description: 'Permanent public file ID.', schema: { type: 'string' }, }, ], responses: { '200': { description: 'File metadata.', content: jsonContent({ type: 'object', properties: { public_id: { type: 'string', example: 'xYz123' }, file_name: { type: 'string', example: 'document.pdf' }, mime_type: { type: 'string', example: 'application/pdf' }, size_bytes: { type: 'integer', example: 1048576 }, file_type: { type: 'string', example: 'document' }, uploader_id: { type: 'integer', example: 0 }, created_at: { type: 'string', format: 'date-time', example: '2026-05-18T10:00:00.000Z', }, }, }), }, '400': { description: 'Missing public ID.', content: jsonContent(errorSchema('Missing file id')), }, '404': { description: 'File not found.', content: jsonContent(errorSchema('File not found')), }, '500': { description: 'Internal server error.', content: jsonContent(errorSchema('Server error')), }, }, }, }, }, }; return Response.json(spec, { status: 200, headers: { 'access-control-allow-origin': '*', }, }); }; export const handleSwaggerHtml = async (): Promise => { const html = ` TeleUploader API Documentation
`; return new Response(html, { status: 200, headers: { 'content-type': 'text/html; charset=utf-8', 'access-control-allow-origin': '*', 'x-content-type-options': 'nosniff', }, }); };