Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.
Open

dev #32

Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .github/workflows/tsc.yml → .github/workflows/biome.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
name: tsc
name: biome
on:
- push
- pull_request

jobs:
lint:
name: Lint
name: Lint & Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install -g yarn # this is necessary for act, also just a good failsafe
- run: npm install -g yarn
- run: yarn install
- run: yarn lint
- run: yarn tsc --noEmit
- run: yarn biome:check
20 changes: 20 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"files": {
"ignore": [".next", "node_modules", "backend"]
}
}
601 changes: 15 additions & 586 deletions bun.lock

Large diffs are not rendered by default.

25 changes: 0 additions & 25 deletions eslint.config.mjs

This file was deleted.

6 changes: 3 additions & 3 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
images: {
domains: ['hc-cdn.hel1.your-objectstorage.com', 'ui-avatars.com'],
domains: ["hc-cdn.hel1.your-objectstorage.com", "ui-avatars.com"],
},
async redirects() {
return [
{
source: '/404',
destination: '/?404',
source: "/404",
destination: "/?404",
permanent: false,
},
];
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint",
"lint:fix": "eslint --fix"
"biome:check": "biome check .",
"biome:fix": "biome check --write .",
"lint": "biome lint .",
"format": "biome format --write ."
},
"dependencies": {
"clsx": "^2.1.1",
Expand All @@ -19,15 +21,14 @@
"swr": "^2.3.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.3",
"@biomejs/biome": "^1.9.4",
"@tailwindcss/postcss": "^4.1.18",
"@types/bun": "^1.3.6",
"@types/node": "^20.19.29",
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.3",
"eslint": "^9.39.2",
"eslint-config-next": "15.5.6",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3"
}
},
"trustedDependencies": ["@biomejs/biome"]
}
30 changes: 20 additions & 10 deletions src/app/api/dashboard/devlogs/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const sessionId = req.cookies.get("sessionId")?.value;
Expand All @@ -7,22 +7,32 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}

let data: unknown;
let data: {
project_id: string;
content: string;
media_url?: string;
description?: string | null;
};
try {
data = await req.json();
} catch {
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
}

if (data.description === "") {
data.description = null;
}
Comment on lines +21 to +23
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mutation of the parsed data on lines 16-18 occurs after type assertion but without proper type checking. Since the type annotation on line 10 doesn't provide runtime safety, the check data.description === "" could fail if description is undefined or missing. Consider restructuring this to avoid mutation and ensure type safety, for example: body: JSON.stringify({ ...data, description: data.description === "" ? null : data.description })

Copilot uses AI. Check for mistakes.
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/devlogs/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `sessionId=${sessionId}`,
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),
},
body: JSON.stringify(data),
});
);

const text = await res.text();
let json: unknown = null;
Expand Down
19 changes: 11 additions & 8 deletions src/app/api/dashboard/projects/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
const sessionId = req.cookies.get("sessionId")?.value;
Expand All @@ -15,14 +15,17 @@ export async function POST(req: NextRequest) {
}

try {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/projects/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `sessionId=${sessionId}`,
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),
},
body: JSON.stringify(data),
});
);

const text = await res.text();
let json: unknown = null;
Expand Down
22 changes: 12 additions & 10 deletions src/app/api/git/route.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { NextResponse } from "next/server";

let cache: {
hash: string
message: string
} | undefined
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"
)
"https://api.github.com/repos/hackclub/aces/commits/main",
);

if (!githubRes.ok) {
throw new Error(`GitHub API error: ${githubRes.status}`)
throw new Error(`GitHub API error: ${githubRes.status}`);
}

const json = await githubRes.json()
const json = await githubRes.json();

cache = {
hash: json.sha.substring(0, 7),
message: json.commit.message,
}
};
}

return NextResponse.json(cache)
return NextResponse.json(cache);
}
27 changes: 18 additions & 9 deletions src/app/api/hackatime/projects/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ export async function GET() {
}

// First, get user info to retrieve hackatime_id
const userRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/users/me`, {
headers: { Cookie: `sessionId=${sessionId}` },
});
const userRes = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/users/me`,
{
headers: { Cookie: `sessionId=${sessionId}` },
},
);

