Skip to content

romankovsv/hackathon-da

Repository files navigation

Notes: Task is done completely and tested Used model Opus 3.7 and Sonnet 3.6 Used Agentic framework It took more than 12 hours of work, several times Cluade make pause with token limit 0 even it is in max subscription What helped : Planning, proper requirements, coverage with unit integration ui playwright and perf k6 tests What slows: constant asking for permission to execute, claude pauses limits of token

hackaton-da

A web-based chat service for up to 300 concurrent users — account auth, presence, public/private rooms with roles, friends / DMs / bans, image & file attachments, and notifications. Hackathon submission built to the spec in requirements_to_project.

Tech stack

Layer Choice
Client React 18 · Vite · TypeScript · TanStack Query · React Hook Form · Tailwind CSS · shadcn primitives
Server Node 20 · TypeScript (strict) · Express · ws · Zod · Pino · Multer
Database PostgreSQL 16 via Drizzle ORM (drizzle-kit migrations)
Cache / PS Redis 7 via ioredis (presence, pub/sub fan-out, rate-limit buckets)
Testing Vitest (unit + integration) · Playwright (E2E) · k6 (load)
Runtime Docker Compose (db, redis, server, client)

Run

docker compose up

That's the only command needed from a fresh clone. It builds and starts every service in the right order and seeds the database on first boot.

Open these URLs to verify:

What URL
Client (React SPA) http://localhost:5173
Server health probe http://localhost:3001/healthz
WebSocket endpoint ws://localhost:3001/ws (auth needed)
PostgreSQL postgres://app:app@localhost:5432/app

To wipe volumes and rebuild from scratch: npm run dev:reset.

Quickstart demo

  1. Register — open the client, click "Register", fill the form. You're signed in immediately and land on the dashboard.
  2. Create a room — go to Browse rooms, click Create room, pick public. You're auto-joined as owner.
  3. Invite / DM a friend — on the Contacts page, add a friend by username, accept the request in the other account, then click Message to open a DM. For private rooms, use Manage room → Invitations and send an invite; the recipient sees it in their Invitations inbox.
  4. Send a message / upload an image — in the chat pane, type a message and press Enter. Drag an image into the composer, paste one from the clipboard, or click Attach. Peers receive the frame in < 50 ms on localhost; an unread badge bumps in every other room the recipient isn't looking at.

Keyboard shortcuts inside a chat route: Enter sends, Shift+Enter newlines, / focuses the composer from anywhere, Cmd/Ctrl+K jumps to the room catalog, Esc closes dialogs.

Features

Every bullet maps to a requirements_to_project section.

  • §2.1 Auth & sessions — email + username + password registration, signed session cookies, active-sessions list, per-device sign-out, change password, forgot/reset, account deletion with cascade.
  • §2.2 Presenceonline / afk / offline per user, AFK after AFK_THRESHOLD_MS (default 60 s), fan-out scope = self ∪ co-room ∪ friends minus bans.
  • §2.3 Friends & DMs & user bans — friend requests (send / accept / decline / cancel), friend list, user-to-user bans, frozen-DM banner when one side blocks the other.
  • §2.4 Rooms & moderation — public catalog, private rooms by invitation, owner / admin / member roles, promote / demote, remove (= ban), unban, owner-only delete, room settings (name / description / visibility).
  • §2.5 Messaging — sub-second WS delivery, reply, edit, delete (tombstones), 3 KB body limit, infinite-scroll history.
  • §2.6 Attachments — up to 4 images or files per message, 10 MB each, served from the server's upload directory, orphan GC at boot.
  • §2.7 Notifications — per-conversation unread counters pushed on unread.update, invitation received / responded, room role changes, member joined / left, presence transitions.

Architecture

Classic three-tier split. The client is a Vite-bundled React SPA that talks to the server over REST for request/response work and over one authenticated WebSocket for push traffic. The server persists to PostgreSQL (Drizzle ORM + drizzle-kit migrations) and uses Redis for pub/sub fan-out across the WS process + per-tab presence keys + rate-limit buckets. Files are stored on the server's local disk under UPLOAD_DIR, streamed back through /api/attachments/:id. All boundary contracts (HTTP bodies, WS frames) live in shared/ as Zod schemas and are shared between both ends — the discriminated union wsMessage enumerates every frame.

Testing

npm run typecheck            # all three workspaces
npm run lint                 # eslint, zero warnings
npm test                     # vitest unit + integration — 234 tests
npm run e2e                  # Playwright — 24 specs (needs stack running)
npm run load                 # k6 chat-300-users scenario (needs stack running)

The load test's k6 script can also run through the official Docker image — no host install needed:

docker run --rm -i --add-host=host.docker.internal:host-gateway \
  --env API_URL=http://host.docker.internal:3001 \
  --env WS_URL=ws://host.docker.internal:3001/ws \
  grafana/k6 run - <tests/load/chat-300-users.js

See tests/load/README.md and tests/load/REPORT.md for the latest numbers — p95 chat delivery ≈ 26 ms at 300 concurrent users, well under the 3 s budget.

Layout

shared/      Zod contracts (ws-messages, http-contracts, enums, ids)
server/      Express + ws + Drizzle
client/      React + Vite
db/init/     Postgres first-boot SQL (extensions + test DB)
tests/e2e/   Playwright specs
tests/load/  k6 scripts + report

Environment

docker compose.yml ships sensible defaults. Copy .env.example to .env to override. Common knobs: DATABASE_URL, REDIS_URL, CLIENT_ORIGIN, UPLOAD_DIR, SESSION_COOKIE_NAME, AFK_THRESHOLD_MS, PRESENCE_TAB_TTL_SECONDS.

Deferred

§6 Jabber federation is not implemented. The scope is called out in the requirements but was deprioritised in favour of finishing §1–§5 to a submittable polish; the WS protocol + domain model are shaped so a future XEP-xxxx bridge can translate frames without schema changes.

Test Results

Full test run: all checks pass ✓

Suite Result
npm run typecheck ✅ green across 3 workspaces
npm run lint ✅ green (0 warnings)
npm test (server) ✅ 307/307 pass
npm test (client) ✅ 32/32 pass
Unit + integration total ✅ 339 pass
npm run e2e ✅ 32/32 Playwright pass
k6 chat — 300 VUs × 90 s ✅ 300/300 connected, 0 errors, p95 delivery 45 ms (budget 3000 ms), p99 78 ms, all thresholds pass
k6 presence — 50 VUs × 45 s ✅ transition p95 16 ms (budget 2000 ms), p99 ~45 ms

About

hackathon

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors