import swaggerJSDoc from 'swagger-jsdoc'; import path from 'path'; import fs from 'fs'; // Swagger JSDoc needs the source TypeScript files (not compiled JS) to read JSDoc comments // When running from dist/, we need to look for src/ at the project root // When running from src/, we use relative path // Check if we're running from dist (compiled) or src (development) const isRunningFromDist = __dirname.includes('dist'); const projectRoot = isRunningFromDist ? path.resolve(__dirname, '../..') // Go up from dist/docs to project root : path.resolve(__dirname, '..'); // Go up from src/docs to project root // Always use source TypeScript files from src/routes const routesPath = path.join(projectRoot, 'src/routes/*.ts'); console.log(`🔍 Swagger looking for routes at: ${routesPath}`); console.log(`📂 Running from: ${isRunningFromDist ? 'dist' : 'src'}`); console.log(`📂 Project root: ${projectRoot}`); // Check if route files exist (for debugging) try { const routesDir = path.join(projectRoot, 'src/routes'); if (fs.existsSync(routesDir)) { const files = fs.readdirSync(routesDir); console.log(`📁 Found ${files.length} route files in ${routesDir}`); const tsFiles = files.filter(f => f.endsWith('.ts')); console.log(`📄 TypeScript route files: ${tsFiles.length}`); } else { console.warn(`⚠️ Routes directory not found: ${routesDir}`); } // Also check if the glob pattern resolves correctly const testPattern = path.join(projectRoot, 'src/routes/*.ts'); console.log(`🔍 Using pattern: ${testPattern}`); } catch (error) { console.warn('⚠️ Could not check routes directory:', error); } export const swaggerSpec = swaggerJSDoc({ definition: { openapi: '3.0.0', info: { title: 'Zurri API', version: '1.0.0', description: 'Zurri Agents Marketplace API with Wallet Point System', }, servers: [ { url: process.env.API_BASE_URL || '/api', description: 'API Server' }, ], components: { securitySchemes: { bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT', }, }, schemas: { Agent: { type: 'object', properties: { id: { type: 'string', format: 'uuid', }, name: { type: 'string', example: 'PixelPainter', }, description: { type: 'string', example: 'I turn prompts into AI art!', }, endpoint: { type: 'string', format: 'uri', example: 'https://api.pixelpainter.ai/run', }, avatar: { type: 'string', format: 'uri', nullable: true, description: 'IPFS gateway URL for avatar image', }, category: { type: 'string', example: 'image generation', }, reputation: { type: 'number', format: 'float', example: 4.8, }, capabilities: { type: 'array', items: { type: 'string', }, example: ['text-to-image', 'image-editing'], }, pointsPerTask: { type: 'number', format: 'float', example: 10, }, status: { type: 'string', enum: ['pending', 'approved', 'rejected', 'suspended'], }, usageCount: { type: 'integer', example: 0, }, rating: { type: 'number', format: 'float', nullable: true, }, ratingCount: { type: 'integer', example: 0, }, creatorId: { type: 'string', format: 'uuid', }, createdAt: { type: 'string', format: 'date-time', }, updatedAt: { type: 'string', format: 'date-time', }, }, }, ChatMessage: { type: 'object', properties: { id: { type: 'string', format: 'uuid', }, role: { type: 'string', enum: ['user', 'assistant', 'system'], description: 'Message role (user = user message, assistant = agent response, system = system message)', }, content: { type: 'string', description: 'Message content', }, agentId: { type: 'string', format: 'uuid', }, userId: { type: 'string', format: 'uuid', nullable: true, }, conversationId: { type: 'string', nullable: true, description: 'Conversation ID this message belongs to', }, metadata: { type: 'object', description: 'Additional message metadata (files, IPFS hashes, etc.)', additionalProperties: true, }, createdAt: { type: 'string', format: 'date-time', }, }, }, ChatResponse: { type: 'object', properties: { response: { type: 'string', description: 'Agent\'s response message', }, conversationId: { type: 'string', description: 'Conversation ID for this chat session', }, metadata: { type: 'object', description: 'Additional response metadata from agent', additionalProperties: true, }, }, }, User: { type: 'object', properties: { id: { type: 'string', format: 'uuid', description: 'User ID', }, email: { type: 'string', format: 'email', description: 'User email address', }, name: { type: 'string', nullable: true, description: 'User display name', }, isAdmin: { type: 'boolean', default: false, description: 'Whether user has admin privileges', }, isCreator: { type: 'boolean', default: false, description: 'Whether user can create and manage agents', }, isActive: { type: 'boolean', default: true, description: 'Whether user account is active', }, createdAt: { type: 'string', format: 'date-time', description: 'Account creation timestamp', }, updatedAt: { type: 'string', format: 'date-time', description: 'Last update timestamp', }, }, }, }, }, security: [{ bearerAuth: [] }], }, apis: [routesPath], }); // Log Swagger spec generation try { const spec = swaggerSpec as any; // Type assertion for logging const paths = spec.paths ? Object.keys(spec.paths) : []; console.log(`📚 Swagger spec generated with ${paths.length} endpoints`); if (paths.length === 0) { console.warn('⚠️ No endpoints found in Swagger spec. Check route files.'); } } catch (error) { console.error('❌ Error generating Swagger spec:', error); }