diff --git a/.replit b/.replit deleted file mode 100644 index 6281b48..0000000 --- a/.replit +++ /dev/null @@ -1,35 +0,0 @@ -modules = ["nodejs-24", "postgresql-16"] - -[deployment] -router = "application" -deploymentTarget = "autoscale" - -[deployment.postBuild] -args = ["pnpm", "store", "prune"] -env = { "CI" = "true" } - -[workflows] -runButton = "Project" - -[agent] -stack = "PNPM_WORKSPACE" -expertMode = true - -[postMerge] -path = "scripts/post-merge.sh" -timeoutMs = 20000 - -[[ports]] -localPort = 8080 -externalPort = 80 - -[[ports]] -localPort = 8081 -externalPort = 8081 - -[[ports]] -localPort = 26015 -externalPort = 3000 - -[nix] -channel = "stable-25_05" diff --git a/.replitignore b/.replitignore deleted file mode 100644 index 9eb019c..0000000 --- a/.replitignore +++ /dev/null @@ -1,5 +0,0 @@ -# The format of this file is identical to `.dockerignore`. -# It is used to reduce the size of deployed images to make the process of publishing faster. - -# No need to store the pnpm store twice. -.local diff --git a/replit.md b/README.md similarity index 65% rename from replit.md rename to README.md index ee5bc20..2c85129 100644 --- a/replit.md +++ b/README.md @@ -2,10 +2,30 @@ Community platform for listing and rating developer/productivity tools by usefulness and usability. -## Run & Operate +## Stack -- `pnpm --filter @workspace/api-server run dev` — run the API server (port 8080) -- `pnpm --filter @workspace/toolrate run dev` — run the frontend (port 26015) +- pnpm workspaces, Node.js 24, TypeScript 5.9 +- API: Express 5 + express-session + connect-pg-simple +- DB: PostgreSQL + Drizzle ORM +- Validation: Zod (`zod/v4`), `drizzle-zod` +- API codegen: Orval (from OpenAPI spec) +- Build: esbuild (CJS bundle) +- Frontend: React 19 + Vite + TanStack Query + wouter + shadcn/ui + recharts + +## Packages + +- `lib/api-spec` — OpenAPI contract (source of truth) +- `lib/api-zod` — generated Zod schemas from the spec +- `lib/api-client-react` — generated React Query hooks +- `lib/db` — Drizzle schema + DB access +- `artifacts/api-server` — Express backend +- `artifacts/toolrate` — React frontend +- `artifacts/mockup-sandbox` — component preview + +## Run & develop + +- `pnpm --filter @workspace/api-server run dev` — run the API server +- `pnpm --filter @workspace/toolrate run dev` — run the frontend - `pnpm run typecheck` — full typecheck across all packages - `pnpm run build` — typecheck + build all packages - `pnpm --filter @workspace/api-spec run codegen` — regenerate API hooks and Zod schemas from the OpenAPI spec @@ -13,29 +33,16 @@ Community platform for listing and rating developer/productivity tools by useful ## Required env vars -- `DATABASE_URL` — Postgres connection string (auto-provisioned) -- `SESSION_SECRET` — Session signing secret (already set) +- `DATABASE_URL` — Postgres connection string +- `SESSION_SECRET` — session signing secret +- `VOTER_SECRET` — secret for voter submissions +- `LOCAL_ADMIN_USERNAME` / `LOCAL_ADMIN_PASSWORD` — local admin login -## Keycloak (optional) +## Auth -Set these to enable login: -- `KEYCLOAK_URL` — e.g. `https://auth.example.com` -- `KEYCLOAK_REALM` — realm name -- `KEYCLOAK_CLIENT_ID` — client ID -- `KEYCLOAK_CLIENT_SECRET` — client secret -- `APP_URL` — public base URL for OAuth callback (optional, auto-detected if omitted) - -Without these, the app runs in read-only mode (browsing and viewing ratings works, submitting tools/ratings requires login). - -## Stack - -- pnpm workspaces, Node.js 24, TypeScript 5.9 -- API: Express 5 + openid-client (Keycloak OIDC) + express-session + connect-pg-simple -- DB: PostgreSQL + Drizzle ORM -- Validation: Zod (`zod/v4`), `drizzle-zod` -- API codegen: Orval (from OpenAPI spec) -- Build: esbuild (CJS bundle) -- Frontend: React 19 + Vite + TanStack Query + wouter + shadcn/ui + recharts +- Session-based auth (not JWT), sessions stored in Postgres via connect-pg-simple. +- Write operations (create/update/delete tools, submit ratings) require auth; reads are public. +- Login via local admin user (`LOCAL_ADMIN_*`). Keycloak/OIDC support exists as an optional legacy path. ## Where things live @@ -45,7 +52,7 @@ Without these, the app runs in read-only mode (browsing and viewing ratings work - `lib/api-zod/src/generated/` — generated Zod schemas (do not edit) - `artifacts/api-server/src/routes/` — Express route handlers - `artifacts/api-server/src/middleware/auth.ts` — `requireAuth` middleware -- `artifacts/api-server/src/routes/auth.ts` — Keycloak OIDC login/callback/logout/me +- `artifacts/api-server/src/routes/auth.ts` — login/logout/me - `artifacts/toolrate/src/pages/` — frontend pages - `artifacts/toolrate/src/components/` — shared components (layout, tool-card, category-combobox, feature-input) - `artifacts/toolrate/src/hooks/use-auth.ts` — auth state hook @@ -54,9 +61,9 @@ Without these, the app runs in read-only mode (browsing and viewing ratings work - Contract-first: OpenAPI spec → codegen → typed hooks + Zod schemas. Never hand-write fetch calls. - Session-based auth (not JWT) — sessions stored in Postgres via connect-pg-simple. -- Write operations (create/update/delete tools, submit ratings) require auth. Reads are public. +- Write operations (create/update/delete tools, submit votes) require auth. Reads are public. - Category and feature autocomplete are client-side filtered against live API data (no separate index). -- Grafana can consume `/api/analytics/*` endpoints directly via JSON datasource plugin. +- Grafana can consume `/api/analytics/*` endpoints directly via the JSON datasource plugin. ## Product @@ -66,16 +73,12 @@ Without these, the app runs in read-only mode (browsing and viewing ratings work - Analytics dashboard: top tools chart, category breakdown, score distribution histograms - Grafana integration: all `/api/analytics/*` endpoints return clean JSON -## User preferences +## Deployment -_Populate as you build — explicit user instructions worth remembering across sessions._ +- Container image: `git.kubebase.de/admin/tool-evaluator:` (pushed by CI) +- Deployed to k3s via the `apps` repo (kustomize), served at `https://toolr.kubebase.de` ## Gotchas - Orval clears the output folder during codegen — transient HMR errors in the dev server are normal and auto-recover. -- Auth routes use PKCE — the code_verifier is stored in the session, not in-memory state. -- `trust proxy: 1` is set on Express so that session cookies work correctly behind Replit's reverse proxy. - -## Pointers - -- See the `pnpm-workspace` skill for workspace structure, TypeScript setup, and package details +- Auth uses PKCE — the `code_verifier` is stored in the session, not in-memory state. \ No newline at end of file diff --git a/artifacts/api-server/.replit-artifact/artifact.toml b/artifacts/api-server/.replit-artifact/artifact.toml deleted file mode 100644 index 814b9ad..0000000 --- a/artifacts/api-server/.replit-artifact/artifact.toml +++ /dev/null @@ -1,32 +0,0 @@ -kind = "api" -previewPath = "/api" # TODO - should be excluded from preview in the first place -title = "API Server" -version = "1.0.0" -id = "3B4_FFSkEVBkAeYMFRJ2e" - -[[services]] -localPort = 8080 -name = "API Server" -paths = ["/api"] - -[services.development] -run = "pnpm --filter @workspace/api-server run dev" - -[services.production] - -[services.production.build] -args = ["pnpm", "--filter", "@workspace/api-server", "run", "build"] - -[services.production.build.env] -NODE_ENV = "production" - -[services.production.run] -# we don't run through pnpm to make startup faster in production -args = ["node", "--enable-source-maps", "artifacts/api-server/dist/index.mjs"] - -[services.production.run.env] -PORT = "8080" -NODE_ENV = "production" - -[services.production.health.startup] -path = "/api/healthz" diff --git a/artifacts/mockup-sandbox/.replit-artifact/artifact.toml b/artifacts/mockup-sandbox/.replit-artifact/artifact.toml deleted file mode 100644 index 8f94c25..0000000 --- a/artifacts/mockup-sandbox/.replit-artifact/artifact.toml +++ /dev/null @@ -1,17 +0,0 @@ -kind = "design" -previewPath = "/__mockup" -title = "Canvas" -version = "1.0.0" -id = "XegfDyZt7HqfW2Bb8Ghoy" - -[[services]] -localPort = 8081 -name = "Component Preview Server" -paths = ["/__mockup"] - -[services.env] -PORT = "8081" -BASE_PATH = "/__mockup" - -[services.development] -run = "pnpm --filter @workspace/mockup-sandbox run dev" diff --git a/artifacts/toolrate/.replit-artifact/artifact.toml b/artifacts/toolrate/.replit-artifact/artifact.toml deleted file mode 100644 index 6afe456..0000000 --- a/artifacts/toolrate/.replit-artifact/artifact.toml +++ /dev/null @@ -1,31 +0,0 @@ -kind = "web" -previewPath = "/" -title = "ToolRate — Tool-Bewertungsplattform" -version = "1.0.0" -id = "artifacts/toolrate" -router = "path" - -[[integratedSkills]] -name = "react-vite" -version = "1.0.0" - -[[services]] -name = "web" -paths = [ "/" ] -localPort = 26015 - -[services.development] -run = "pnpm --filter @workspace/toolrate run dev" - -[services.production] -build = [ "pnpm", "--filter", "@workspace/toolrate", "run", "build" ] -publicDir = "artifacts/toolrate/dist/public" -serve = "static" - -[[services.production.rewrites]] -from = "/*" -to = "/index.html" - -[services.env] -PORT = "26015" -BASE_PATH = "/" diff --git a/scripts/post-merge.sh b/scripts/post-merge.sh deleted file mode 100644 index ab61c44..0000000 --- a/scripts/post-merge.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e -pnpm install --frozen-lockfile -pnpm --filter db push