docs(dev): align setup with current stack

This commit is contained in:
cesnimda
2026-08-30 11:32:00 +02:00
parent 16ef3b9463
commit 2d7a51c3fd
10 changed files with 104 additions and 78 deletions
+13 -16
View File
@@ -1,6 +1,6 @@
# Job Tracker
Job Tracker is a simple, self-hosted app for tracking job applications with a React frontend and an ASP.NET Core API backed by SQLite.
Job Tracker is a self-hosted career workspace built with a Next.js/React frontend and an ASP.NET Core API. SQLite is the local default; MariaDB/MySQL is the supported server-database option.
## Features (high level)
@@ -21,9 +21,9 @@ Job Tracker is a simple, self-hosted app for tracking job applications with a Re
## Architecture
- `job-tracker-ui/`: React app (runs on `http://localhost:3000` in dev)
- `job-tracker-ui/`: statically exported Next.js 16 / React 19 app (runs on `http://localhost:3000` in dev)
- `JobTrackerApi/`: ASP.NET Core API (defaults to `http://localhost:5202`)
- SQLite DB file: defaults to `JobTrackerApi/jobtracker.db` unless `Data:Root` / connection string overrides it
- Database: SQLite defaults to `JobTrackerApi/jobtracker.db`; MariaDB/MySQL is selected with `Database:Provider`
- Attachments: stored on disk under `DataRoot/Attachments/<jobId>/...`
- Optional local AI service: `tools/summarizer/` (FastAPI) used by the API via `Ai:BaseUrl`
@@ -46,14 +46,14 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
### Prereqs
- .NET SDK `9.x` (API targets `net9.0`)
- Node.js (for the UI)
- (Optional) Python 3.x if running the AI service without Docker
- Node.js 24 and npm (matching CI)
- (Optional) Python 3.12 if running the AI service without Docker
### 1) Run the API
```bash
cd JobTrackerApi
dotnet restore
dotnet restore --locked-mode
dotnet run
```
@@ -99,8 +99,8 @@ If the placeholder development password no longer matches the local DB, use the
```bash
cd job-tracker-ui
npm install
npm start
npm ci
npm run dev
```
The UI defaults to calling `http://localhost:5202/api` when running on localhost (see `job-tracker-ui/src/api.ts`).
@@ -113,7 +113,7 @@ npx playwright install chromium
npm run test:e2e
```
The suite starts isolated API/SQLite and Next.js processes, then covers login, saved-job creation,
The suite uses `http://localhost:3300`, starts isolated API/SQLite and Next.js processes, then covers login, saved-job creation,
Career Workspace, and anonymous public-CV/PDF access. It is also a required CI gate.
### 4) (Optional) Run the AI service
@@ -183,6 +183,8 @@ Common keys:
- `NEXT_PUBLIC_API_BASE_URL`: override the API base URL (example: `http://localhost:5202/api`)
The complete environment template is `.env.example`. Production requirements and restore/rollback procedures are maintained in `deploy/README.md`; current component boundaries and supported providers are documented in `docs/architecture/current.md`.
## API endpoint reference
Base URL in local dev: `http://localhost:5202` (all routes are under `/api/...`).
@@ -308,11 +310,6 @@ Authentication:
- The background rules engine may automatically transition jobs to `Ghosted` based on rule settings.
- In Docker, the UI proxies `/api/*` to the backend service (see `job-tracker-ui/nginx.conf`).
## Ideas to improve the app (next steps)
## Project status and planned work
- Add first-class “timeline” view combining `JobEvent` history + correspondence + attachments into a single chronological stream (this also naturally supports the “Applied → Interview → Reply → …” flow you mentioned).
- Define a canonical pipeline/status model (enum + ordering) and drive UI badges/board columns from it; allow custom pipelines per user.
- Add Swagger/OpenAPI for the controllers (so endpoint docs stay in sync) + include example requests/responses.
- Add validation + problem-details responses consistently (and keep request/response DTOs stable and versioned).
- Add search improvements (full-text search, filter by tags, filter by date ranges, saved views).
- Add notifications (email/desktop) for follow-ups and upcoming deadlines.
This README documents released behaviour, not the roadmap. Validated findings and implementation status live in `docs/audits/audit-remediation-backlog.md` and `docs/work-programmes/master-progress.md`; archived phase documents are historical evidence and may describe superseded behaviour.
+5 -21
View File
@@ -65,29 +65,14 @@ EMAIL_FOLLOWUPREMINDERS_UPCOMINGDAYS=2
SUMMARIZER_BASE_URL=http://ai-service:8001
```
## Database recommendation
For production, yes — use a real database.
## Supported databases
### Recommended direction
Short term:
- SQLite is acceptable for a single-user or very small deployment
- keep backups and volume persistence
The application supports only these provider values:
Better production choice:
- MariaDB or PostgreSQL
- `sqlite` — local development and small single-instance deployments; persist and back up the data volume.
- `mariadb` or `mysql` — server deployments through the Pomelo EF Core provider. The production example uses MariaDB.
### My recommendation
- **PostgreSQL** if you want the best long-term maintainability and fewer edge cases
- **MariaDB** is also fine if that is what you already know or host elsewhere
If you stay on SQLite:
- okay for small personal use
- not ideal for concurrent writes, larger scale, or operational robustness
## Practical recommendation for this project
If this app is going to be a real production service on Ubuntu:
- move to PostgreSQL first if possible
- MariaDB is still a reasonable option if preferred
PostgreSQL is not implemented. Do not configure or recommend it without first adding an EF Core provider, migrations, startup validation, backup/restore support, and a provider test matrix.
## Deployment flow
@@ -117,7 +102,6 @@ if forwarded-header trust is enabled without a valid known CIDR.
- confirm AI service container is reachable from backend
- confirm reminder and admin/system pages load
- verify follow-up reminder emails are enabled only when intended and that links open the correct job/tab
hat links open the correct job/tab
---
+1 -1
View File
@@ -90,7 +90,7 @@ flowchart LR
**Backend:** ASP.NET Core net9.0, EF Core 9 (SQLite default; Pomelo MySQL/MariaDB via `Database:Provider`), ASP.NET Identity Core, JWT bearer (smart policy scheme: local + Google), built-in RateLimiter, DataProtection (file-system keys), Playwright (PDF export).
**Frontend:** **Next.js 16** + React 19 + **TypeScript 5.9** + MUI 7 (+ x-data-grid, x-date-pickers, lab), axios, react-router-dom 6, @tanstack/react-table, i18n EN + NB (custom provider), Jest/RTL.
**Frontend:** **Next.js 16** + React 19 + **TypeScript 5.9** + MUI 7 (+ x-data-grid, x-date-pickers, lab), axios, react-router-dom 7, @tanstack/react-table, i18n EN + NB (custom provider), Jest/RTL.
> Corrected 2026-07-31: the CRA migration is complete; direct Jest/Babel configuration replaced `react-scripts`.
+2
View File
@@ -569,6 +569,8 @@ SEC-008 implements the same durable state machine with `<final>.uploading` and `
### P3-2 — Rebuild the current developer/operator documentation
**Status (2026-08-30): repository scope complete.** The frontend/root/deployment/architecture sources now match Next.js 16, React Router 7, .NET 9, locked installs, current ports, and the implemented SQLite/MariaDB provider matrix. Clean install, lint, full frontend/backend tests, production build/TypeScript and locked NuGet restores pass; see `docs/verification/jt-018-developer-documentation.md` and V-193. A separate clean-machine/production operator rehearsal remains deployment evidence, not an undocumented implementation gap.
- **Findings/scope:** JT-018 plus JT-016; frontend README, supported database matrix, architecture/API/env/setup/test/deploy source of truth.
- **Dependencies:** Phase 02 behaviour/config decisions to avoid documenting transient state.
- **Acceptance criteria:** unfamiliar developer follows docs from clean clone through build/tests/local start; no CRA/PostgreSQL/stale API claims.
+1
View File
@@ -224,3 +224,4 @@ Output was reduced to filenames and commit counts. The token artifact appears un
| V-190 | Compatible parser dependency resolution; Linux CPU hash lock; clean hash install; `pip-audit`; generated parser boundary tests; focused/full backend and sidecar suites; Docker daemon probe | Repository root / `tools/summarizer` | Remove reachable upload-parser advisories and close unsafe fallback/resource-boundary paths without parsing hostile fixtures | PASS/PARTIAL — FastAPI 0.141.1/Starlette 1.6.0, Pillow 12.3.0, pypdf 6.16.2 and python-multipart 0.0.32 resolve and install from hashes; upload-facing packages audit clear; parser 32/32, focused backend 51/51 and backend 719/719. Signature/container/page/pixel/decompression/output limits pass; backend binary fallback is removed and unexpected failures are sanitized | Docker daemon unavailable, so production image smoke/container assertions did not run. `pip-audit` reports 45 Torch/Transformers model-stack advisories tracked separately under JT-017. One Starlette/httpx test-client warning and five SWIG warnings remain | SEC-006 implemented locally; SEC-007 boundary work in progress, with child-process and container isolation still required |
| V-191 | Real parser child extraction; minimal-environment probe; timeout/process-tree/capacity/stale-cleanup tests; `py_compile`; parser suite; Compose interpolation/control inspection | Repository root / `tools/summarizer` | Ensure untrusted document decode cannot consume the AI web process or inherit provider secrets and receives explicit runtime/container budgets | PASS/PARTIAL — parser 37/37 without warnings; TXT extraction executes in a child; provider/service secrets are absent; timeout kills parent and descendant; capacity recovers; stale cleanup preserves recent/unrelated paths. Compose resolves read-only root, `cap_drop: ALL`, no-new-privileges, 96 PIDs, 2 CPUs, 2 GiB memory, 768 MiB tmpfs and one model-cache volume | Windows proves deadline/process-tree behavior but cannot execute Linux `setrlimit`; Docker Desktop daemon is offline, so image build, non-root identity, inside-container rlimits and benign PDF/DOCX/image sizing remain unverified | SEC-007 repository boundary implemented; Linux/container/browser/production verification remains |
| V-192 | Exact SDK resolution; generated API/test NuGet locks; locked restore; full backend; transitive NuGet vulnerability audit | Repository root | Make .NET toolchain and transitive package resolution reproducible and fail CI on dependency drift | PASS — SDK 9.0.317 selected by `global.json`; both lock files restore in locked mode; backend 719/719; NuGet reports no known vulnerable direct or transitive package | The first test attempt observed the deliberately started local API holding the apphost, then passed after shutdown; the clean rerun passed without warnings. CI action SHAs, image digests, installer hashing, SBOM and container scanning remain JT-017 work | .NET provenance gap closed; broader build provenance remains partial |
| V-193 | Clean `npm ci`; lint; focused/full Jest; Next build/TypeScript; locked NuGet restores; full backend; stale-claim/path inspection | Repository root / `job-tracker-ui` | Rebuild current developer/operator documentation and prove its setup, quality and provider claims | PASS — install succeeds without the obsolete peer override; lint zero warnings; CV Builder 12/12 and frontend 64 suites/272 tests; optimized build/TypeScript; locked restores and backend 719/719; every documented path exists | User-local SDK 9.0.317 is shadowed by a runtime-only system host on this workstation, so backend proof invoked the installed host explicitly. No second clean machine or production rehearsal | JT-018 repository documentation complete; external operator rehearsal remains deployment evidence |
@@ -0,0 +1,30 @@
# JT-018 developer and operator documentation verification
## Implemented
- Replaced the obsolete Create React App frontend guide with the actual Next.js 16, React 19, TypeScript, Jest, ESLint and Playwright workflow.
- Corrected the root quickstart to use the committed npm and NuGet lock files (`npm ci` and `dotnet restore --locked-mode`).
- Documented the separate normal-development (`3000`) and isolated Playwright (`3300`) frontend ports.
- Removed the obsolete npm peer-dependency override and verified the current graph installs without it.
- Corrected the production provider matrix: SQLite and MariaDB/MySQL are implemented; PostgreSQL is not.
- Corrected the architecture inventory to React Router 7 and removed completed roadmap suggestions from the runtime README.
- Linked environment, architecture, deployment, audit and programme sources so historical phase notes are not mistaken for current setup instructions.
## Proof
- Clean `npm ci`: passed without the removed `.npmrc` override and without lockfile drift.
- Frontend lint: zero warnings.
- Frontend Jest: 64/64 suites, 272/272 tests.
- CV Builder autosave regression: 12/12 tests; timing-dependent manual-save assertions now verify the actual debounced autosave contract.
- Next.js production build and integrated TypeScript check: passed.
- API and test-project NuGet restore with `--locked-mode`: passed.
- Backend: 719/719 tests passed on SDK 9.0.317.
- Documentation stale-claim and referenced-path checks: passed; the only PostgreSQL mention explicitly states that it is unsupported.
## Environment note
SDK 9.0.317 is installed at the user-local `.dotnet` location. This workstation also has a runtime-only `C:\Program Files\dotnet` host earlier on `PATH`; verification therefore invoked the installed SDK host explicitly. The repository's `global.json` correctly rejects the runtime-only host instead of silently selecting a different SDK.
## Remaining external proof
A truly clean second machine and production operator rehearsal were not used. The documented install, lint, test, build and locked-restore commands were executed against a clean frontend dependency installation and the current repository checkout.
+2 -1
View File
@@ -42,6 +42,7 @@ Updated: 2026-08-30
- Localized the remaining active helper defaults in the admin SMTP test form, pasted-email importer, and image-crop alternative text.
- Restored the local Python 3.12 toolchain and both virtual environments; the AI sidecar now passes 26/26 tests. Added a Next.js-compatible ESLint 9 flat configuration, strict zero-warning scripts, and a patched CommonJS-compatible `brace-expansion` override. The lint gate passes with zero findings and npm audit reports zero vulnerabilities.
- Pinned the repository to .NET SDK 9.0.317, generated content-hashed transitive NuGet locks for the API and test project, and made CI restores fail on lock drift. Locked restore and backend 719/719 pass; the current NuGet graph has no known vulnerable packages.
- Rebuilt the active developer/operator documentation around the actual Next.js 16/.NET 9 application, replaced CRA and `npm start` guidance, separated normal and Playwright ports, corrected React Router 7 and the SQLite/MariaDB provider matrix, removed the obsolete npm peer override, and verified the documented clean install, lint, test, build and locked-restore commands.
### In progress
@@ -110,7 +111,7 @@ Updated: 2026-08-30
- Manual desktop browser review: webpack development server rendered the new Career navigation and Overview correctly in dark mode; API-dependent profile status remained unavailable because the backend was not running for that isolated UI review.
- **Overall programme status:** Active but externally blocked. Eight packages are locally verified and twenty-seven are implemented with verification incomplete. The prioritized admin-only version indicator, every immediate repository/browser item, SEC-006/SEC-007 repository boundaries, SEC-009, the PROD-001 read-only inventory, and the PROD-003 safe benchmark harness are complete on the feature branch.
- **Current work package:** JT-017/JT-018 supply-chain and developer-documentation hardening while external runtime gates remain blocked.
- **Current work package:** JT-017 remaining immutable CI/image/scanner provenance and JT-019 schema-ownership inventory while external runtime gates remain blocked. JT-018 is complete in repository scope.
- **Completed work packages:** None are `DONE`; all repository security/AI packages still have applicable browser, provider and/or production gates.
- **Locally verified work:** SEC-001, SEC-002, SEC-003, SEC-005A, CORE-001, PROD-002, DEP-001 and VER-001 (`VERIFIED LOCALLY`).
- **Implemented, verification incomplete:** SEC-004, SEC-005B, SEC-006, SEC-007, SEC-008, SEC-009, CORE-002, BG-001, OPS-001A/B/C, POL-001/002, AI-001/002/003/004, UX-001/002/003, QA-001, CAREER-001/002, MAIL-001, JOBS-001/002 and PRODUCT-001 (`IMPLEMENTED — NOT VERIFIED`). Their safe repository/browser scope is implemented; production/native-device/provider/retention gates remain where recorded.
-4
View File
@@ -1,4 +0,0 @@
# react-scripts (kept only as the Jest test runner, see package.json) still declares a
# typescript ^3.2.1||^4 peer constraint that's stale for our actual (Next.js-driven) TS 5.x --
# it doesn't type-check via that peer path, so the conflict is safe to relax.
legacy-peer-deps=true
+44 -26
View File
@@ -1,46 +1,64 @@
# Getting Started with Create React App
# Job Tracker frontend
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
The frontend is a statically exported Next.js 16 application using React 19, TypeScript, Material UI, and React Router. Next.js supplies the build shell; authenticated and public application routes are defined in `src/App.tsx`.
## Available Scripts
## Requirements
In the project directory, you can run:
- Node.js 24 (the version used by CI)
- npm 11 or a compatible npm release
- The Job Tracker API at `http://localhost:5202` for normal local use
### `npm start`
Install the lockfile exactly:
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
```powershell
npm ci
```
The page will reload if you make edits.\
You will also see any lint errors in the console.
## Development
### `npm test`
Start the application on Next.js's default development port:
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
```powershell
npm run dev
```
### `npm run build`
Open `http://localhost:3000`. The client calls `http://localhost:5202/api` on localhost unless `NEXT_PUBLIC_API_BASE_URL` overrides it.
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The Playwright harness uses port `3300` and starts its own isolated API and SQLite database. Do not point it at a development or production database.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
## Quality gates
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
```powershell
npm run lint
npm test -- --runInBand
npm run build
```
### `npm run eject`
The production build is written to `out/` and is served by nginx in the frontend container.
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
Run the browser suite after installing Chromium once:
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
```powershell
npx playwright install chromium
npm run test:e2e
```
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
The browser suite covers login, job creation, Career Workspace routing, and public CV/PDF access against isolated services.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Useful paths
## Learn More
- `app/` — thin Next.js application shell
- `src/App.tsx` — application routes and shared authenticated shell
- `src/api.ts` — API client and local/default base URL
- `src/components/` — feature and shared UI components
- `src/i18n/` — English and Norwegian Bokmål UI messages
- `src/theme.ts` — application theme
- `e2e/` — Playwright browser tests
- `next.config.js` — static-export configuration
- `playwright.config.ts` — isolated browser-test services
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
## Runtime configuration
To learn React, check out the [React documentation](https://reactjs.org/).
`NEXT_PUBLIC_API_BASE_URL` is the only browser-visible API setting. Leave it unset in the production container so nginx proxies same-origin `/api` requests. Values prefixed with `NEXT_PUBLIC_` are embedded at build time and must never contain secrets.
See the repository `README.md` for full-stack development and `deploy/README.md` for production requirements.
@@ -239,13 +239,12 @@ test('custom entries can be added, edited, reordered and deleted with confirmati
await waitFor(() => expect(screen.getAllByLabelText(/^Entry /)).toHaveLength(1));
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
expect(await screen.findByText('Saved')).toBeInTheDocument();
expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
await waitFor(() => expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
settings: expect.objectContaining({
customSections: [expect.objectContaining({ title: 'Selected projects', items: ['First project'] })],
}),
}));
})), { timeout: 5000 });
expect(await screen.findByText('Saved')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Remove Selected projects section from this CV' }));
dialog = await screen.findByRole('dialog', { name: 'Delete custom section' });
@@ -279,15 +278,13 @@ test('profile-backed sections expand, reorder, hide and persist variant-only ove
fireEvent.click(screen.getByRole('button', { name: 'Move Engineer entry down' }));
fireEvent.click(screen.getByRole('button', { name: 'Hide Lead entry' }));
fireEvent.click(screen.getByRole('button', { name: 'Move Experience section down' }));
fireEvent.click(screen.getByRole('button', { name: 'Save now' }));
expect(await screen.findByText('Saved')).toBeInTheDocument();
expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
await waitFor(() => expect(mockedApi.put).toHaveBeenLastCalledWith('/cv/variants/3', expect.objectContaining({
settings: expect.objectContaining({
sections: expect.arrayContaining([expect.objectContaining({ key: 'experience', itemOrder: ['job-2', 'job-1'], presentation: 'grid', columns: 2 })]),
overrides: expect.objectContaining({ 'job-2': expect.objectContaining({ hidden: true }) }),
}),
}));
})), { timeout: 5000 });
expect(await screen.findByText('Saved')).toBeInTheDocument();
});
test('preview failure is visible and retryable without leaving the editor', async () => {