Skip to content
Open

dev #32

Show file tree
Hide file tree
Changes from all 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"]
}
}
610 changes: 18 additions & 592 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.4.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.3",
"@biomejs/biome": "^1.9.4",
"@tailwindcss/postcss": "^4.1.18",
"@types/bun": "^1.3.8",
"@types/node": "^20.19.30",
"@types/react": "^19.2.10",
"@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