Commit Graph
83 Commits
Author SHA1 Message Date
toanhoangandClaude Sonnet 5 9e08c3f994 feat: add ba + pm agents to multi-agent dev workflow
Stakeholder should only need to state a business goal/priority, not
pick between implementation options or break work into tasks
themselves. `ba` turns a business ask into a concrete spec grounded in
docs/asset-license-management.md, `pm` breaks that spec into an
ordered, dependency-aware sequence of feature-implementer-sized tasks.
Neither writes code or resolves business-rule ambiguity — that stays
with the stakeholder.

Also fixes code-reviewer.md/feature-implementer.md pointing at the old
../processes/asset-license-management.md path — the doc moved into
docs/ inside this repo a while ago.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 08:36:21 -04:00
toanhoangandClaude Sonnet 5 87e2cca019 feat: add Dockerfile + app service for production deploy
Adds a production Docker image (node:20-alpine) and an `app` service in
docker-compose.yml so the Next.js app can run as a long-lived container
instead of just build/test in ephemeral containers. Requires `apk add
openssl` before `npm ci`, otherwise Prisma silently picks the wrong query
engine binary on Alpine and only fails at runtime. Also wires
AUTH_TRUST_HOST=true, required by Auth.js when running behind the NPM
reverse proxy.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-02 08:25:25 -04:00
toanhoang 539e511022 docs: update CLAUDE.md + docs for AssetItem entity 2026-07-31 16:10:57 +07:00
toanhoang 087e613a69 test: add AssetItem API tests 2026-07-31 16:04:58 +07:00
toanhoang 84f8177995 feat: add AssetItem API routes 2026-07-31 16:04:35 +07:00
toanhoang e7e359cbf7 feat: add AssetItem validation + CRUD (src/lib/assets.ts) 2026-07-31 16:04:22 +07:00
toanhoang 09b54f3c6b feat: add AssetItem Prisma model + migration 2026-07-31 16:04:01 +07:00
toanhoang 3f6497e2df docs: fix stale Employee entry, note fileParallelism scalability tradeoff 2026-07-31 15:25:34 +07:00
toanhoang 41381147f8 docs: update CLAUDE.md for Employee entity + Request FK retrofit
- Trang thai hien tai: Employee entity done (model, lib, API route,
  tests), Request.employee_id now a real FK to Employee - documents
  the intentional behavior change (employee_id must now be a valid
  UUID referencing a real Employee, not free text; non-existent
  references get a 400, not a 500). Also documents the vitest
  fileParallelism fix and the resulting 31/31 test count.
- Viec con do: remove Employee from the remaining-entities list.
2026-07-31 15:16:12 +07:00
toanhoang 2d838857d5 fix: serialize test file execution to avoid cross-file DB races
Each test file gets its own throwaway Postgres database by mutating
process.env.DATABASE_URL in tests/setup.ts's beforeAll. That per-file
DB switch is not safe under Vitest's default parallel file execution:
timing between two files' beforeAll/query calls can interleave enough
that both end up issuing real queries against the same throwaway DB.

This was invisible before because no two test files shared tables
with a real FK between them. It became visible once Request gained a
real FK to Employee: tests/employees.test.ts and tests/requests.test.ts
started intermittently deleting/reading each other's rows (FK
violations on deleteMany, wrong row counts), reproducible on every
run. Setting fileParallelism: false makes each file run to completion
before the next starts, so the per-file throwaway DB is genuinely
private. Confirmed: 31/31 tests pass reliably under plain npm test
(previously reproducibly 4 failing).
2026-07-31 15:15:11 +07:00
toanhoang 7e69efa4a8 fix: update Request validation/tests for Employee FK relation
- createRequestSchema now requires employee_id to be a UUID-format
  string (was a free-text non-empty check), matching the new FK to
  Employee in schema.prisma.
- createRequest() now returns a CreateRequestResult discriminated
  union and catches Prisma P2003 (FK violation), returning a friendly
  ok:false error instead of a raw 500 when employee_id does not
  reference an existing Employee. POST /api/requests branches on
  created.ok and returns 400 in that case.
- tests/requests.test.ts: every test that used to POST a fake string
  employee_id (emp-1, emp-2) now creates a real Employee first via
  createTestEmployee() and uses its id, since employee_id is now a
  real FK. Added coverage for a syntactically invalid UUID and for a
  well-formed but non-existent UUID (expects the friendly 400 from
  the P2003 catch, not a raw 500) - the exact case this retrofit was
  meant to protect against.
2026-07-31 15:14:55 +07:00
toanhoang 49d96b77e8 feat: relate Request.employee_id to Employee via FK
Retrofits Request.employeeId from a free-text String with no
referential integrity to a real relation: employeeId String @db.Uuid
+ employee Employee @relation(...), plus the inverse Employee.requests
back-relation field. Migration alters requests.employee_id from TEXT
to UUID with an FK constraint (drop+recreate, confirmed empty table -
no production/dev data existed to migrate).
2026-07-31 14:58:47 +07:00
toanhoang 10a8b0411d test: add Employee API tests
Mirrors tests/requests.test.ts's structure and @/auth mocking
pattern. Covers: successful create (with and without managerId),
empty name/department validation failures, malformed managerId UUID,
nonexistent managerId (FK violation -> friendly 400, not a raw 500),
a self-referential manager sanity check via direct Prisma update,
list ordering, empty list, and 401-when-unauthenticated for both
routes.
2026-07-31 14:57:37 +07:00
toanhoang a8964a091a feat: add Employee API routes
POST/GET /api/employees, same thin-route pattern as
src/app/api/requests/route.ts including the auth() 401 check
(middleware.ts excludes /api from page-route gating). POST also
surfaces createEmployee()'s FK-violation translation (nonexistent
managerId) as a 400, not a raw 500.
2026-07-31 14:55:50 +07:00
toanhoang eeaf946712 feat: add Employee validation + CRUD (src/lib/employees.ts)
Mirrors src/lib/requests.ts's shape: Zod schema, ValidationResult
discriminated union, CRUD via Prisma, toRecord() camelCase->snake_case
mapping. managerId is only format-checked as a UUID here; a
nonexistent managerId is caught via Prisma's P2003 FK-violation error
in createEmployee() and translated into a friendly {ok:false,error}
instead of leaking a raw Prisma error.
2026-07-31 14:54:48 +07:00
toanhoang 4c69bcf2c1 feat: add Employee Prisma model + migration
Employee directory entity per docs/asset-license-management.md's data
model table (id, name, department, manager). department stays a plain
String for now (not a separate Department entity). managerId is a
nullable self-relation (EmployeeManager) - top-level employees have
managerId = null.
2026-07-31 14:53:57 +07:00
toanhoang 3645e907bc docs: update CLAUDE.md with route-gating status
Moves the middleware/route-gating item out of Viec con do now that
it is done, and documents the auth.config.ts split, matcher choice,
the / home page being protected, and the vi.mock test approach in
Trang thai hien tai.
2026-07-31 12:39:06 +07:00
toanhoang 1770f68379 test: add auth gating tests for /api/requests
Mocks @/auth (vi.mock) instead of exercising the real auth(): that
helper reads cookies via next/headers under the hood, which only
works inside Next.js own request-handling machinery, not when these
tests import and call GET()/POST() directly. beforeEach mocks a
valid session by default so the existing business-logic tests are
unaffected, and two new tests mock a null session to assert both
GET and POST return 401 with { error: Unauthorized } and that POST
does not create a row.

middleware.ts itself has no automated test - it runs on the Edge
Runtime and is not exercised by vitest calling route handlers
directly. Verified manually instead via next build + next start:
curl confirmed GET / redirects to /login (307), GET /login stays
200 with no redirect loop, GET/POST /api/requests return 401 JSON
unauthenticated, and GET /api/auth/session (needed for the login
flow) is not blocked.
2026-07-31 12:38:47 +07:00
toanhoang cb7c646b3d feat: require auth on /api/requests routes
GET and POST both call auth() first and return 401 { error:
Unauthorized } when there is no session, before doing any
validation or DB work. This is the API-side half of route-gating -
middleware.ts handles page routes but deliberately excludes /api,
so API routes verify the session themselves.

Flat permission model only (per CLAUDE.md): this checks logged-in
or not, nothing more granular. No role/permission logic added.
2026-07-31 12:38:21 +07:00
toanhoang 65029480a9 feat: add middleware for page route protection
middleware.ts gates page routes by default (redirect to /login when
unauthenticated), except /login itself and Next.js internal asset
paths - config.matcher excludes /api entirely, /_next/static,
/_next/image, and favicon.ico. Default-deny: everything requires login
unless explicitly excluded, including the home page, rather than an
opt-in allowlist new routes could forget to join.

Builds its own lightweight NextAuth instance from auth.config.ts
(no Credentials provider) instead of importing auth from auth.ts, to
stay Edge-Runtime compatible - see auth.config.ts.

API routes are deliberately NOT gated here (config.matcher excludes
/api). Auth.js guidance is to verify the session as close to the data
fetching as possible rather than rely on middleware alone; API route
auth is added directly in each route handler instead (next commit).

This project pins Next.js 14.2.35, which predates the Next.js 16
middleware.ts -> proxy.ts rename, so this stays middleware.ts with a
default export, not proxy.ts.
2026-07-31 12:38:12 +07:00
toanhoang 68693c9b84 feat: split auth config into edge-safe auth.config.ts
auth.config.ts holds only the Edge-Runtime-safe parts of the Auth.js
config (session strategy, pages, jwt/session callbacks) with an empty
providers array. auth.ts spreads it and adds the real Credentials
provider on top for use in API routes/Server Components (Node runtime).

Needed for middleware.ts (next commit): the Credentials provider's
authorize() pulls in bcryptjs via verifyUserCredentials(), which is a
Node API not supported on the Edge Runtime that middleware.ts runs on.
Without this split, next build bundles bcryptjs/jose into the Edge
middleware chunk and warns; with it, the build is clean.
2026-07-31 12:37:58 +07:00
toanhoang c059e2f50f docs: record Auth verification results and follow-up gaps 2026-07-31 12:12:23 +07:00
toanhoang b6e3a4e386 test: add auth tests 2026-07-31 12:01:00 +07:00
toanhoang ceca1c51b4 feat: add login page UI 2026-07-31 11:59:35 +07:00
toanhoang a662ed8d6a feat: add Auth.js Credentials login config + route 2026-07-31 11:55:58 +07:00
toanhoang 43a20d8530 feat: add User password hashing + account creation script 2026-07-31 11:51:08 +07:00
toanhoang 41de13b1ff feat: add User Prisma model + migration 2026-07-31 11:48:55 +07:00
toanhoang ed3dc46456 docs: note future Gitea self-host + GitHub backup plan 2026-07-31 11:12:47 +07:00
toanhoang d72b73b2f5 docs: add git commit convention (Conventional Commits, split by layer) 2026-07-31 10:55:46 +07:00
toanhoang 2710f3b61d docs: record decision - no public Postgres exposure, keep DB+app in one compose stack
- dev/test Postgres stays local per-machine, not exposed
- prod (home Proxmox) Postgres stays private, cross-machine access via VPN/SSH tunnel
- keep db + app services in a single docker-compose.yml (already uses explicit
  network/volume/container names, so splitting later stays cheap if a real
  second consumer of the DB shows up)
2026-07-31 10:45:53 +07:00
toanhoang f2ca214b55 Move business-context doc from workspace processes/ into repo
- docs/asset-license-management.md (moved from ../processes/, fixed
  relative links to stay accurate from the new location)
- CLAUDE.md now points at docs/asset-license-management.md
- removed stale no-git-repo-yet note, added git init/push status
2026-07-30 18:32:01 +07:00
toanhoang 19b4c96eaa CLAUDE.md: make Môi trường section host-agnostic (Windows no-Node vs Linux VM native Node) 2026-07-30 18:27:09 +07:00
toanhoang afdbdbdcfb Initial commit: Next.js scaffold, Request entity, Prisma+Zod, Tailwind v4+shadcn
- Next.js 14 (TS, App Router) monolith
- Request entity (Draft state) via Prisma + Postgres, validated with Zod
- ESLint configured
- Tailwind v4 + shadcn/ui (new-york-v4 style)
- Vitest test suite (6 tests, runs against throwaway Postgres DB per file)
- Multi-agent dev workflow docs in CLAUDE.md and .claude/agents/
2026-07-30 18:14:36 +07:00