Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading