import { Router } from "@oak/oak"; import { APIErrorCode, APIException, isLoginUserRequest, isRegisterUserRequest, } from "../model/interfaces.ts"; import { createJWT, verifyPassword } from "../lib/jwt.ts"; import { type AuthContext, authMiddleware } from "../middleware/auth.ts"; import { createUser, getUserById, getUserByUsername, } from "../services/user-service.ts"; import { getDumpsByUser, getVotedDumpsByUser, } from "../services/dump-service.ts"; // Users router const router = new Router({ prefix: "/api/users" }); // Register a new user router.post("/register", async (ctx) => { try { const body = await ctx.request.body.json(); if (!isRegisterUserRequest(body)) { throw new APIException( APIErrorCode.VALIDATION_ERROR, 400, "Invalid request", ); } const user = await createUser(body); const token = await createJWT({ userId: user.id, username: user.username, isAdmin: user.isAdmin, }); ctx.response.status = 201; ctx.response.body = { success: true, data: { token, user, }, }; } catch (err) { console.error(err); throw new APIException( APIErrorCode.SERVER_ERROR, 500, "Failed to register user", ); } }); // Login router.post("/login", async (ctx) => { try { const body = await ctx.request.body.json(); if (!isLoginUserRequest(body)) { throw new APIException( APIErrorCode.VALIDATION_ERROR, 400, "Invalid request", ); } const user = getUserByUsername(body.username); const valid = await verifyPassword(body.password, user.passwordHash); if (!valid) { throw new APIException( APIErrorCode.VALIDATION_ERROR, 401, "Invalid username or password", ); } const token = await createJWT({ userId: user.id, username: user.username, isAdmin: user.isAdmin, }); ctx.response.body = { success: true, data: { token, user, }, }; } catch (err) { console.error(err); throw new APIException(APIErrorCode.SERVER_ERROR, 500, "Failed to login"); } }); // Get current user profile router.get("/me", authMiddleware, (ctx: AuthContext) => { try { if (!ctx.state.user) { throw new APIException( APIErrorCode.UNAUTHORIZED, 401, "Not authenticated", ); } const user = getUserById(ctx.state.user.userId); ctx.response.body = { success: true, data: user, }; } catch (err) { console.error(err); throw new APIException( APIErrorCode.SERVER_ERROR, 500, "Failed to fetch user profile", ); } }); // Public user profile by internal ID (used when only userId is available, e.g. dump.userId) router.get("/by-id/:userId", (ctx) => { const user = getUserById(ctx.params.userId); const { passwordHash: _, ...publicUser } = user; ctx.response.body = { success: true, data: publicUser }; }); // Public user profile by username (no passwordHash) router.get("/:username", (ctx) => { const user = getUserByUsername(ctx.params.username); const { passwordHash: _, ...publicUser } = user; ctx.response.body = { success: true, data: publicUser }; }); // Dumps posted by user router.get("/:username/dumps", (ctx) => { const user = getUserByUsername(ctx.params.username); const dumps = getDumpsByUser(user.id); ctx.response.body = { success: true, data: dumps }; }); // Dumps upvoted by user router.get("/:username/votes", (ctx) => { const user = getUserByUsername(ctx.params.username); const dumps = getVotedDumpsByUser(user.id); ctx.response.body = { success: true, data: dumps }; }); export default router;