-
Notifications
You must be signed in to change notification settings - Fork 2
a cop asked me what's my name and dont lie #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
8c350a4
59ae6ad
c404dbe
01283d8
b40d8af
9870653
9e4a6f4
6cdcf50
dd2cbd9
dae501a
8f6a80d
5988565
6ee74aa
a92425a
802a31c
f985d2e
003efe5
417ea98
fc9b985
90cdf56
a6b6763
eb41a70
fefb2af
1b1f6ff
0c34542
ad5a554
0e650ec
5e3883b
f302661
39b2765
c00fae5
1385234
212c68f
2f40f5a
9236222
5936b2f
72080ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,3 +42,4 @@ next-env.d.ts | |
| /.idea/* | ||
| /.idea/ | ||
| /src/dev/ | ||
| /backend/ | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
|
|
||
| export async function POST(req: NextRequest) { | ||
| const sessionId = req.cookies.get("sessionId")?.value; | ||
|
|
||
| if (!sessionId) { | ||
| return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } | ||
|
|
||
| const data = await req.json(); | ||
|
|
||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/devlogs/`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Cookie: `sessionId=${sessionId}`, | ||
| }, | ||
| body: JSON.stringify(data), | ||
| }); | ||
|
|
||
| const responseData = await res.json().catch(() => null); | ||
|
|
||
| return NextResponse.json(responseData, { status: res.status }); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,41 @@ | ||||||||||||
| import { NextRequest, NextResponse } from "next/server"; | ||||||||||||
|
|
||||||||||||
| export async function POST(req: NextRequest) { | ||||||||||||
| const data = await req.json(); | ||||||||||||
| const { email, otp } = data; | ||||||||||||
|
|
||||||||||||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/validate_otp`, { | ||||||||||||
| method: "POST", | ||||||||||||
| headers: { | ||||||||||||
| "Content-Type": "application/json", | ||||||||||||
| }, | ||||||||||||
| body: JSON.stringify({ email, otp }), | ||||||||||||
| credentials: "include", | ||||||||||||
| }); | ||||||||||||
|
|
||||||||||||
| let cookie: string | null = null; | ||||||||||||
|
|
||||||||||||
| try { | ||||||||||||
| const data = await res.json(); | ||||||||||||
| cookie = data?.sessionId ?? null; | ||||||||||||
| } catch { | ||||||||||||
| // ignore | ||||||||||||
|
Comment on lines
+21
to
+22
|
||||||||||||
| } catch { | |
| // ignore | |
| } catch (error) { | |
| console.error("Failed to parse validate_otp response JSON:", error); | |
| cookie = null; |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing max-age or expires attribute for the session cookie. The cookie configuration sets httpOnly, secure, path, and sameSite but doesn't specify a max-age or expires attribute. This means the cookie will be a session cookie that gets deleted when the browser closes. If persistent login is desired, add a maxAge attribute.
| sameSite: 'none', | |
| sameSite: 'none', | |
| maxAge: 60 * 60 * 24 * 7, // 7 days |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sameSite cookie attribute is set to 'none' which requires the secure flag. While secure is set to true, this configuration means cookies will be sent in cross-site requests. This could be a security risk if not intentional. If the frontend and API are on the same domain, consider using 'lax' or 'strict' instead for better security.
| sameSite: 'none', | |
| sameSite: 'lax', |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
|
|
||
| export async function POST(req: NextRequest) { | ||
| const sessionId = req.cookies.get("sessionId")?.value; | ||
|
|
||
| if (!sessionId) { | ||
| return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } | ||
|
|
||
| const data = await req.json(); | ||
|
|
||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/projects/`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Cookie: `sessionId=${sessionId}`, | ||
| }, | ||
| body: JSON.stringify(data), | ||
| }); | ||
|
|
||
| const responseData = await res.json().catch(() => null); | ||
|
|
||
| return NextResponse.json(responseData, { status: res.status }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { NextRequest } from "next/server"; | ||
|
|
||
| export async function POST(req: NextRequest) { | ||
| const data = await req.json(); | ||
| const email = data.email; | ||
|
|
||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/send_otp`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ email }), | ||
| }); | ||
|
|
||
| return new Response(null,{ | ||
| status: res.status, | ||
| headers: { "Content-Type": "application/json" }, | ||
| }) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { NextResponse } from "next/server"; | ||
|
|
||
| let cache: { | ||
| hash: string | ||
| message: string | ||
| } | undefined | ||
|
|
||
| export async function GET() { | ||
| if (!cache) { | ||
| const githubRes = await fetch( | ||
| "https://api.github.com/repos/hackclub/aces/commits/main" | ||
| ) | ||
|
|
||
| if (!githubRes.ok) { | ||
| throw new Error(`GitHub API error: ${githubRes.status}`) | ||
| } | ||
|
|
||
| const json = await githubRes.json() | ||
|
|
||
| cache = { | ||
| hash: json.sha.substring(0, 7), | ||
| message: json.commit.message, | ||
| } | ||
| } | ||
|
|
||
| return NextResponse.json(cache) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { NextResponse } from "next/server"; | ||
|
|
||
| const cacheForEmergencies: number | undefined = undefined | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const res = await fetch( | ||
| `https://api.airtable.com/v0/${process.env.RSVP_AIRTABLE_BASE_ID}/RSVPS?sort[0][field]=count&sort[0][direction]=desc&maxRecords=1`, | ||
| { | ||
| headers: { | ||
| Authorization: `Bearer ${process.env.RSVP_AIRTABLE_API_KEY}`, | ||
| }, | ||
| } | ||
| ); | ||
|
|
||
| const data = await res.json(); | ||
| const newestRecord = data.records?.[0]; | ||
| const count = newestRecord?.fields?.count ?? cacheForEmergencies; | ||
|
|
||
| return NextResponse.json({ count }); | ||
| } catch { | ||
| return NextResponse.json({ count: cacheForEmergencies, error: "Failed to fetch" }, { status: 500 }); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,12 @@ | ||||||||||||||
| "use client"; | ||||||||||||||
| import { ReactNode } from "react"; | ||||||||||||||
| import { UserContext } from "@/app/dashboard/userContext"; | ||||||||||||||
| import { User } from "@/app/dashboard/layout"; | ||||||||||||||
|
|
||||||||||||||
| export default function UserProvider({ user, children }: { user: User; children: ReactNode }) { | ||||||||||||||
| return ( | ||||||||||||||
| <UserContext value={user}> | ||||||||||||||
| {children} | ||||||||||||||
| </UserContext> | ||||||||||||||
|
Comment on lines
+8
to
+10
|
||||||||||||||
| <UserContext value={user}> | |
| {children} | |
| </UserContext> | |
| <UserContext.Provider value={user}> | |
| {children} | |
| </UserContext.Provider> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import { ReactNode } from "react"; | ||
| import { cookies } from "next/headers"; | ||
| import { redirect } from "next/navigation"; | ||
| import DashNav from "@/components/dashboard/DashNav"; | ||
| import UserProvider from "@/app/dashboard/UserProvider"; | ||
|
|
||
| export type User = { | ||
| id: number | ||
| email: string | ||
| username: string | ||
| hackatime_id: number | ||
| permissions: string[] | ||
| marked_for_deletion: boolean | ||
| } | null | ||
|
|
||
| async function getUser(): Promise<User> { | ||
| const cookieStore = await cookies(); | ||
| const sessionId = cookieStore.get("sessionId")?.value; | ||
|
|
||
| if (!sessionId) { | ||
| return null; | ||
| } | ||
|
|
||
| const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/users/me`, { | ||
| headers: { | ||
| Cookie: `sessionId=${sessionId}`, | ||
| }, | ||
| cache: "no-store", | ||
| }); | ||
|
|
||
| if (res.status === 401) { | ||
| return null; | ||
| } | ||
|
|
||
| if (!res.ok) { | ||
| return null; | ||
| } | ||
|
|
||
| return res.json(); | ||
| } | ||
|
|
||
| export default async function DashboardLayout({ children }: { children: ReactNode }) { | ||
| const user = await getUser(); | ||
|
|
||
| if (!user) { | ||
| redirect("/login"); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="h-screen w-screen flex flex-col bg-[url(/bg_new.png)] bg-cover"> | ||
| <div className="flex-1 p-4 pb-24 md:pb-32 lg:pb-40 overflow-auto"> | ||
| <div className="bg-red-300/50 backdrop-blur-md rounded-lg shadow-lg p-6 min-h-full"> | ||
| <UserProvider user={user}> | ||
| {children} | ||
| </UserProvider> | ||
| </div> | ||
| </div> | ||
| <DashNav /> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { cookies } from "next/headers"; | ||
| import Link from "next/link"; | ||
| import ProjectCard from "@/components/dashboard/ProjectCard"; | ||
|
|
||
| export type Project = { | ||
| project_id: number; | ||
| project_name: string; | ||
| hackatime_projects: string[]; | ||
| hackatime_total_hours: number; | ||
| last_updated: string; | ||
| repo: string; | ||
| demo_url: string; | ||
| preview_image: string; | ||
| shipped: boolean; | ||
| }; | ||
|
|
||
| async function getProjects(): Promise<Project[]> { | ||
| const cookieStore = await cookies(); | ||
| const sessionId = cookieStore.get("sessionId")?.value; | ||
| console.log(sessionId); | ||
|
|
||
| if (!sessionId) { | ||
| return []; | ||
| } | ||
|
|
||
| const req = new Request( | ||
| `${process.env.NEXT_PUBLIC_API_URL}/api/v1/projects`, | ||
| { | ||
| method: "GET", | ||
| headers: { | ||
| Cookie: `sessionId=${sessionId}`, | ||
| }, | ||
| cache: "no-store", | ||
| } | ||
| ) | ||
|
|
||
| console.log(req) | ||
|
|
||
| const res = await fetch(req); | ||
|
|
||
| console.log(res) | ||
|
|
||
| if (!res.ok) { | ||
| return []; | ||
| } | ||
|
|
||
| return res.json(); | ||
| } | ||
|
|
||
| export default async function Page() { | ||
| const projects = await getProjects(); | ||
|
|
||
| return ( | ||
| <div> | ||
| <h1 className="text-3xl font-bold mb-6">My Projects</h1> | ||
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | ||
| <Link href="/dashboard/projects/new"> | ||
| <div className="rounded-lg overflow-hidden shadow-md hover:shadow-lg transition-shadow bg-gray-50 h-full min-h-[200px] flex items-center justify-center cursor-pointer border-2 border-dashed border-gray-300 hover:border-gray-400"> | ||
| <div className="text-center p-4"> | ||
| <span className="text-4xl text-gray-400">+</span> | ||
| <p className="text-gray-600 mt-2">New Project</p> | ||
| </div> | ||
| </div> | ||
| </Link> | ||
| {projects.map((project) => ( | ||
| <ProjectCard key={project.project_id} project={project} /> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using next-auth 4.x with Next.js 16.x may have compatibility issues. The next-auth package is at version 4.24.13, but the stable version recommended for Next.js App Router (v13+) is NextAuth.js v5 (Auth.js). Consider upgrading to the latest Auth.js to ensure full compatibility with Next.js 16 and the App Router architecture.