Files
fitaiProto/apps/admin/src/app/api/fitness-goals/route.ts
T

137 lines
3.8 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@clerk/nextjs/server";
import { getDatabase } from "@/lib/database";
import { randomBytes } from "crypto";
import log from "@/lib/logger";
import {
fitnessGoalSchema,
paginationSchema,
statusFilterSchema,
} from "@/lib/validation/schemas";
import {
validateRequestBody,
validateQueryParams,
validationErrorResponse,
} from "@/lib/validation/helpers";
import {
successResponse,
createdResponse,
unauthorizedResponse,
internalErrorResponse,
addCorsHeaders,
} from "@/lib/api/responses";
// Helper to get user ID from auth (works with both web sessions and mobile tokens)
async function getAuthenticatedUserId(
req: NextRequest,
): Promise<string | null> {
// The auth() function from Clerk should handle Bearer tokens automatically
// when called within an API route that's processed by clerkMiddleware
const { userId } = await auth();
return userId;
}
// OPTIONS - Handle preflight requests
export async function OPTIONS() {
return NextResponse.json({}, { headers: addCorsHeaders() });
}
// GET - List user's fitness goals
export async function GET(req: NextRequest) {
try {
const userId = await getAuthenticatedUserId(req);
if (!userId) {
log.error("Fitness goals GET - authentication failed", undefined, {
headers: Object.fromEntries(req.headers.entries()),
});
return unauthorizedResponse(undefined, addCorsHeaders());
}
log.debug("Fitness goals GET - authenticated user", { userId });
const { searchParams } = new URL(req.url);
const targetUserId = searchParams.get("userId") || userId;
const status = searchParams.get("status"); // active, completed, all
const db = await getDatabase();
// Fetch goals with optional status filter
const goals = await db.getFitnessGoalsByUserId(
targetUserId,
status && status !== "all" ? status : undefined,
);
log.debug("Fitness goals retrieved", {
count: goals.length,
userId: targetUserId,
});
return successResponse(goals, { headers: addCorsHeaders() });
} catch (error) {
log.error("Failed to get fitness goals", error);
return internalErrorResponse(undefined, addCorsHeaders());
}
}
// POST - Create new fitness goal
export async function POST(req: NextRequest) {
try {
const userId = await getAuthenticatedUserId(req);
if (!userId) {
log.error("Fitness goals POST - authentication failed");
return unauthorizedResponse(undefined, addCorsHeaders());
}
// Validate request body
const validation = await validateRequestBody(req, fitnessGoalSchema);
if (!validation.success) {
log.warn("Fitness goal creation validation failed", {
errors: validation.errors,
});
return validationErrorResponse(validation.errors);
}
const {
goalType,
title,
description,
targetValue,
currentValue,
unit,
targetDate,
priority,
status,
notes,
} = validation.data;
const db = await getDatabase();
// Create the goal
const goal = await db.createFitnessGoal({
id: randomBytes(16).toString("hex"),
userId,
fitnessProfileId: undefined,
goalType,
title,
description,
targetValue,
currentValue: currentValue || 0,
unit,
startDate: new Date(),
targetDate: targetDate ? new Date(targetDate) : undefined,
status: status || "active",
progress: 0,
priority: priority || "medium",
notes,
});
log.info("Fitness goal created", { goalId: goal.id, userId });
return createdResponse(goal, { headers: addCorsHeaders() });
} catch (error) {
log.error("Failed to create fitness goal", error);
return internalErrorResponse(undefined, addCorsHeaders());
}
}