mirror of
https://github.com/Snouzy/workout-cool.git
synced 2026-05-19 14:40:35 +00:00
chore: update server port variable to uppercase for clarity and consistency
This commit is contained in:
@@ -1,22 +1,20 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { enrollInProgram } from "@/features/programs/actions/enroll-program.action";
|
||||
import { auth } from "@/features/auth/lib/better-auth";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
export async function POST(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) {
|
||||
try {
|
||||
const { slug } = await params;
|
||||
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
const session = await getMobileCompatibleSession(req);
|
||||
|
||||
if (!session) {
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "Unauthorized", code: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
const program = await prisma.program.findUnique({
|
||||
where: { slug },
|
||||
select: { id: true, participantCount: true },
|
||||
@@ -26,17 +24,34 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ slu
|
||||
return NextResponse.json({ error: "Program not found", code: "NOT_FOUND" }, { status: 404 });
|
||||
}
|
||||
|
||||
const result = await enrollInProgram(program.id);
|
||||
// Check if already enrolled
|
||||
const existingEnrollment = await prisma.userProgramEnrollment.findUnique({
|
||||
where: { userId_programId: { userId, programId: program.id } },
|
||||
});
|
||||
|
||||
const updatedProgram = await prisma.program.findUnique({
|
||||
if (existingEnrollment) {
|
||||
return NextResponse.json({
|
||||
enrollment: existingEnrollment,
|
||||
isNew: false,
|
||||
totalEnrollments: program.participantCount,
|
||||
});
|
||||
}
|
||||
|
||||
// Create enrollment + increment participant count
|
||||
const enrollment = await prisma.userProgramEnrollment.create({
|
||||
data: { userId, programId: program.id },
|
||||
});
|
||||
|
||||
const updatedProgram = await prisma.program.update({
|
||||
where: { id: program.id },
|
||||
data: { participantCount: { increment: 1 } },
|
||||
select: { participantCount: true },
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
enrollment: result.enrollment,
|
||||
isNew: result.isNew,
|
||||
totalEnrollments: updatedProgram?.participantCount || program.participantCount,
|
||||
enrollment,
|
||||
isNew: true,
|
||||
totalEnrollments: updatedProgram.participantCount,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error enrolling in program:", error);
|
||||
@@ -51,20 +66,15 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ slu
|
||||
|
||||
export async function GET(req: NextRequest, { params }: { params: Promise<{ slug: string }> }) {
|
||||
try {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
const session = await getMobileCompatibleSession(req);
|
||||
|
||||
const { slug } = await params;
|
||||
|
||||
if (!session) {
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "Unauthorized", code: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userId = session.user?.id;
|
||||
if (!userId) {
|
||||
return NextResponse.json({ error: "User not found", code: "USER_NOT_FOUND" }, { status: 404 });
|
||||
}
|
||||
const userId = session.user.id;
|
||||
|
||||
const program = await prisma.program.findUnique({
|
||||
where: { slug },
|
||||
|
||||
@@ -1,18 +1,70 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { ProgramVisibility } from "@prisma/client";
|
||||
|
||||
import { getProgramProgressBySlug } from "@/features/programs/actions/get-program-progress-by-slug.action";
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) {
|
||||
export async function GET(request: NextRequest, { params }: { params: Promise<{ slug: string }> }) {
|
||||
try {
|
||||
const { slug } = await params;
|
||||
const session = await getMobileCompatibleSession(request);
|
||||
|
||||
const progress = await getProgramProgressBySlug(slug);
|
||||
|
||||
if (!progress) {
|
||||
return NextResponse.json({ error: "Program progress not found" }, { status: 404 });
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
return NextResponse.json(progress);
|
||||
const userId = session.user.id;
|
||||
const { slug } = await params;
|
||||
|
||||
const program = await prisma.program.findFirst({
|
||||
where: {
|
||||
OR: [{ slug }, { slugEn: slug }, { slugEs: slug }, { slugPt: slug }, { slugRu: slug }, { slugZhCn: slug }],
|
||||
visibility: ProgramVisibility.PUBLISHED,
|
||||
isActive: true,
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!program) {
|
||||
return NextResponse.json({ error: "Program not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
const enrollment = await prisma.userProgramEnrollment.findUnique({
|
||||
where: { userId_programId: { userId, programId: program.id } },
|
||||
include: {
|
||||
sessionProgress: {
|
||||
include: { session: true, workoutSession: true },
|
||||
},
|
||||
program: {
|
||||
include: {
|
||||
weeks: {
|
||||
include: { sessions: { orderBy: { sessionNumber: "asc" } } },
|
||||
orderBy: { weekNumber: "asc" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!enrollment) {
|
||||
return NextResponse.json(null);
|
||||
}
|
||||
|
||||
const totalSessions = enrollment.program.weeks.reduce((acc, week) => acc + week.sessions.length, 0);
|
||||
const completedSessions = enrollment.sessionProgress.filter((p) => p.completedAt !== null).length;
|
||||
const completionPercentage = totalSessions > 0 ? Math.round((completedSessions / totalSessions) * 100) : 0;
|
||||
const isProgramCompleted = totalSessions > 0 && completedSessions === totalSessions;
|
||||
|
||||
return NextResponse.json({
|
||||
enrollment,
|
||||
stats: {
|
||||
totalSessions,
|
||||
completedSessions,
|
||||
completionPercentage,
|
||||
currentWeek: enrollment.currentWeek,
|
||||
currentSession: enrollment.currentSession,
|
||||
isProgramCompleted,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching program progress:", error);
|
||||
return NextResponse.json({ error: "Failed to fetch program progress" }, { status: 500 });
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { completeProgramSession } from "@/features/programs/actions/complete-program-session.action";
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
export async function POST(request: NextRequest, { params }: { params: Promise<{ progressId: string }> }) {
|
||||
try {
|
||||
const session = await getMobileCompatibleSession(request);
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userId = session.user.id;
|
||||
const { progressId } = await params;
|
||||
const body = await request.json();
|
||||
const { workoutSessionId } = body;
|
||||
@@ -12,25 +20,79 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({ error: "Missing workout session ID" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Use the existing server action
|
||||
const result = await completeProgramSession(progressId, workoutSessionId);
|
||||
// Get session progress with enrollment and program structure
|
||||
const sessionProgress = await prisma.userSessionProgress.findUnique({
|
||||
where: { id: progressId },
|
||||
include: {
|
||||
enrollment: {
|
||||
include: {
|
||||
program: {
|
||||
include: {
|
||||
weeks: {
|
||||
include: { sessions: { orderBy: { sessionNumber: "asc" } } },
|
||||
orderBy: { weekNumber: "asc" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
session: { include: { week: true } },
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(result);
|
||||
} catch (error: any) {
|
||||
console.error("Error completing session progress:", error);
|
||||
|
||||
if (error.message === "Unauthorized") {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (error.message === "User not found") {
|
||||
return NextResponse.json({ error: "User not found" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (error.message === "Session progress not found") {
|
||||
if (!sessionProgress || sessionProgress.enrollment.userId !== userId) {
|
||||
return NextResponse.json({ error: "Session progress not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Mark session as completed
|
||||
const updatedProgress = await prisma.userSessionProgress.update({
|
||||
where: { id: progressId },
|
||||
data: { completedAt: new Date(), workoutSessionId },
|
||||
});
|
||||
|
||||
// Count completed sessions
|
||||
const completedSessionsCount = await prisma.userSessionProgress.count({
|
||||
where: { enrollmentId: sessionProgress.enrollment.id, completedAt: { not: null } },
|
||||
});
|
||||
|
||||
// Find next session
|
||||
const currentWeek = sessionProgress.session.week.weekNumber;
|
||||
const currentSession = sessionProgress.session.sessionNumber;
|
||||
let nextWeek = currentWeek;
|
||||
let nextSession = currentSession + 1;
|
||||
|
||||
const currentWeekSessions =
|
||||
sessionProgress.enrollment.program.weeks.find((w) => w.weekNumber === currentWeek)?.sessions.length || 0;
|
||||
|
||||
if (nextSession > currentWeekSessions) {
|
||||
nextWeek = currentWeek + 1;
|
||||
nextSession = 1;
|
||||
}
|
||||
|
||||
// Check if program is completed
|
||||
const totalSessions = sessionProgress.enrollment.program.weeks.reduce((acc, week) => acc + week.sessions.length, 0);
|
||||
const isCompleted = completedSessionsCount >= totalSessions;
|
||||
|
||||
// Update enrollment
|
||||
await prisma.userProgramEnrollment.update({
|
||||
where: { id: sessionProgress.enrollment.id },
|
||||
data: {
|
||||
completedSessions: completedSessionsCount,
|
||||
currentWeek: isCompleted ? currentWeek : nextWeek,
|
||||
currentSession: isCompleted ? currentSession : nextSession,
|
||||
completedAt: isCompleted ? new Date() : null,
|
||||
isActive: !isCompleted,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
sessionProgress: updatedProgress,
|
||||
isCompleted,
|
||||
nextWeek: isCompleted ? null : nextWeek,
|
||||
nextSession: isCompleted ? null : nextSession,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error completing session progress:", error);
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { startProgramSession } from "@/features/programs/actions/start-program-session.action";
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const session = await getMobileCompatibleSession(request);
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userId = session.user.id;
|
||||
const body = await request.json();
|
||||
const { enrollmentId, sessionId } = body;
|
||||
|
||||
@@ -11,29 +19,71 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: "Missing required fields" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Use the existing server action
|
||||
const result = await startProgramSession(enrollmentId, sessionId);
|
||||
// Verify enrollment belongs to user
|
||||
const enrollment = await prisma.userProgramEnrollment.findFirst({
|
||||
where: { id: enrollmentId, userId },
|
||||
include: { program: true },
|
||||
});
|
||||
|
||||
return NextResponse.json(result);
|
||||
} catch (error: any) {
|
||||
console.error("Error starting session progress:", error);
|
||||
|
||||
if (error.message === "Unauthorized") {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (error.message === "User not found") {
|
||||
return NextResponse.json({ error: "User not found" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (error.message === "Enrollment not found") {
|
||||
if (!enrollment) {
|
||||
return NextResponse.json({ error: "Enrollment not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
if (error.message === "Session not found") {
|
||||
|
||||
// Check if session already started
|
||||
const existingProgress = await prisma.userSessionProgress.findUnique({
|
||||
where: { enrollmentId_sessionId: { enrollmentId, sessionId } },
|
||||
});
|
||||
|
||||
if (existingProgress) {
|
||||
return NextResponse.json({ sessionProgress: existingProgress, isNew: false });
|
||||
}
|
||||
|
||||
// Get session details
|
||||
const programSession = await prisma.programSession.findUnique({
|
||||
where: { id: sessionId },
|
||||
include: {
|
||||
week: true,
|
||||
exercises: {
|
||||
include: {
|
||||
exercise: {
|
||||
include: {
|
||||
attributes: {
|
||||
include: { attributeName: true, attributeValue: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
suggestedSets: { orderBy: { setIndex: "asc" } },
|
||||
},
|
||||
orderBy: { order: "asc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!programSession) {
|
||||
return NextResponse.json({ error: "Session not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
|
||||
// Create session progress
|
||||
const sessionProgress = await prisma.userSessionProgress.create({
|
||||
data: { enrollmentId, sessionId },
|
||||
});
|
||||
|
||||
// Update enrollment current position
|
||||
await prisma.userProgramEnrollment.update({
|
||||
where: { id: enrollmentId },
|
||||
data: {
|
||||
currentWeek: programSession.week.weekNumber,
|
||||
currentSession: programSession.sessionNumber,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
sessionProgress,
|
||||
isNew: true,
|
||||
sessionData: programSession,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error starting session progress:", error);
|
||||
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
import { Platform } from "@prisma/client";
|
||||
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { serverRequiredUser } from "@/entities/user/model/get-server-session-user";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
/**
|
||||
* Sync RevenueCat Status with Backend (Simplified)
|
||||
@@ -25,8 +25,13 @@ const syncStatusSchema = z.object({
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Get authenticated user
|
||||
const user = await serverRequiredUser();
|
||||
// Get authenticated user (supports mobile cookie format)
|
||||
const session = await getMobileCompatibleSession(request);
|
||||
const user = session?.user;
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ error: "Authentication required" }, { status: 401 });
|
||||
}
|
||||
|
||||
// Parse and validate request body
|
||||
const body = await request.json();
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { z } from "zod";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { prisma } from "@/shared/lib/prisma";
|
||||
import { getMobileCompatibleSession } from "@/shared/api/mobile-auth";
|
||||
|
||||
const preferencesSchema = z.object({
|
||||
goals: z.array(z.string()),
|
||||
fitnessLevel: z.enum(["beginner", "intermediate", "advanced"]).nullable(),
|
||||
equipment: z.array(z.string()),
|
||||
muscles: z.array(z.string()),
|
||||
duration: z.number().nullable(),
|
||||
weeklyFrequency: z.number().min(1).max(7),
|
||||
notificationDays: z.array(z.number()).optional(),
|
||||
notificationTime: z.string().optional(),
|
||||
});
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const session = await getMobileCompatibleSession(req);
|
||||
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
select: { onboardingPreferences: true },
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
preferences: user?.onboardingPreferences ?? null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Get preferences error:", error);
|
||||
return NextResponse.json({ error: "INTERNAL_SERVER_ERROR" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(req: NextRequest) {
|
||||
try {
|
||||
const session = await getMobileCompatibleSession(req);
|
||||
|
||||
if (!session?.user) {
|
||||
return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const body = await req.json();
|
||||
const parsed = preferencesSchema.safeParse(body);
|
||||
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "INVALID_INPUT", details: parsed.error.format() }, { status: 400 });
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: session.user.id },
|
||||
data: { onboardingPreferences: parsed.data },
|
||||
});
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Update preferences error:", error);
|
||||
return NextResponse.json({ error: "INTERNAL_SERVER_ERROR" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,9 @@ model User {
|
||||
licenses License[]
|
||||
isPremium Boolean? @default(false)
|
||||
|
||||
// Onboarding preferences
|
||||
onboardingPreferences Json?
|
||||
|
||||
// Training programs
|
||||
programEnrollments UserProgramEnrollment[]
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export function WorkoutSessionSets({
|
||||
} else {
|
||||
exerciseElement.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
block: "start",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user