Feature/nextjs migration#7
Closed
Stevenjoelrs wants to merge 21 commits into
Closed
Conversation
- Add packageManager field (pnpm@11.1.1) to package.json - Add preinstall guard to block npm/yarn usage - Add .npmrc with strict isolation (hoist=false, ignore-scripts) - Gitignore package-lock.json and yarn.lock - Update README dev commands to pnpm
- Migrate from SvelteKit to Next.js 15 (App Router + Turbopack) - Create Prisma schema with 15 models (PostgreSQL) from orbital-ctf - Implement macOS-style terminal window shell (TerminalWindow component) - Add file tree sidebar with challenge categories as directories - Port CRT effects from orbital-ctf (scanlines, phosphor, flicker, glow) - Add CRT settings toggles with localStorage persistence - Set up NextAuth with credentials provider and JWT sessions - Create status bar footer, landing page with ASCII banner - Configure admin route protection middleware
…lenge detail view
…d announcements UI
There was a problem hiding this comment.
Pull request overview
This PR introduces a Next.js (App Router) migration for CTFrbt, including UI pages/components, NextAuth credentials-based authentication, Prisma/Postgres persistence, and a basic admin + gameplay API surface.
Changes:
- Adds Next.js app structure (layout, pages, global CRT styles) and core UI components (terminal shell, sidebar, challenge view).
- Implements auth + core gameplay APIs (register/sign-in, challenges, submissions/scoring, hints, teams, file downloads, leaderboard).
- Adds Prisma schema/migration/seed plus project tooling/config (TypeScript, ESLint, pnpm, Docker).
Reviewed changes
Copilot reviewed 44 out of 48 changed files in this pull request and generated 21 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds TypeScript config suitable for Next.js App Router. |
| src/types/next-auth.d.ts | Adds NextAuth type augmentation for custom user/session/JWT fields. |
| src/middleware.ts | Adds middleware to protect /api/admin/* routes. |
| src/lib/prisma.ts | Adds Prisma client singleton wiring for Next.js runtime. |
| src/lib/auth.ts | Adds NextAuth credentials provider + callbacks. |
| src/components/TerminalWindow.tsx | Implements terminal-style app shell container. |
| src/components/StatusBar.tsx | Adds bottom status bar UI. |
| src/components/Sidebar.tsx | Adds explorer-style sidebar navigation tree. |
| src/components/Providers.tsx | Adds NextAuth SessionProvider wrapper. |
| src/components/CRTSettings.tsx | Adds client-side CRT effect toggle UI + persistence. |
| src/components/ChallengeView.tsx | Adds challenge details, file downloads, flag submission, hints UI. |
| src/app/scoreboard/page.tsx | Adds client scoreboard page fetching /api/leaderboard. |
| src/app/rules/page.tsx | Adds rules page fetching /api/rules. |
| src/app/profile/page.tsx | Adds profile/team management page backed by /api/teams. |
| src/app/page.tsx | Adds landing page with links to auth/rules. |
| src/app/layout.tsx | Adds root layout with fonts, session bootstrap, toaster, app shell. |
| src/app/globals.css | Adds global CRT/terminal UI styling. |
| src/app/dashboard/page.tsx | Adds dashboard page fetching challenges and rendering ChallengeView. |
| src/app/auth/signup/page.tsx | Adds registration UI backed by /api/auth/register. |
| src/app/auth/signin/page.tsx | Adds credentials sign-in UI via NextAuth signIn. |
| src/app/api/teams/route.ts | Adds API to create/join teams and fetch team info. |
| src/app/api/submissions/route.ts | Adds flag submission API with scoring + rate limiting. |
| src/app/api/rules/route.ts | Adds API to fetch rules from site config. |
| src/app/api/leaderboard/route.ts | Adds API to compute team rankings. |
| src/app/api/hints/route.ts | Adds APIs for listing/purchasing hints. |
| src/app/api/files/[fileId]/route.ts | Adds API to download challenge files by ID. |
| src/app/api/challenges/route.ts | Adds API to list challenges grouped by category + solved/unlocked enrichment. |
| src/app/api/auth/register/route.ts | Adds registration endpoint (bcrypt hash + alias uniqueness). |
| src/app/api/auth/[...nextauth]/route.ts | Adds NextAuth route handler exports. |
| src/app/api/announcements/route.ts | Adds announcements listing endpoint. |
| src/app/api/admin/game/route.ts | Adds admin game config read/write endpoint. |
| src/app/api/admin/challenges/route.ts | Adds admin challenge list/create endpoint. |
| src/app/api/admin/challenges/[id]/route.ts | Adds admin challenge update/delete endpoint. |
| src/app/api/admin/announcements/route.ts | Adds admin announcement creation endpoint. |
| src/app/admin/page.tsx | Adds admin panel UI for challenges/game config/announcements. |
| README.md | Adds project overview + dev workflow commands. |
| prisma/seed.ts | Adds seed data for users/teams/challenges/rules/game config. |
| prisma/schema.prisma | Adds Prisma data model for platform entities. |
| prisma/migrations/migration_lock.toml | Adds Prisma migration lock file. |
| prisma/migrations/20260611062951_ctfrbt/migration.sql | Adds initial DB migration SQL for schema. |
| pnpm-workspace.yaml | Adds pnpm workspace config for allowed builds. |
| package.json | Adds scripts and dependencies for Next.js/Prisma/NextAuth stack. |
| next.config.ts | Adds Next.js config scaffold. |
| eslint.config.mjs | Adds ESLint flat config for Next.js. |
| docker-compose.yml | Adds local Postgres service for development. |
| .gitignore | Adds ignores for Next.js/Node/Prisma artifacts and env files. |
| .env.example | Adds environment variable template for DB + NextAuth. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+20
to
+30
| try { | ||
| const filePath = path.join(process.cwd(), "public", file.path); | ||
| const fileBuffer = await readFile(filePath); | ||
|
|
||
| return new NextResponse(fileBuffer, { | ||
| headers: { | ||
| "Content-Type": "application/octet-stream", | ||
| "Content-Disposition": `attachment; filename="${file.name}"`, | ||
| "Content-Length": String(fileBuffer.length), | ||
| }, | ||
| }); |
Comment on lines
+82
to
+84
| // Generate a random invite code | ||
| const code = Math.random().toString(36).substring(2, 10).toUpperCase(); | ||
|
|
Comment on lines
+64
to
+88
| if (!teamName || teamName.length > 32) { | ||
| return NextResponse.json( | ||
| { error: "Team name is required (max 32 chars)" }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| const existingTeam = await prisma.team.findUnique({ | ||
| where: { name: teamName }, | ||
| }); | ||
|
|
||
| if (existingTeam) { | ||
| return NextResponse.json( | ||
| { error: "Team name is already taken" }, | ||
| { status: 409 } | ||
| ); | ||
| } | ||
|
|
||
| // Generate a random invite code | ||
| const code = Math.random().toString(36).substring(2, 10).toUpperCase(); | ||
|
|
||
| const team = await prisma.team.create({ | ||
| data: { | ||
| name: teamName, | ||
| code, |
| hints, | ||
| } = body; | ||
|
|
||
| if (!title || !description || !points || !category || !difficulty) { |
Comment on lines
+24
to
+33
| // Rate limiting | ||
| const now = Date.now(); | ||
| const lastTime = lastSubmission.get(session.user.id); | ||
| if (lastTime && now - lastTime < RATE_LIMIT_MS) { | ||
| const waitSec = Math.ceil((RATE_LIMIT_MS - (now - lastTime)) / 1000); | ||
| return NextResponse.json( | ||
| { error: `Rate limited. Wait ${waitSec}s before submitting again.` }, | ||
| { status: 429 } | ||
| ); | ||
| } |
Comment on lines
+1
to
+2
| # Database — PostgreSQL connection string | ||
| DATABASE_URL="postgresql://ctfrbt:ctfrbt@localhost:5432/ctfrbt" |
Comment on lines
+114
to
+118
| if (hint.cost > 0) { | ||
| const updatedTeam = await prisma.team.update({ | ||
| where: { id: session.user.teamId }, | ||
| data: { score: { decrement: hint.cost } }, | ||
| }); |
Comment on lines
+12
to
+18
| const file = await prisma.challengeFile.findUnique({ | ||
| where: { id: fileId }, | ||
| }); | ||
|
|
||
| if (!file) { | ||
| return NextResponse.json({ error: "File not found" }, { status: 404 }); | ||
| } |
Comment on lines
+79
to
+82
| isLocked, | ||
| isSolved: solvedChallengeIds.includes(challenge.id), | ||
| solveCount: challenge._count.submissions, | ||
| multipleFlags: challenge.multipleFlags, |
Comment on lines
+20
to
+27
| children: [ | ||
| { label: "web", icon: "folder", href: "/categories/web" }, | ||
| { label: "crypto", icon: "folder", href: "/categories/crypto" }, | ||
| { label: "pwn", icon: "folder", href: "/categories/pwn" }, | ||
| { label: "forensics", icon: "folder", href: "/categories/forensics" }, | ||
| { label: "reverse", icon: "folder", href: "/categories/reverse" }, | ||
| { label: "misc", icon: "folder", href: "/categories/misc" }, | ||
| ], |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.