diff --git a/CHANGELOG.md b/CHANGELOG.md index 18c5efa..f1e39d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- **API surface expansion** (#38, PR #39): full CRUD for ten entities + that were in `setup/TimeTracker.sql` but lacked endpoints — Worker, + Company, BillingType, InventoryItem, Job, Invoice, CustomerPayment, + InvoiceJob, ProductEntry, VersionInfo. Path count went from 7 to 35. +- Three centralized auth-scoping patterns in `middleware/auth.js`: + - Direct `compId` scoping (Worker, BillingType, InventoryItem) — + same as Customer. + - Customer-scoped via new `getCompanyIdByCustomerId()` helper + (Job, Invoice, CustomerPayment) — auth walks parent FK. + - Job-scoped via new `getCompanyIdByJobId()` helper + (InvoiceJob, ProductEntry) — auth walks two-hop FK. +- Specials: Company has `compId` IS the company id (master-only + POST/DELETE/list; GET/PATCH scoped to own row). VersionInfo is + global, no archive column, reads open to any authKey, mutations + master-only, DELETE is a hard destroy. +- Migration `20260517000000-purchase-orders-and-archive-columns`: + creates `PurchaseOrderHeaders`, `PurchaseOrderLines`, + `PurchaseOrderVendors`, and `InventoryTransactions` tables (omitted + from the initial PG port of the BACPAC), and retrofits the missing + `invitArch` and `injbArch` columns the new soft-delete logic + depends on. +- `docker compose` now applies Sequelize migrations between the SQL + bootstrap and the api start (new `migrate` one-shot service). + Without this, migrations landed after the baseline never applied + to containerized deploys. (#40, PR #41) +- `tini` runs as PID 1 in the container image for clean signal + forwarding to the Node process (server.js already had the + graceful-shutdown handler; tini just makes sure it gets the + signal). (#40, PR #41) +- OCI `org.opencontainers.image.*` labels on the runtime image + (source URL, license, vendor). (#40, PR #41) + +### Changed +- `sequelize-cli` moved from `devDependencies` to `dependencies` so + the production `npm ci --omit=dev` build can run migrations. +- HEALTHCHECK in the Dockerfile uses Node's built-in `http` module + instead of `wget`; drops the `wget` apt-install layer. (#40) +- `.dockerignore` excludes `tests/`, `vitest.config.js`, `README.md`, + `CHANGELOG.md`, and `docs/` from the runtime image; explicitly + keeps `LICENSE` (Apache-2.0 §4(c) requires it accompany derivative + works, including container images). + +### Added (earlier in [Unreleased] window) - Codeberg mirror at https://codeberg.org/CryptoJones/TimeTrackerAPI; README now carries badges for both forges. - `GET /healthz` liveness + DB-readiness probe. No auth. Returns diff --git a/README.md b/README.md index 511b2f0..61cbbde 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,16 @@ Working example at [node.timetrackerapi.com](http://node.timetrackerapi.com). | `GET /v1/timeentry/bycompany/:id` | yes (`authKey`) | List time entries for a company. Query params: `customerId` (filter), `from` / `to` (ISO 8601 date range on `teStartedAt`), `limit` (default 100, max 500). Ordered most-recent first. | | `PATCH /v1/timeentry/:id` | yes (`authKey`) | Partial update. Updatable: `teDescription`, `teStartedAt`, `teEndedAt`, `teBillable`. `teMinutes` is recomputed on bound change. | | `DELETE /v1/timeentry/:id` | yes (`authKey`) | Soft-delete (sets `teArch = true`). Entries are never physically removed via the API. | +| `* /v1/worker/*` | yes (`authKey`) | Full CRUD for Workers (`workerId`, `workerFName`, `workerLName`, `workerTitle`, `workerDefaultBillType`, `workerCompId`, `workerArch`). Direct company scoping via `workerCompId`. Endpoints: `POST /v1/worker`, `GET /v1/worker/:id`, `GET /v1/worker/bycompany/:id`, `PATCH /v1/worker/:id`, `DELETE /v1/worker/:id`. | +| `* /v1/billingtype/*` | yes (`authKey`) | Full CRUD for BillingTypes (hourly rates a Worker can default to). Same shape as Worker. | +| `* /v1/inventoryitem/*` | yes (`authKey`) | Full CRUD for InventoryItems. Same shape as Worker. | +| `* /v1/company/*` | yes (`authKey`) | Companies. Master keys only for `POST /v1/company`, `DELETE /v1/company/:id`, and `GET /v1/company` (list); non-master keys may `GET /v1/company/:id` and `PATCH /v1/company/:id` for their own row only. | +| `* /v1/job/*` | yes (`authKey`) | Jobs (customer-scoped via `jobCustId` → `custCompId`). Endpoints: `POST`, `GET /:id`, `GET /bycustomer/:id`, `PATCH /:id`, `DELETE /:id`. | +| `* /v1/invoice/*` | yes (`authKey`) | Invoices (customer-scoped). Same shape as Job. | +| `* /v1/customerpayment/*` | yes (`authKey`) | Customer payments (customer-scoped). `GET /bycustomer/:id` lists newest first. | +| `* /v1/invoicejob/*` | yes (`authKey`) | Invoice line items (job-scoped via `injbJobId` → Job → Customer.custCompId). `GET /byinvoice/:id` lists per invoice. | +| `* /v1/productentry/*` | yes (`authKey`) | Product entries consumed on a Job (job-scoped). `GET /byjob/:id` lists per job. | +| `* /v1/versioninfo/*` | yes (`authKey`) | Schema/build version records. Reads open to any `authKey`; mutations require a master key. `DELETE` is a hard destroy (no archive column on this table). | Every v1 request must include the API key in the `authKey` HTTP header. The `/healthz` endpoint is intentionally unauthenticated so it can be