Skip to content

sasmaq/hackathon

Repository files navigation

AI Coding Hackathon

A small React + TypeScript app for hackathon participants to browse AI coding projects, join one project at a time, switch or give up their current selection, and propose new project ideas.

Run Locally

Use Node.js 24:

nvm use
npm install
npm run dev

Vite will print the local URL, usually http://localhost:5173.

Build

npm run build

The production build is written to dist/.

End-to-End Tests (Playwright)

Install the Playwright browser once:

npx playwright install chromium

Run E2E tests:

npm run test:e2e

Playwright starts the app automatically and runs a smoke test against /.

Deploy

Deploy the dist/ folder to any static host. For Netlify, use:

  • Build command: npm run build
  • Publish directory: dist
  • Context env config: see netlify.toml (VITE_API_URL for deploy previews and production)

Environment and migration strategy for preview/production is documented in doc/environments.md.

The current MVP stores identity, signups, and pending proposals in localStorage; no backend environment variables are required yet.

Local Postgres Setup

Copy the env template once (or edit .env directly):

cp .env.example .env

Start Postgres with Docker Compose:

docker compose up -d

Connection details for local development:

  • Host: localhost
  • Port: POSTGRES_PORT from .env (default 5432)
  • Database: POSTGRES_DB from .env (default hackathon)
  • User: POSTGRES_USER from .env (default hackathon)
  • Password: POSTGRES_PASSWORD from .env (default hackathon)

Apply the schema migration:

set -a; source .env; set +a
docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" < server/db/migrations/001_initial_schema.sql

Seed starter projects:

set -a; source .env; set +a
docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" < server/db/seeds/001_projects.sql

Stop the database:

docker compose down

Backend Server (Hono)

All frontend and backend env vars now live in the root .env file.

Server env vars:

  • DATABASE_URL (required unless DEBUG_SQLITE_ONLY=true): Postgres connection string used by the backend.
  • CORS_ORIGIN (required): Comma-separated allowlist. Include local frontend (http://localhost:5173) and production Netlify origin(s).
  • PORT (optional): API server port (default 8787).
  • ADMIN_SECRET (optional): required only for PATCH /api/admin/projects/:id/status.
  • DEBUG_SQLITE_ONLY (optional): set true to run the API on SQLite as the primary database (no Postgres dependency).
  • DEBUG_SQLITE_MIRROR (optional): set true to mirror mutation payloads into local SQLite for debugging.
  • DEBUG_SQLITE_PATH (optional): SQLite file location (default server/db/sqlite/debug-mirror.sqlite).

Identity limitation (MVP): protected mutation routes resolve the participant from X-Client-Id and scope changes to that participant. This is not real authentication; if a client ID is exposed, another client could impersonate it. Future fix: replace header-only identity with session tokens or full auth.

Run the backend in dev mode:

npm run dev:server

Run backend in SQLite-only dev mode (no Postgres required):

npm run dev:server:sqlite

Core API usage:

  • GET /api/projects/cards?limit=20&offset=0
    • Returns approved project cards only.
    • Includes aggregated fields: signupCount, participantNamesPreview (up to 5), and isSignedUp (when X-Client-Id is provided).
    • Pagination output: { items, limit, offset, hasMore }.
  • GET /api/projects/:id
    • Returns approved project details only.
    • Includes full participant list for that project.

Build backend output:

npm run build:server

Serve the built frontend from Hono (single server for app + API):

npm run build
npm run build:server
npm run start:server

Then open http://localhost:8787.

Debug SQLite Mirror (Development)

Enable mirror mode in .env:

DEBUG_SQLITE_MIRROR=true
DEBUG_SQLITE_PATH=server/db/sqlite/debug-mirror.sqlite

When enabled, mutation payloads from bootstrap/join/switch/give up/propose/admin status updates are mirrored into SQLite tables (participants, projects, signups, events) for local debugging only.

Inspect mirrored data:

npm run debug:sqlite:inspect

Reset the mirror file:

npm run debug:sqlite:reset

Limitations:

  • Mirror writes are best-effort and never block API responses.
  • SQLite mirror is a debug aid, not a source of truth; Postgres remains authoritative.

SQLite-Only Primary Mode (Development)

Set this in .env when you want local backend development without Postgres:

DEBUG_SQLITE_ONLY=true
DEBUG_SQLITE_PATH=server/db/sqlite/debug-mirror.sqlite

In this mode, the API reads/writes participants, projects, and signups directly in SQLite.

Full Stack Local Development

Ensure local env file exists:

cp .env.example .env

Then run full stack locally:

docker compose up -d
set -a; source .env; set +a
docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" < server/db/migrations/001_initial_schema.sql
docker compose exec -T postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" < server/db/seeds/001_projects.sql
npm run dev:fullstack

This starts:

  • frontend (Vite) on http://localhost:5173
  • backend (Hono) on http://localhost:8787

Moderation Flow (SQL)

The app should only list projects with status = 'approved'. Proposals are created as pending, then manually reviewed:

List pending projects:

select id, title, status, created_at
from projects
where status = 'pending'
order by created_at desc;

Approve a project:

update projects
set status = 'approved'
where id = '<project-uuid>';

Reject a project:

update projects
set status = 'rejected'
where id = '<project-uuid>';

Optional admin API moderation:

curl -X PATCH "http://localhost:8787/api/admin/projects/<project-uuid>/status" \
  -H "Content-Type: application/json" \
  -H "X-Admin-Secret: $ADMIN_SECRET" \
  -d '{"status":"approved"}'

Frontend Security Notes

  • Netlify security headers are defined in public/_headers (CSP, frame-ancestors, Referrer-Policy, X-Content-Type-Options, Permissions-Policy).
  • Propose form input is validated on both client and server for required fields, max lengths, and script-tag rejection.
  • User-provided strings are rendered with standard React JSX interpolation (no dangerouslySetInnerHTML), so React escapes content by default.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors