[WSAPI-2] Bootstrap bun/TypeScript service scaffold + Docker stack #1
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature/WSAPI-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ticket
WSAPI-2 Bootstrap bun/TypeScript service scaffold + Docker stack
Summary
Foundational scaffold for the Wealthsimple API service — no Wealthsimple logic yet, just a working, deployable bun/TypeScript service:
package.json/tsconfig.jsonwith strict typing, 4-space indent, and lint/format tooling (ESLint flat config + Prettier), adapted frombudget-tracker-api/irrigo-apiconventions.lib/logger(pino,LOG_LEVEL-driven),lib/shutdown(idempotent SIGTERM/SIGINT teardown handler, injectable log/exit), andlib/port(PORT validated as a finite integer in[1, 65535]instead of silently binding a random port).routes/health(GET /health→200 { status: 'ok' }), acreate-appExpress factory (request logging, JSON body parsing,x-powered-bydisabled), and a rootindex.tsentrypoint.Dockerfile+docker-compose.yml: singleapiservice, runs as the image's non-rootbunuser, joined only to the pre-existing externalapi-sharedDocker network, noports:key — no public exposure, reachable only from other containers on that network.CLAUDE.md,docs/CI.md, and.forgejo/workflows/server.yml(checkout → setup-bun → cache → install → test → type-check → lint → format:check), mirroringbudget-tracker-api's CI conventions for this standalone repo.create-app/.test.ts) assertingGET /healthreturns 200 against the real assembled app, wired intobun testin CI.Prerequisite infra (already done, outside this diff): the external
api-sharedDocker network was created on the host andbudget-tracker-api/home-assistant-apiwere already joined to it and verified to resolve each other by hostname. This PR'sdocker-compose.ymljust referencesapi-sharedasexternal: true— cross-container reachability (budget-tracker-api→wealthsimple-api:8080/health→200) was verified live during implementation.🤖 Generated with Claude Code
https://claude.ai/code/session_01DSc34oDVEBXLz9TJWt26Nn
- .dockerignore: exclude .git, node_modules, and .env* (re-allowing .env.example) from the build context — closes off a real future leak, since this service's whole purpose is eventually holding real Wealthsimple credentials in .env, and `COPY . /app` has no other guard. - Dockerfile: chown the app directory to the image's built-in `bun` user (uid/gid 1000, matching the typical single-user host) and run as that user instead of root, since docker-compose.yml bind-mounts the host checkout read-write. - create-app/index.ts: app.disable('x-powered-by') so the framework fingerprint isn't advertised on every response. - docker-compose.yml: healthcheck now targets ${PORT:-8080} instead of a hardcoded 8080, so it can't silently drift from an overridden PORT. Verified via `docker compose build && up`: container runs as uid 1000 (bun), healthy, x-powered-by absent, and cross-container reachability from budget-tracker-api over api-shared still returns 200. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DSc34oDVEBXLz9TJWt26Nn- eslint.config.js: drop the stale `ignores: ['**/.test.ts', ...]` left over from before tsconfig.json's `include` fix — type-aware rules now actually cover test files, surfacing two real require-await violations in lib/shutdown/.test.ts (unnecessary `async` on synchronous teardowns), fixed alongside. - lib/shutdown/index.ts: `Teardown` over-corrected to `() => void | Promise<void>`, which (unlike a bare `void`-returning function type) does NOT tolerate a non-void return through the union — rejecting a normal teardown like `() => server.close()` (returns `this`, not void). Changed to `() => unknown`: non-dead, and the return value is discarded by createShutdownHandler regardless. - docker-compose.yml: reverted the healthcheck's `${PORT:-8080}` Compose interpolation (resolved from the host/shell env at config-parse time) back to reading `process.env.PORT` inside the container at runtime, matching how the app itself resolves PORT — avoids a healthcheck/server port mismatch if an operator has PORT exported in their own shell. .env.example's PORT comment updated to match. - docs/CI.md: workflow table still read `bunx tsc --noEmit`; updated to `bun run type-check` to match the actual workflow step (and CLAUDE.md's own "never invoke tsc directly" rule). - CLAUDE.md: architecture map was missing lib/port/, added earlier this round. - create-app/.test.ts: added an `x-powered-by` header assertion — the disable had no test. - lib/shutdown/.test.ts: onShutdown test cleanup now removes only the listeners each test itself registered (via a before/after snapshot), instead of unconditionally calling `process.removeAllListeners`, which could strip a handler registered by something else. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DSc34oDVEBXLz9TJWt26Nn