Skip to content

feat: foundation for auth — environment, database, e-mail and error format (LUI-135) - #2

Merged
argentinaluiz merged 14 commits into
feature/projeto-webfrom
feature/lui-135-fundacao-ambiente-banco-email-erros
Oct 9, 2026
Merged

argentinaluiz merged 14 commits into
feature/projeto-webfrom
feature/lui-135-fundacao-ambiente-banco-email-erros

Conversation

@argentinaluiz

Copy link
Copy Markdown
Contributor

Summary

The base every authentication slice of feature 01 builds on (LUI-135). It delivers no user-facing behaviour: it leaves the environment and the API ready for the first slice (LUI-137).

What changed

  • Environment: the dev Compose gains PostgreSQL and Mailpit. up -d --build --wait leaves the four services healthy, with .env created from .env.example, the RS256 JWT key pair generated and the migrations applied, with no manual step.
  • Database: Prisma 7.10 owns the schema and migrations. The first migration only enables citext and pg_trgm; domain tables come with the next tickets.
  • Config: a config module is the only place that reads the environment. It validates everything against a Joi schema at boot (including the JWT keys as a real, matching RSA pair) and the API refuses to start when a variable is missing or invalid.
  • Errors: routes live under /v1, input is validated, and every failure goes out as RFC 9457 application/problem+json with a stable code. Unexpected errors become a 500 with no internal detail.
  • E-mail: a MailSender interface with an SMTP driver, with bounded waits.
  • Tests: three suites by file suffix (pnpm test, pnpm test:int, pnpm test:e2e). Integration and e2e run against the real PostgreSQL (a separate _test database, cleaned before each test file) and the real Mailpit, with no doubles. The scaffold's sample controller and its tests are gone.
  • Docs: the root AGENTS.md describes the new services and bring-up; apps/api/AGENTS.md lists the three suites and the test base; docs/lld.md gains the error body (3.1) and the new variables (6); docs/hld.md records three open questions about production.

Verification

Run on a fresh clone of this branch (no .env, no node_modules, no volumes), following only the root AGENTS.md:

  • up -d --build --wait: the four services healthy in 19 s; migration applied; web, API and Mailpit answering from the host.
  • pnpm test: no test files, exits 0.
  • pnpm test:int: 38 passed.
  • pnpm test:e2e: 24 passed.
  • pnpm exec tsc --noEmit and pnpm lint: clean.
  • git status stays empty after the bring-up: .env, the keys and the generated Prisma client are ignored.

All twelve acceptance criteria of the ticket were checked this way. The per-file database cleanup was also proven by hand (rows planted in app_test were gone after running another test file); that proof is not an automated test.

Notes for review

  • Base is feature/projeto-web, not main: that branch still needs its own PR. It was one commit behind its local copy when this PR was opened, so d50401b (the handoff and implement skills) shows up here too.
  • Decisions that differ from, or complete, the documents: .env is created by a script instead of copied by hand; up --wait with healthchecks replaces "watch the logs"; the test database has no variable of its own (it is DATABASE_URL plus _test); the env schema only covers the variables this ticket uses.
  • Open questions recorded in section 7 of docs/hld.md, with no code for them here: how migrations reach staging and production (the production image does not apply them), whether DATABASE_URL in production uses the Cloud SQL socket form (the schema rejects it today), and how the JWT keys are delivered as secrets (only PEM with real line breaks is accepted).
  • Known and left as is: the validated env is cached in a module-level variable; database queries have no statement timeout; MAIL_FROM accepts a display name with an unquoted comma; the minimum RSA key size is not enforced.
  • The web project only changed in Dockerfile.dev (readiness file for the healthcheck) and in the root layout (lang="pt-BR" and the page title).

🤖 Generated with Claude Code

https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP

argentinaluiz and others added 13 commits October 9, 2026 02:19
The auth slices need a database and an inbox from the first run. The api container now prepares its own .env, RS256 keys and migrations on start, so a fresh clone needs no manual step, and healthchecks let 'up --wait' block until that is done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
Every auth slice relies on these: routes under /v1, one filter that shapes all errors with a stable code, env vars validated at boot, an e-mail interface with an SMTP driver, and e2e/integration suites that run against a real test database and Mailpit. The example controller is gone, so the unit suite must pass empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…body

Keeps each fact in its owner: environment in the root guide, code practices and commands in the API guide, error contract, env vars and the Prisma 7 pin in the LLD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
A third-party SDK error carrying statusCode 404 was answered as a client error and never logged. The filter now takes the status only from HttpException and body-parser errors, keeps deliberate 5xx statuses instead of flattening them to 500, and drops the connection when the response has already started.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
Subject configs re-read raw process.env, so conversion and defaults lived outside the schema and a value changed after boot skipped validation. The PEM regex also accepted unparsable keys and refused valid PKCS#1 ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
nodemailer defaults keep a send hanging for minutes on a stuck server, so the timeouts are now explicit and configurable. The Mailpit helper returned as soon as any message existed, which would hand a resend test the first e-mail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
pg_isready over the unix socket also passes on the temporary init server, letting the api start migrating too early. The production image cannot apply migrations; that is out of scope here, so the open decision is written down in the HLD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…ne place

The three Vitest configs, the env stub cleanup and the problem+json
assertions were copied across files, and the env schema ran through two
separate mechanisms. Each now has a single owner, so the next feature
copies one pattern instead of four.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
With the pg adapter, $connect() opens no connection, so the API booted
against a dead database and only failed on the first request. The env
setup script also replaced a key the developer had put in .env when the
other half of the pair was missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
The test database URL is now derived once per run and handed to the test
files, so a development database whose name already ends in _test can no
longer be truncated. The env is validated once per set of values, SMTP
connections are pooled, tests read mail from the server SMTP_URL points
to, and the scaffold leftovers are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…ecks

A database that accepts the connection and never answers held the boot
forever, because pg waits without a limit by default. The env schema now
also refuses a JWT public key that is not the pair of the private one
and a MAIL_FROM without an address, which used to fail only at runtime.

The test base hands the Mailpit address to the test files once, so a
test that swaps SMTP_URL keeps reading from the right server, and a test
app that fails to boot is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…he SMTP idle wait

A HttpException carrying a 2xx/3xx status went out as a problem document
with a success status. The SMTP socket timeout doubled as the pool's
idle limit, so pooled connections were dropped before being reused; the
inactivity wait now has its own, longer setting.

Also documents how to repair the environment when the API container
exits during setup, and the Prisma CLI config as the second place that
reads the environment directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
Copilot AI balanced review requested due to automatic review settings October 9, 2026 06:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The error filter can misclassify third-party errors as trusted framework errors and expose their status codes.

1 open finding
What changed in this PR

Establishes the API foundation required by upcoming authentication features.

Changes:

  • Adds PostgreSQL, Prisma, Mailpit, validated configuration, and SMTP infrastructure.
  • Standardizes validation and RFC 9457 error responses under /v1.
  • Introduces unit, integration, and end-to-end test suites with real dependencies.
