Skip to content

Deploying Photos

The Photos product (images.theaccessible.org) has three deployable parts. The frontend calls the shared API, so order matters and the API change is not photos-specific.

PartPathDeploy commandServes
HTTP API (primary)workers/apinpm run deploy:lambdaapi-pdf.theaccessible.org via AWS Lambda + API Gateway
Node serverrepo rootnpm run rebuildbatch/async work on 10.1.1.4 (builds remotely from origin/main, zero-downtime)
Frontendapps/photosnpm run deployimages.theaccessible.org (Cloudflare Worker)

⚠️ The one thing that bites: two API entry points

workers/api has two app compositions and they drift:

  • src/index-aws.ts β†’ bundled by src/lambda.ts β†’ AWS Lambda. This is the production HTTP API the frontends call.
  • src/index.ts β†’ the Node server Docker image on 10.1.1.4.

Any new API route module must be app.route()-mounted in BOTH files. A route registered only in index.ts passes local tests but returns a generic {code:"NOT_FOUND"} 404 on api-pdf.theaccessible.org β€” it was never live on the serverless API. (This is exactly how the photos backend shipped dark until the mount was added to index-aws.ts.) The smoke test below guards against it.

Prerequisites

  • Run from the main checkout (~/Projects/accessible), not a git worktree. Worktrees have partial deps; the Lambda build (build:lambda + CDK synth) needs the full monorepo. If you must deploy from a worktree, first run: npm install (root), npm --prefix infra/cdk install, and npm --prefix workers/email-intake run build (CDK synth validates every Lambda asset path, even when deploying only the API stack).
  • AWS credentials authed (aws sts get-caller-identity).
  • wrangler authed (npx wrangler whoami).
  • SSH key for the Node server (~/.ssh/nightly-audit β†’ [email protected]).

Steps

  1. Merge to main. Production deploys build from the merged code (npm run rebuild literally git reset --hard origin/main on the server).

  2. Deploy the API to Lambda (do this before the frontend, or new endpoints 404):

    Terminal window
    cd workers/api && npm run deploy:lambda

    Deploys AccessiblePdfProd-Api (+ its dependency stacks). A current credentials could not be used to assume … Proceeding anyway line is a benign root-creds warning.

  3. Rebuild the Node server (only needed if index.ts/batch code or migrations changed; safe/zero-downtime otherwise):

    Terminal window
    npm run rebuild # from repo root
  4. Smoke the API before shipping the frontend:

    Terminal window
    npm --prefix apps/photos run smoke:prod

    All checks must pass β€” especially photos route mounted (asserts PHOTO_NOT_FOUND, proving the routes are on the Lambda).

  5. Deploy the frontend:

    Terminal window
    cd apps/photos && npm run deploy
  6. Smoke again and do a quick manual e2e: sign in β†’ upload (alt text required) β†’ comment β†’ follow β†’ copy an embed code and confirm the iframe renders with alt text.

Migrations

No migration runs as part of these deploys. If a PR adds a non-additive migration, apply it before the Node rebuild (see the general DB deploy notes); new photos routes to date reuse existing tables (photo_comments, follows, activity_events).

Rollback

  • Lambda: redeploy the previous commit (git checkout <sha> β†’ npm run deploy:lambda); CloudFormation updates the function in place.
  • Frontend: wrangler rollback on the theaccessible-images worker, or redeploy a prior commit.
  • Node server: npm run rebuild after pointing origin/main back, or roll the branch back and rebuild.

Smoke checklist (what smoke:prod verifies)

  • GET /health β†’ 200, status: healthy
  • GET /api/photos/photo/<nonexistent> β†’ 404 PHOTO_NOT_FOUND (routes mounted on Lambda)
  • GET /api/photos/me β†’ 401 (auth-gated route present)
  • GET /api/photos/embed/photo/<nonexistent> β†’ 404, HTML β€œPhoto unavailable”
  • GET images.theaccessible.org/ β†’ 200