if (!userRes.ok) {
return NextResponse.json({ detail: "Failed to fetch user" }, { status: userRes.status });
return NextResponse.json(
{ detail: "Failed to fetch user" },
{ status: userRes.status },
);
}

const user = await userRes.json();
Expand All @@ -27,21 +33,24 @@ export async function GET() {

// Fetch all Hackatime projects for this user
const hackatimeRes = await fetch(
`https://hackatime.hackclub.com/api/v1/users/${hackatimeId}/stats?features=projects&start_date=2025-12-01T00:00:00Z`,
{ cache: "no-store" }
`https://hackatime.hackclub.com/api/v1/users/${hackatimeId}/stats?features=projects&start_date=2025-12-21T00:00:00Z`,
{ cache: "no-store" },
);

if (!hackatimeRes.ok) {
return NextResponse.json({ detail: "Failed to fetch Hackatime data" }, { status: 500 });
return NextResponse.json(
{ detail: "Failed to fetch Hackatime data" },
{ status: 500 },
);
}

const hackatimeData = await hackatimeRes.json();
const projects = hackatimeData?.data?.projects || [];

const projectMap: Record<string, number> = {};
projects.forEach((p: { name: string; total_seconds: number }) => {
for (const p of projects as { name: string; total_seconds: number }[]) {
projectMap[p.name] = p.total_seconds;
});
}

return NextResponse.json(projectMap);
}
6 changes: 3 additions & 3 deletions src/app/api/projects/[projectId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";

export async function PATCH(
req: NextRequest,
{ params }: { params: Promise<{ projectId: string }> }
{ params }: { params: Promise<{ projectId: string }> },
) {
const { projectId } = await params;
const cookieStore = await cookies();
Expand All @@ -30,7 +30,7 @@ export async function PATCH(
Cookie: `sessionId=${sessionId}`,
},
body: JSON.stringify(body),
}
},
);

const text = await res.text();
Expand Down
9 changes: 5 additions & 4 deletions src/app/api/projects/[projectId]/ship/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { cookies } from "next/headers";
import { type NextRequest, NextResponse } from "next/server";

export async function POST(
request: NextRequest,
{ params }: { params: Promise<{ projectId: string }> }
_req: NextRequest,
{ params }: { params: Promise<{ projectId: string }> },
) {
const { projectId } = await params;
const cookieStore = await cookies();
Expand All @@ -19,9 +19,10 @@ export async function POST(
{
method: "POST",
headers: {
"Content-Type": "application/json",
Cookie: `sessionId=${sessionId}`,
},
}
},
);

const text = await res.text();
Expand Down
9 changes: 6 additions & 3 deletions src/app/api/rsvp/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";

const cacheForEmergencies: number | undefined = undefined
const cacheForEmergencies: number | undefined = undefined;

export async function GET() {
try {
Expand All @@ -10,7 +10,7 @@ export async function GET() {
headers: {
Authorization: `Bearer ${process.env.RSVP_AIRTABLE_API_KEY}`,
},
}
},
);

const data = await res.json();
Expand All @@ -19,6 +19,9 @@ export async function GET() {

return NextResponse.json({ count });
} catch {
return NextResponse.json({ count: cacheForEmergencies, error: "Failed to fetch" }, { status: 500 });
return NextResponse.json(
{ count: cacheForEmergencies, error: "Failed to fetch" },
{ status: 500 },
);
}
}
19 changes: 19 additions & 0 deletions src/app/api/shop/form-url/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NextResponse } from "next/server";

// TODO: This is a placeholder implementation. Backend needs to be updated to:
// 1. Add a ShopConfig model/table to store form URLs and other shop settings
// 2. Create /api/v1/shop/config endpoint to fetch/update shop configuration
// 3. This route should then proxy to the backend instead of using env vars

export async function GET() {
const formUrl = process.env.NEXT_PUBLIC_SHOP_FORM_URL;

if (!formUrl) {
return NextResponse.json(
{ error: "Shop form URL not configured" },
{ status: 503 },
);
}

return NextResponse.json({ formUrl });
}
Loading