Skip to content

Cleanup: drop Vercel path, wire rate-limit env vars, add tests, harden CI, refresh docs - #29

Merged
southbayjay merged 1 commit into
mainfrom
devin/1790177541-cleanup-ci-docs
Sep 23, 2026
Merged

southbayjay merged 1 commit into
mainfrom
devin/1790177541-cleanup-ci-docs

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Cleanup/docs/CI third of the security review. No runtime behaviour change for anyone using the defaults; the one deployment-relevant change is that the repo is now explicitly Cloudflare Pages only.

Removed dead Vercel deployment path — api/ (4 files, incl. the un-rate-limited generate-worksheet.js copy and two duplicate health handlers), vercel.json, .vercelignore, root tsconfig.json (only included api/**/*.ts), client vercel-build script. Also dropped start-dev.js (superseded by root npm run dev), server/package.json.backup, server/tsconfig.node.json (Vite leftover, server has no vite config). Root package.json still lists cors/dotenv/express (only api/ used them) — left alone to avoid a lockfile conflict with #28; trivial follow-up once that lands.

Rate-limit env vars are now real — .env.example/README documented WORKSHEET_RATE_LIMIT_* / GENERAL_RATE_LIMIT_* but nothing read them.

// server/src/middleware/rateLimiting.ts
const envInt = (name, fallback) => /* unset/'' → fallback; else must be a positive integer or throw at startup */
export const WORKSHEET_RATE_LIMIT_WINDOW_MS   = envInt('WORKSHEET_RATE_LIMIT_WINDOW_MS', 3_600_000)
export const WORKSHEET_RATE_LIMIT_MAX_REQUESTS = envInt('WORKSHEET_RATE_LIMIT_MAX_REQUESTS', 10)
export const GENERAL_RATE_LIMIT_WINDOW_MS      = envInt('GENERAL_RATE_LIMIT_WINDOW_MS', 900_000)
export const GENERAL_RATE_LIMIT_MAX_REQUESTS   = envInt('GENERAL_RATE_LIMIT_MAX_REQUESTS', 100)

worksheetSlowDown shares the worksheet window; 429 messages are derived from the configured values. MAX_REQUEST_SIZE was never implemented on either runtime (both hard-code 1 KB) so it's removed from the docs rather than wired.

Dev-port drift fixed — .env.example, both READMEs and the Vite proxy said 3000; the server and client constants use 3001. The Vite proxy also had rewrite: path.replace(/^\/api/, ''), which would have hit /generate-worksheet (404) had anything used it — removed. .env.example now says up front that it belongs at server/.env (where config/index.ts actually loads it).

Tests (new, run in CI via npm test)

  • test/cloudflare-function.test.mjs — imports onRequestPost/onRequestOptions directly with an in-memory KV and a stubbed globalThis.fetch for OpenAI: invalid bodies → 400 without an upstream call; happy path returns the worksheet + RateLimit-*; upstream error is masked; 11th request in a window → 429; foreign origin 403 / allow-listed origin 204.
  • server/src/middleware/validation.test.ts — worksheetRequestSchema against every UI grade label, bare grades/bands, length bounds, filter words, complexity enum, .strict().
  • server/src/middleware/rateLimiting.test.ts — defaults, overrides, and startup failure on 0 / -5 / 1.5 / ten (each case in a child process since limits are read at import).
  • Runner is node:test (node --import tsx --test on the server). New server/tsconfig.eslint.json includes test files so ESLint's typed parsing and a tsc --noEmit -p tsconfig.eslint.json cover them without emitting them into dist/.

CI / supply chain — permissions: contents: read, actions/checkout + actions/setup-node pinned to v4.4.0 commit SHAs, persist-credentials: false, Dependabot github-actions ecosystem added, removed the step that loaded the deleted Vercel handler, smoke test also asserts /favicon.svg (index.html pointed at a non-existent /vite.svg; a small favicon.svg is added under server/client/public/).

Docs — root README rewritten around the actual stack (React 19 / Vite 8 / Tailwind 4 / Express 5 / Node 22, Cloudflare Pages + Functions + KV, 3001) with a step-by-step Pages deployment section (encrypted OPENAI_API_KEY, RATE_LIMIT_KV binding, WAF rate rule, OpenAI spend cap). server/README.md replaced (it described Docker/PM2/models/app.ts that don't exist) with an accurate layout + API reference. Note: the README's security section describes the state after #27 and #28 land (Helmet, _headers, TRUST_PROXY, output validation).

Verified locally on Node 24: npm test (15 tests pass), server tsc incl. tests, eslint . (0 errors), client tsc + vite build (favicon in dist/), production server with WORKSHEET_RATE_LIMIT_MAX_REQUESTS=2 → third request 429 with the derived message.

Link to Devin session: https://app.devin.ai/sessions/9385fb134d5f4dacb30bc28bd6bede11
Open in Devin Desktop: https://app.devin.ai/desktop/session/9385fb134d5f4dacb30bc28bd6bede11?variant=devin
Requested by: @southbayjay

…n CI, refresh docs

- Remove the unused Vercel deployment (api/, vercel.json, .vercelignore,
  root tsconfig.json) plus dead start-dev.js, server/package.json.backup
  and server/tsconfig.node.json; Cloudflare Pages is the only target
- Read WORKSHEET_/GENERAL_RATE_LIMIT_* from the environment (validated
  positive integers) instead of documenting knobs that were never read;
  drop the never-implemented MAX_REQUEST_SIZE from docs
- Fix dev-port drift: .env.example/README/vite proxy now say 3001 and
  the vite proxy no longer strips the /api prefix
- Add tests: request schema + rate-limit config (node:test via tsx) and
  the Cloudflare function (stubbed fetch, in-memory KV); npm test runs
  both and CI runs it
- CI: permissions: contents: read, SHA-pinned actions, persist-credentials
  off, typecheck includes tests; Dependabot covers github-actions
- Add favicon.svg (index.html pointed at a missing /vite.svg)
- Rewrite README/server README to match the actual stack and Cloudflare
  deployment

Co-Authored-By: Jason Maycock <jason@southbaycreations.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@southbayjay
southbayjay merged commit 2cc4abd into main Sep 23, 2026
1 check passed
southbayjay added a commit that referenced this pull request Sep 23, 2026
cors/dotenv/express and their @types were only imported by api/*, removed
in #29. The server workspace declares its own copies, so the lockfile
still hoists them for server/.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Jason Maycock <jason@southbaycreations.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant