diff --git a/README.md b/README.md index a821ad6..3387285 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ **Live demo:** [fleetcheck-1.onrender.com](https://fleetcheck-1.onrender.com) — backend API at [fleetcheck-j4y2.onrender.com](https://fleetcheck-j4y2.onrender.com) -Hosted on Render's free tier, so the first request after a period of inactivity may take 30-60 seconds to wake up. +Hosted on Render's free tier, so the first request after a period of inactivity may take 30-60 seconds to wake up. The shared identity service that handles sign-in runs on an always-on paid instance, so sign-in itself never waits on a cold start. + +**Demo access:** available on request. Access is role-based: a sign-in only sees FleetCheck once an admin has added that email on the Accounts page. A digital Driver Vehicle Inspection Report (DVIR) system built to replace a paper-and-carbon-copy process at a Cintas route-service operation. @@ -21,7 +23,7 @@ Drivers currently fill out a paper DVIR booklet at the start and end of every sh - **Mechanic repair queue** — an actionable, three-stage queue (needs repair order → ready to complete → awaiting driver review), not just a filtered list - **Fleet & vehicle history views** — a fleet-wide dispatch-status table and a full per-vehicle inspection/repair/damage timeline - **Account administration** — admin-only user management with deactivation (not hard-delete), write-only password handling -- **Test coverage** — 37 backend tests (JUnit 5 + Mockito unit tests on the workflow engine; MockMvc + Spring Security Test integration tests exercising the full DVIR lifecycle through real HTTP and real authorization rules) and 22 frontend tests (Vitest + React Testing Library) +- **Test coverage** — 36 backend tests (JUnit 5 + Mockito unit tests on the workflow engine; MockMvc + Spring Security Test integration tests exercising the full DVIR lifecycle through real HTTP and real authorization rules) and 31 frontend tests (Vitest + React Testing Library) - **CI/CD** — GitHub Actions runs the full test suite on every push and pull request; deployed on Render with a separate production Spring profile, PostgreSQL, and CORS-aware cross-origin setup ## Inspection report workflow @@ -48,7 +50,7 @@ A vehicle stays blocked from dispatch for any open `SAFETY`/`BOTH` repair until | Layer | Stack | |---|---| | Backend | Java 21, Spring Boot 3.5, Spring Data JPA, Spring Security, H2 (dev) / PostgreSQL (prod) | -| Frontend | React 18, TypeScript, Vite, Tailwind CSS v4 | +| Frontend | React 19, TypeScript, Vite, Tailwind CSS v4 | | Testing | JUnit 5, Mockito, MockMvc, Spring Security Test, Vitest, React Testing Library | | CI/CD | GitHub Actions, Render (web service + static site + managed Postgres) | @@ -136,11 +138,12 @@ Deployed on Render: a Dockerized Spring Boot web service, a React static site, a Once the bootstrap admin has signed in, use the Accounts page to add driver/mechanic/manager roles by email. Each person creates their identity-service account with that same email (the login page has a **Create an account** link). The frontend needs `VITE_IDENTITY_URL` set at build time, and identity-service must list the frontend's origin in its `CORS_ALLOWED_ORIGINS`. -**Frontend** is a static site build (`npm run build`) with one build-time variable: +**Frontend** is a static site build (`npm run build`) with two build-time variables: | Variable | Purpose | |---|---| | `VITE_API_BASE_URL` | Base URL of the deployed backend API | +| `VITE_IDENTITY_URL` | Base URL of identity-service, used for sign-in | ## Roadmap diff --git a/frontend/README.md b/frontend/README.md index 670f443..3bcd9a1 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,14 +4,14 @@ React + TypeScript SPA for [FleetCheck](../README.md) — the driver/mechanic/fl ## Tech stack -React 18, TypeScript, Vite, React Router, Tailwind CSS v4. Tests with Vitest + React Testing Library. +React 19, TypeScript, Vite, React Router, Tailwind CSS v4. Tests with Vitest + React Testing Library. ## Structure ``` src/ -├── api/ # Typed fetch client (client.ts) — adds the Basic auth header, throws ApiError on non-2xx -├── auth/ # AuthContext (session-storage credentials, current-user state), ProtectedRoute (role gate) +├── api/ # Typed fetch client (client.ts) — adds the Bearer token header, throws ApiError on non-2xx +├── auth/ # AuthContext (session-storage tokens, current-user state), tokens.ts (identity-service sign-in and refresh), ProtectedRoute (role gate) ├── components/ # Shared UI — AppShell (nav/layout), DamageMarkerEditor (click-to-place damage diagram) └── pages/ # One component per route (see Routes, below) ``` @@ -37,7 +37,7 @@ These match the backend's own role enforcement (see `SecurityConfig` in the back ## Auth -There's no token endpoint: the app sends HTTP Basic credentials (base64-encoded `username:password`) on every API request. `AuthContext` base64-encodes the credentials once at login, stores them in `sessionStorage` (cleared when the tab closes, never `localStorage`), and confirms them by calling `GET /api/me` — a failed call clears the stored credentials and surfaces "Invalid username or password." On reload, `AuthContext` re-validates whatever's in `sessionStorage` against `/api/me` before rendering any protected route, rather than trusting stale local state. +Sign-in goes through the shared identity service, not FleetCheck's backend. `tokens.ts` posts the email and password to the identity service's `/auth/login`, keeps the access and refresh tokens in `sessionStorage` (cleared when the tab closes, never `localStorage`), and API requests carry the access token as a Bearer header. When the access token expires the app refreshes it; if that fails, the session is cleared and the user is sent back to the login page. `AuthContext` confirms who is signed in by calling `GET /api/me`, which also returns the user's FleetCheck role (the identity service holds no roles). On reload, it re-validates whatever tokens are in `sessionStorage` before rendering any protected route, rather than trusting stale local state. The identity service runs on a paid instance that stays awake, so sign-in should be quick. ## Getting started @@ -48,7 +48,7 @@ npm install npm run dev ``` -Runs on `http://localhost:5173`. Vite's dev server proxies `/api/*` to `http://localhost:8080` (see `vite.config.ts`), so no `.env` is needed for local development — just have the backend running. +Runs on `http://localhost:5173`. Vite's dev server proxies `/api/*` to `http://localhost:8080` (see `vite.config.ts`), so no `.env` is needed for local development — just have the backend running, plus identity-service on `http://localhost:8081` for sign-in. ## Testing @@ -65,11 +65,12 @@ Vitest + React Testing Library, `jsdom` environment. Covers the API client's err npm run build ``` -Type-checks (`tsc -b`) then builds a static bundle to `dist/`. The only runtime configuration is where the API lives: +Type-checks (`tsc -b`) then builds a static bundle to `dist/`. Two build-time variables set where the API and the identity service live: | Variable | Purpose | |---|---| | `VITE_API_BASE_URL` | Base URL of the backend API. Falls back to `/api` (the dev proxy target) if unset. | +| `VITE_IDENTITY_URL` | Base URL of the identity service used for sign-in. Falls back to `http://localhost:8081` if unset. | In production this is deployed as a Render static site pointed at the FleetCheck backend — see the [root README](../README.md#deployment) for the full env var reference. diff --git a/src/main/java/com/fleetcheck/config/OpenApiConfig.java b/src/main/java/com/fleetcheck/config/OpenApiConfig.java index f7ffd21..05bacd5 100644 --- a/src/main/java/com/fleetcheck/config/OpenApiConfig.java +++ b/src/main/java/com/fleetcheck/config/OpenApiConfig.java @@ -12,13 +12,14 @@ public class OpenApiConfig { @Bean public OpenAPI fleetCheckOpenAPI() { - String schemeName = "basicAuth"; + String schemeName = "bearerAuth"; return new OpenAPI() .addSecurityItem(new SecurityRequirement().addList(schemeName)) .components(new Components().addSecuritySchemes(schemeName, new SecurityScheme() .name(schemeName) .type(SecurityScheme.Type.HTTP) - .scheme("basic"))); + .scheme("bearer") + .bearerFormat("JWT"))); } }