import "reflect-metadata"; import express from "express"; import cors from "cors"; import dotenv from "dotenv"; import swaggerJsdoc from "swagger-jsdoc"; import swaggerUi from "swagger-ui-express"; import { AppDataSource } from "./utils/dataSource"; import authRoute from "./routes/auth"; import uploadRoute from "./routes/upload"; import suggestRoute from "./routes/suggest"; import wardrobeRoute from "./routes/wardrobe"; import profileRoute from "./routes/profile"; import avatarRoute from "./routes/avatar"; import chatRoute from "./routes/chat"; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json({ limit: "10mb" })); // Root endpoint with milestone information app.get("/", (req, res) => { res.json({ name: "StyleGPT Milestone 2", version: "1.0.0", description: "Visual Wardrobe Tagging & AI-Powered Outfit Suggestions", milestone: 2, features: [ "User Authentication (JWT-based)", "Visual Wardrobe Management", "AI-Powered Image Classification (Fashion-CLIP)", "Smart Outfit Suggestions", "User Profile Management" ], endpoints: { root: "/", docs: "/docs", health: "/health", api: { auth: "/api/auth", profile: "/api/profile", upload: "/api/upload", suggest: "/api/suggest", chat: "/api/chat", wardrobe: "/api/wardrobe", avatar: "/api/avatar" } }, technologies: [ "Node.js", "TypeScript", "Express.js", "PostgreSQL", "TypeORM", "Hugging Face Fashion-CLIP", "JWT Authentication" ] }); }); // Swagger/OpenAPI configuration const swaggerOptions: swaggerJsdoc.Options = { definition: { openapi: "3.0.0", info: { title: "StyleGPT Milestone 2 API", version: "1.0.0", description: "API documentation for StyleGPT Milestone 2 - Visual Wardrobe Tagging & AI-Powered Outfit Suggestions", contact: { name: "StyleGPT", url: "https://huggingface.co/spaces/nexusbert/StyleGPT-milestone2" } }, servers: [ { url: process.env.API_URL || `http://localhost:${process.env.PORT || 7860}`, description: "API Server" } ], components: { securitySchemes: { bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT" } }, schemas: { User: { type: "object", properties: { id: { type: "integer" }, name: { type: "string" }, email: { type: "string", format: "email" }, profilePicture: { type: "string", format: "uri", description: "Avatar render URL or base64 image" }, readyPlayerMeAvatarId: { type: "string", description: "Ready Player Me avatar ID" }, createdAt: { type: "string", format: "date-time" } } }, WardrobeItem: { type: "object", properties: { id: { type: "integer" }, imageUrl: { type: "string", format: "uri" }, category: { type: "string", description: "Classification: shirt, pants, dress, jacket, shoes, sneakers, boots, watch, glasses, bag, hat, jewelry, accessories, etc." }, style: { type: "string", enum: ["formal", "casual", "streetwear", "sportswear"], description: "Style classification" }, userId: { type: "integer" }, createdAt: { type: "string", format: "date-time" } } }, RegisterRequest: { type: "object", required: ["name", "email", "password"], properties: { name: { type: "string" }, email: { type: "string", format: "email" }, password: { type: "string", minLength: 6 } } }, LoginRequest: { type: "object", required: ["email", "password"], properties: { email: { type: "string", format: "email" }, password: { type: "string" } } }, AuthResponse: { type: "object", properties: { success: { type: "boolean" }, user: { $ref: "#/components/schemas/User" }, token: { type: "string" }, error: { type: "string" } } }, UploadRequest: { type: "object", required: ["image"], properties: { image: { type: "string", format: "binary", description: "Image file (JPEG, PNG, etc.)" } } }, SuggestRequest: { type: "object", required: ["message"], properties: { message: { type: "string" }, session_id: { type: "string" } } } } }, tags: [ { name: "Authentication", description: "User registration and login" }, { name: "Profile", description: "User profile management" }, { name: "Upload", description: "Wardrobe item upload and classification" }, { name: "Suggest", description: "AI-powered outfit suggestions with wardrobe" }, { name: "Chat", description: "Fashion chat without wardrobe" }, { name: "Avatar", description: "Ready Player Me avatar management" }, { name: "Health", description: "Health check endpoints" } ] }, apis: ["./src/routes/*.ts", "./src/index.ts"] }; const swaggerSpec = swaggerJsdoc(swaggerOptions); // Swagger UI endpoint app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, { customCss: ".swagger-ui .topbar { display: none }", customSiteTitle: "StyleGPT Milestone 2 API Docs" })); // Swagger JSON endpoint app.get("/docs.json", (req, res) => { res.setHeader("Content-Type", "application/json"); res.send(swaggerSpec); }); // Health check endpoint /** * @openapi * /health: * get: * summary: Health check endpoint * tags: [Health] * responses: * 200: * description: Service is healthy * content: * application/json: * schema: * type: object * properties: * status: * type: string * example: healthy * database: * type: boolean * example: true */ app.get("/health", (req, res) => { res.json({ status: "healthy", database: AppDataSource.isInitialized }); }); app.use("/api/auth", authRoute); app.use("/api/profile", profileRoute); app.use("/api/upload", uploadRoute); app.use("/api/suggest", suggestRoute); app.use("/api/wardrobe", wardrobeRoute); app.use("/api/avatar", avatarRoute); app.use("/api/chat", chatRoute); const PORT = process.env.PORT || 7860; AppDataSource.initialize() .then(() => { console.log("✅ Database connected"); app.listen(PORT, () => console.log(`🧥 StyleGPT running on port ${PORT}`)); }) .catch((error) => console.error("❌ DB connection failed:", error));