import { Router } from "express"; import { requireAuth } from "../middleware/auth"; import { prisma } from "../utils/db"; import crypto from "crypto"; const router = Router(); // All API key routes require authentication (session-based) router.use(requireAuth); /** * @openapi * /api-keys: * post: * summary: Create a new API key for mobile/external authentication * tags: [API Keys] * security: * - sessionAuth: [] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - deviceName * properties: * deviceName: * type: string * description: Name of the device (e.g., "iPhone 14", "Android Tablet") * example: "iPhone 14" * responses: * 201: * description: API key created successfully * content: * application/json: * schema: * type: object * properties: * apiKey: * type: string * description: The generated API key (64-character hex string) * example: "a1b2c3d4e5f6..." * name: * type: string * example: "iPhone 14" * createdAt: * type: string * format: date-time * message: * type: string * example: "API key created successfully. Save this key - you won't see it again!" * 400: * description: Invalid request * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Not authenticated * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ router.post("/", async (req, res) => { try { const { deviceName } = req.body; if (!deviceName || deviceName.trim().length === 0) { return res.status(400).json({ error: "Device name is required" }); } // Use req.user.id (set by requireAuth middleware) - supports both session and JWT auth const userId = req.user?.id || req.session?.userId; if (!userId) { return res.status(401).json({ error: "Not authenticated" }); } // Generate a secure random API key (32 bytes = 64 hex chars) const apiKeyValue = crypto.randomBytes(32).toString("hex"); const apiKey = await prisma.apiKey.create({ data: { userId, name: deviceName.trim(), key: apiKeyValue, }, }); console.log(`API key created for user ${userId}: ${deviceName}`); res.status(201).json({ apiKey: apiKey.key, name: apiKey.name, createdAt: apiKey.createdAt, message: "API key created successfully. Save this key - you won't see it again!", }); } catch (error) { console.error("Create API key error:", error); res.status(500).json({ error: "Failed to create API key" }); } }); /** * @openapi * /api-keys: * get: * summary: List all API keys for the current user * tags: [API Keys] * security: * - sessionAuth: [] * responses: * 200: * description: List of API keys (without the actual key values for security) * content: * application/json: * schema: * type: object * properties: * apiKeys: * type: array * items: * $ref: '#/components/schemas/ApiKey' * 401: * description: Not authenticated * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ router.get("/", async (req, res) => { try { // Use req.user.id (set by requireAuth middleware) - supports both session and JWT auth const userId = req.user?.id || req.session?.userId; if (!userId) { return res.status(401).json({ error: "Not authenticated" }); } const keys = await prisma.apiKey.findMany({ where: { userId }, select: { id: true, name: true, lastUsed: true, createdAt: true, // Don't return the actual key for security! }, orderBy: { createdAt: "desc" }, }); res.json({ apiKeys: keys }); } catch (error) { console.error("List API keys error:", error); res.status(500).json({ error: "Failed to list API keys" }); } }); /** * @openapi * /api-keys/{id}: * delete: * summary: Revoke an API key * tags: [API Keys] * security: * - sessionAuth: [] * parameters: * - in: path * name: id * required: true * schema: * type: string * description: The API key ID * responses: * 200: * description: API key revoked successfully * content: * application/json: * schema: * type: object * properties: * message: * type: string * example: "API key revoked successfully" * 404: * description: API key not found * content: * application/json: * schema: * $ref: '#/components/schemas/Error' * 401: * description: Not authenticated * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ router.delete("/:id", async (req, res) => { try { // Use req.user.id (set by requireAuth middleware) - supports both session and JWT auth const userId = req.user?.id || req.session?.userId; if (!userId) { return res.status(401).json({ error: "Not authenticated" }); } const keyId = req.params.id; // Only allow users to delete their own keys const deleted = await prisma.apiKey.deleteMany({ where: { id: keyId, userId, }, }); if (deleted.count === 0) { return res .status(404) .json({ error: "API key not found or already deleted" }); } console.log(`API key ${keyId} revoked by user ${userId}`); res.json({ message: "API key revoked successfully" }); } catch (error) { console.error("Delete API key error:", error); res.status(500).json({ error: "Failed to revoke API key" }); } }); export default router;