File Description
skills-lock.json Registers new agent skills.
docs/​lld.md Documents infrastructure and API contracts.
docs/​hld.md Updates environments and open decisions.
compose.dev.yaml Adds PostgreSQL, Mailpit, and healthchecks.
apps/​web/​Dockerfile.dev Signals dependency readiness.
apps/​web/​app/​layout.tsx Localizes metadata and language.
apps/​api/​vitest.shared.ts Defines shared test configuration.
apps/​api/​vitest.config.ts Configures unit tests.
apps/​api/​vitest.config.int.ts Configures integration tests.
apps/​api/​vitest.config.e2e.ts Configures end-to-end tests.
apps/​api/​test/​test-base.e2e-spec.ts Verifies the test foundation.
apps/​api/​test/​support/​test-env.ts Derives test dependency URLs.
apps/​api/​test/​support/​setup.ts Initializes each test file.
apps/​api/​test/​support/​problem.ts Asserts problem responses.
apps/​api/​test/​support/​mailpit.ts Reads captured test mail.
apps/​api/​test/​support/​global-setup.ts Migrates and provides test services.
apps/​api/​test/​support/​database.ts Cleans the test database.
apps/​api/​test/​support/​create-test-app.ts Builds complete test applications.
apps/​api/​test/​problem-details.e2e-spec.ts Tests validation and error responses.
apps/​api/​test/​app.e2e-spec.ts Removes scaffold end-to-end tests.
apps/​api/​src/​main.ts Applies configuration and shutdown hooks.
apps/​api/​src/​infra/​mail/​smtp-mail-sender.ts Implements bounded SMTP delivery.
apps/​api/​src/​infra/​mail/​smtp-mail-sender.int-spec.ts Tests real SMTP delivery.
apps/​api/​src/​infra/​mail/​mail.module.ts Registers the mail provider.
apps/​api/​src/​infra/​mail/​mail-sender.ts Defines the mail abstraction.
apps/​api/​src/​infra/​database/​prisma.service.ts Adds the Prisma database client.
apps/​api/​src/​infra/​database/​database.module.ts Exposes database infrastructure.
apps/​api/​src/​config/​mail.config.ts Maps mail configuration.
apps/​api/​src/​config/​env.ts Caches validated environment values.
apps/​api/​src/​config/​env.schema.ts Validates environment and JWT keys.
apps/​api/​src/​config/​database.config.ts Maps database configuration.
apps/​api/​src/​config/​config.module.ts Centralizes application configuration.
apps/​api/​src/​config/​config.module.int-spec.ts Tests configuration validation.
apps/​api/​src/​config/​auth.config.ts Maps JWT configuration.
apps/​api/​src/​config/​app.config.ts Maps application configuration.
apps/​api/​src/​common/​validation/​input-validation.pipe.ts Adds global DTO validation.
apps/​api/​src/​common/​errors/​problem-details.filter.ts Formats global error responses.
apps/​api/​src/​common/​errors/​input-validation.error.ts Models field validation errors.
apps/​api/​src/​common/​errors/​error-code.ts Defines stable error codes.
apps/​api/​src/​common/​errors/​domain-error.ts Defines the domain error base.
apps/​api/​src/​app.setup.ts Applies the /v1 prefix.
apps/​api/​src/​app.service.ts Removes scaffold service.
apps/​api/​src/​app.module.ts Wires foundation modules and providers.
apps/​api/​src/​app.controller.ts Removes scaffold controller.
apps/​api/​src/​app.controller.spec.ts Removes scaffold unit test.
apps/​api/​scripts/​setup-env.mjs Creates local environment and JWT keys.
apps/​api/​README.md Replaces scaffold documentation.
apps/​api/​prisma/​schema.prisma Initializes the Prisma schema.
apps/​api/​prisma/​migrations/​migration_lock.toml Locks the migration provider.
apps/​api/​prisma/​migrations/​20261009000000_enable_extensions/​migration.sql Enables PostgreSQL extensions.
apps/​api/​prisma.config.ts Configures Prisma CLI behavior.
apps/​api/​pnpm-workspace.yaml Allows Prisma install scripts.
apps/​api/​package.json Adds infrastructure dependencies and scripts.
apps/​api/​Dockerfile.dev Automates development setup.
apps/​api/​Dockerfile Generates Prisma client during builds.
apps/​api/​AGENTS.md Documents API practices and commands.
apps/​api/​.prettierignore Excludes generated Prisma code.
apps/​api/​.oxlintrc.json Excludes generated Prisma code.
apps/​api/​.gitignore Ignores generated Prisma code.
apps/​api/​.env.example Supplies working local defaults.
apps/​api/​.dockerignore Excludes generated client from context.
AGENTS.md Documents Compose-based development.
.claude/​skills/​implement/​SKILL.md Adds implementation workflow.
.claude/​skills/​implement/​agents/​openai.yaml Configures implementation skill UI.
.claude/​skills/​implement-spec/​SKILL.md Adds spec implementation workflow.
.claude/​skills/​implement-spec/​agents/​openai.yaml Configures spec skill UI.
.claude/​skills/​handoff/​SKILL.md Adds session handoff workflow.
.claude/​skills/​handoff/​agents/​openai.yaml Configures handoff skill UI.
Files not reviewed (1)
  • apps/api/pnpm-lock.yaml: Generated file

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/api/src/common/errors/problem-details.filter.ts
The filter took any thrown object with expose, a string type and a 4xx
statusCode as an Express body-reader error, so a third-party error with
that shape leaked its status instead of becoming a 500.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces broad cross-cutting infrastructure and API contracts that warrant final human validation despite comprehensive tests.

0 open findings

1 resolved since last review
Files not reviewed (1)
  • apps/api/pnpm-lock.yaml: Generated file

🧠 Review effort: Balanced

@argentinaluiz
argentinaluiz merged commit cf2ab92 into feature/projeto-web Oct 9, 2026
1 check passed
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.

2 participants