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
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand All @@ -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) |

Expand Down Expand Up @@ -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

Expand Down
13 changes: 7 additions & 6 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```
Expand All @@ -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

Expand All @@ -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

Expand All @@ -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.

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/fleetcheck/config/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
}
}
Loading