routes fix

extensive loging added
This commit is contained in:
dimitar committed 2026-03-10 02:22:11 +01:00
1 parent 36315ea5cf
commit 22c274bb83
9 files changed
+1252 -508

No files matched your search

@@ -1,39 +1,62 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@clerk/nextjs/server";
import { getDatabase } from "@/lib/database";
// Helper to add CORS headers
function corsHeaders() {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
}
// OPTIONS - Handle preflight requests
export async function OPTIONS() {
return NextResponse.json({}, { headers: corsHeaders() });
}
// POST - Mark goal as complete
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
// Mark as completed
const completedGoal = await db.completeGoal(id);
return NextResponse.json(completedGoal);
} catch (error) {
console.error('Error completing fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json(
{ error: "Goal not found" },
{ status: 404, headers: corsHeaders() },
);
}
if (existingGoal.userId !== userId) {
return NextResponse.json(
{ error: "Forbidden" },
{ status: 403, headers: corsHeaders() },
);
}
// Mark as completed
const completedGoal = await db.completeGoal(id);
return NextResponse.json(completedGoal, { headers: corsHeaders() });
} catch (error) {
console.error("Error completing fitness goal:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
+145 -101
View File
@@ -1,119 +1,163 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@clerk/nextjs/server";
import { getDatabase } from "@/lib/database";
// Helper to add CORS headers
function corsHeaders() {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
}
// OPTIONS - Handle preflight requests
export async function OPTIONS() {
return NextResponse.json({}, { headers: corsHeaders() });
}
// GET - Get specific goal
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
const goal = await db.getFitnessGoalById(id);
if (!goal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
// Verify ownership
if (goal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
return NextResponse.json(goal);
} catch (error) {
console.error('Error fetching fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
const { id } = await params;
const db = await getDatabase();
const goal = await db.getFitnessGoalById(id);
if (!goal) {
return NextResponse.json(
{ error: "Goal not found" },
{ status: 404, headers: corsHeaders() },
);
}
// Verify ownership
if (goal.userId !== userId) {
return NextResponse.json(
{ error: "Forbidden" },
{ status: 403, headers: corsHeaders() },
);
}
return NextResponse.json(goal, { headers: corsHeaders() });
} catch (error) {
console.error("Error fetching fitness goal:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
// PUT - Update goal
export async function PUT(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const updates = await req.json();
// Don't allow changing userId or id
delete updates.userId;
delete updates.id;
delete updates.createdAt;
const updatedGoal = await db.updateFitnessGoal(id, updates);
return NextResponse.json(updatedGoal);
} catch (error) {
console.error('Error updating fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json(
{ error: "Goal not found" },
{ status: 404, headers: corsHeaders() },
);
}
if (existingGoal.userId !== userId) {
return NextResponse.json(
{ error: "Forbidden" },
{ status: 403, headers: corsHeaders() },
);
}
const updates = await req.json();
// Don't allow changing userId or id
delete updates.userId;
delete updates.id;
delete updates.createdAt;
const updatedGoal = await db.updateFitnessGoal(id, updates);
return NextResponse.json(updatedGoal, { headers: corsHeaders() });
} catch (error) {
console.error("Error updating fitness goal:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
// DELETE - Delete goal
export async function DELETE(
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json({ error: 'Goal not found' }, { status: 404 });
}
if (existingGoal.userId !== userId) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const deleted = await db.deleteFitnessGoal(id);
if (deleted) {
return NextResponse.json({ success: true });
} else {
return NextResponse.json({ error: 'Failed to delete goal' }, { status: 500 });
}
} catch (error) {
console.error('Error deleting fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
const { id } = await params;
const db = await getDatabase();
// Verify goal exists and user owns it
const existingGoal = await db.getFitnessGoalById(id);
if (!existingGoal) {
return NextResponse.json(
{ error: "Goal not found" },
{ status: 404, headers: corsHeaders() },
);
}
if (existingGoal.userId !== userId) {
return NextResponse.json(
{ error: "Forbidden" },
{ status: 403, headers: corsHeaders() },
);
}
const deleted = await db.deleteFitnessGoal(id);
if (deleted) {
return NextResponse.json({ success: true }, { headers: corsHeaders() });
} else {
return NextResponse.json(
{ error: "Failed to delete goal" },
{ status: 500, headers: corsHeaders() },
);
}
} catch (error) {
console.error("Error deleting fitness goal:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
+129 -83
View File
@@ -1,95 +1,141 @@
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { getDatabase } from '@/lib/database';
import { randomBytes } from 'crypto';
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@clerk/nextjs/server";
import { getDatabase } from "@/lib/database";
import { randomBytes } from "crypto";
// Helper to add CORS headers
function corsHeaders() {
return {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
}
// 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: corsHeaders() });
}
// GET - List user's fitness goals
export async function GET(req: NextRequest) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const userId = await getAuthenticatedUserId(req);
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
);
return NextResponse.json(goals);
} catch (error) {
console.error('Error fetching fitness goals:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
if (!userId) {
console.error("[Fitness Goals API] Authentication failed");
console.error(
"[Fitness Goals API] Headers:",
Object.fromEntries(req.headers.entries()),
);
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
console.log("[Fitness Goals API] 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,
);
console.log(
`[Fitness Goals API] Found ${goals.length} goals for user ${targetUserId}`,
);
return NextResponse.json(goals, { headers: corsHeaders() });
} catch (error) {
console.error("[Fitness Goals API] Error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
// POST - Create new fitness goal
export async function POST(req: NextRequest) {
try {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const userId = await getAuthenticatedUserId(req);
const body = await req.json();
const {
goalType,
title,
description,
targetValue,
currentValue,
unit,
targetDate,
priority,
notes,
fitnessProfileId
} = body;
// Validation
if (!goalType || !title) {
return NextResponse.json(
{ error: 'goalType and title are required' },
{ status: 400 }
);
}
const db = await getDatabase();
// Create the goal
const goal = await db.createFitnessGoal({
id: randomBytes(16).toString('hex'),
userId,
fitnessProfileId: fitnessProfileId || undefined,
goalType,
title,
description: description || undefined,
targetValue: targetValue || undefined,
currentValue: currentValue || 0,
unit: unit || undefined,
startDate: new Date(),
targetDate: targetDate ? new Date(targetDate) : undefined,
status: 'active',
progress: 0,
priority: priority || 'medium',
notes: notes || undefined
});
return NextResponse.json(goal, { status: 201 });
} catch (error) {
console.error('Error creating fitness goal:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
if (!userId) {
console.error("[Fitness Goals API] Authentication failed for POST");
return NextResponse.json(
{ error: "Unauthorized" },
{ status: 401, headers: corsHeaders() },
);
}
const body = await req.json();
const {
goalType,
title,
description,
targetValue,
currentValue,
unit,
targetDate,
priority,
notes,
fitnessProfileId,
} = body;
// Validation
if (!goalType || !title) {
return NextResponse.json(
{ error: "goalType and title are required" },
{ status: 400, headers: corsHeaders() },
);
}
const db = await getDatabase();
// Create the goal
const goal = await db.createFitnessGoal({
id: randomBytes(16).toString("hex"),
userId,
fitnessProfileId: fitnessProfileId || undefined,
goalType,
title,
description: description || undefined,
targetValue: targetValue || undefined,
currentValue: currentValue || 0,
unit: unit || undefined,
startDate: new Date(),
targetDate: targetDate ? new Date(targetDate) : undefined,
status: "active",
progress: 0,
priority: priority || "medium",
notes: notes || undefined,
});
console.log(
`[Fitness Goals API] Created goal ${goal.id} for user ${userId}`,
);
return NextResponse.json(goal, { status: 201, headers: corsHeaders() });
} catch (error) {
console.error("[Fitness Goals API] Error creating goal:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500, headers: corsHeaders() },
);
}
}
+5
View File
@@ -0,0 +1,5 @@
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ message: "Test endpoint works!" });
}
+47
View File
@@ -0,0 +1,47 @@
import { auth, currentUser } from "@clerk/nextjs/server";
import { NextRequest } from "next/server";
/**
* Get authenticated user ID from request
* Handles both session-based auth (web) and Bearer token auth (mobile)
*
* For mobile apps using Clerk Expo, tokens should be passed as:
* Authorization: Bearer <token>
*/
export async function getAuthUserId(req: NextRequest): Promise<string | null> {
try {
// Clerk's auth() should handle both cookies and Bearer tokens automatically
// when the request is properly formatted
const { userId } = await auth();
if (userId) {
console.log("✓ Authenticated user:", userId);
return userId;
}
console.log("✗ No authentication found");
// Log headers for debugging
const authHeader = req.headers.get("authorization");
console.log("Authorization header:", authHeader ? "Present" : "Missing");
return null;
} catch (error) {
console.error("Authentication error:", error);
return null;
}
}
/**
* Simplified version that just uses Clerk's built-in auth
* This should work with both session cookies and Bearer tokens
*/
export async function requireAuth(req: NextRequest): Promise<string> {
const userId = await getAuthUserId(req);
if (!userId) {
throw new Error("Unauthorized");
}
return userId;
}
+29 -16
View File
@@ -1,30 +1,43 @@
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
// Define routes that should be publicly accessible
// Define routes that should be publicly accessible (no auth required)
const isPublicRoute = createRouteMatcher([
"/sign-in(.*)",
"/sign-up(.*)",
"/api/webhooks(.*)",
"/api/attendance(.*)",
"/api/health(.*)",
]);
// Define routes that require authentication
const isProtectedRoute = createRouteMatcher([
"/",
"/users(.*)",
"/analytics(.*)",
"/profile(.*)",
"/api/users(.*)",
"/api/profile(.*)",
"/api/payments(.*)",
"/api/notifications(.*)",
]);
// Define API routes that need auth but should be handled in the route itself
// This prevents auth.protect() from blocking before the route handler runs
const isApiRoute = createRouteMatcher(["/api/(.*)"]);
export default clerkMiddleware(async (auth, req) => {
// Protect all routes except public ones
if (!isPublicRoute(req)) {
await auth.protect();
// Log for debugging
const authHeader = req.headers.get("authorization");
if (authHeader) {
console.log(
"[Middleware] Authorization header present:",
authHeader.substring(0, 20) + "...",
);
}
// Don't protect public routes
if (isPublicRoute(req)) {
console.log("[Middleware] Public route, skipping auth");
return;
}
// For API routes, let the route handler check auth
// This allows API routes to handle both web sessions and mobile Bearer tokens
if (isApiRoute(req)) {
console.log("[Middleware] API route, auth will be checked in handler");
return;
}
// For all other routes (web pages), enforce authentication
console.log("[Middleware] Protected route, requiring auth");
await auth.protect();
});
export const config = {