Files
smoa/backend/README.md

121 lines
5.5 KiB
Markdown
Raw Permalink Normal View History

# SMOA Backend
Ground-up backend with REST APIs for the **Secure Mobile Operations Application (SMOA)** Android app. Provides sync endpoints for directory, orders, evidence, credentials, and reports, with optional API key auth and OpenAPI docs.
## Requirements
- **JDK 17**
- **Gradle 8.x** (wrapper included in repo root; run from `backend/` with `../gradlew` or install Gradle)
## Quick Start
```bash
cd backend
../gradlew bootRun
```
Or with explicit profile:
```bash
../gradlew bootRun --args='--spring.profiles.active=dev'
```
- **API base:** `http://localhost:8080`
- **Health:** `GET http://localhost:8080/health`
- **API info:** `GET http://localhost:8080/api/v1/info`
- **Swagger UI:** `http://localhost:8080/swagger-ui.html`
- **OpenAPI JSON:** `http://localhost:8080/v3/api-docs`
## Sync API (for the mobile app)
All sync endpoints accept JSON and return a **SyncResponse** that matches the mobile `SyncAPI` contract in `core/common/SyncAPI.kt`:
| Endpoint | Method | Request body | Response |
|----------|--------|--------------|----------|
| `/api/v1/sync/directory` | POST | `DirectorySyncRequest` | `SyncResponse` |
| `/api/v1/sync/order` | POST | `OrderSyncRequest` | `SyncResponse` |
| `/api/v1/sync/evidence` | POST | `EvidenceSyncRequest` | `SyncResponse` |
| `/api/v1/sync/credential` | POST | `CredentialSyncRequest` | `SyncResponse` |
| `/api/v1/sync/report` | POST | `ReportSyncRequest` | `SyncResponse` |
**Delete** (sync delete): `DELETE /api/v1/sync/directory/{id}`, `DELETE /api/v1/sync/order/{orderId}`, `DELETE /api/v1/sync/evidence/{evidenceId}`, `DELETE /api/v1/sync/credential/{credentialId}`, `DELETE /api/v1/sync/report/{reportId}` — each returns `SyncResponse`.
**Pull / GET** (refresh or initial load): `GET /api/v1/directory` (optional `unit`, `X-Unit`), `GET /api/v1/orders` (since, limit, jurisdiction / `X-Unit`), `GET /api/v1/evidence`, `GET /api/v1/credentials`, `GET /api/v1/reports` (since, limit, optional filters).
**SyncResponse** fields: `success`, `itemId`, `serverTimestamp`, `conflict`, `remoteData` (optional), `message` (optional). When `conflict: true`, `remoteData` is base64-encoded JSON of the server version.
Conflict detection: send `lastUpdated` (directory) or `clientUpdatedAt` (others). If the server has a newer version, the response has `conflict: true` and `remoteData` with the server payload.
## Authentication
- **Development:** No API key required when `smoa.api.key` is empty (default in `dev` profile).
- **Production:** Set `SMOA_API_KEY` (or `smoa.api.key`). Clients must send:
- Header: `X-API-Key: <key>`, or
- Query: `?api_key=<key>`
## Configuration
| Property | Default | Description |
|----------|---------|-------------|
| `server.port` | 8080 | Server port |
| `spring.datasource.url` | H2 file `./data/smoa` | DB URL (use PostgreSQL in production) |
| `smoa.api.key` | (empty) | API key; empty = no auth |
| `smoa.api.key-header` | X-API-Key | Header name for API key |
| `smoa.cors.allowed-origins` | * | CORS origins (comma-separated); * = any, no credentials |
| `smoa.rate-limit.enabled` | true | Rate limit on `/api/v1/*` (per API key or IP) |
| `smoa.rate-limit.requests-per-minute` | 120 | Max requests per minute; 429 when exceeded |
| `smoa.tenant.require-unit` | false | When true, require `X-Unit` (or `unit` query) for all `/api/v1` requests |
| `smoa.cors.allowed-origins` | * | **Production:** set to your web app origin(s) (e.g. `https://smoa.example.com`) for CORS |
**Tracing:** Each request gets an `X-Request-Id` header (or preserves incoming one); use for logs and support.
**Caching:** GET list endpoints support **ETag** and **If-None-Match**; send `If-None-Match: <etag>` to receive 304 Not Modified when unchanged.
Profiles:
- **dev** relaxed auth, H2 console at `/h2-console`, debug logging.
- **prod** set `SPRING_PROFILES_ACTIVE=prod` and `SMOA_API_KEY`, and switch datasource to PostgreSQL as needed.
## Database
- **Default:** H2 file database at `./data/smoa`. **Flyway** runs migrations from `db/migration/`; `ddl-auto: update` in dev.
- **Production:** Use PostgreSQL: set `spring.datasource.url=jdbc:postgresql://...`, `driver-class-name=org.postgresql.Driver`, and add `org.postgresql:postgresql` dependency. Use **`ddl-auto: validate`** (set in `application-prod.yml`) so Flyway owns the schema.
## Building
```bash
cd backend
../gradlew build
```
JAR:
```bash
../gradlew bootJar
# output: build/libs/smoa-backend-1.0.0.jar
java -jar build/libs/smoa-backend-1.0.0.jar
```
Docker (build from **repo root**):
```bash
docker build -f backend/Dockerfile .
docker run -p 8080:8080 <image-id>
```
Sync and delete operations are **audit logged** (resource type, id, operation, success).
## Connecting the Android app
1. Point the apps sync base URL to this backend (e.g. `http://<host>:8080`).
2. Implement a real `SyncAPI` (e.g. with Retrofit) that:
- Serializes domain models to JSON matching the backend DTOs (`DirectorySyncRequest`, `OrderSyncRequest`, etc.).
- POSTs to `/api/v1/sync/directory`, `/api/v1/sync/order`, etc.
- Parses `SyncResponse` (and handles `conflict` / `remoteData` when present).
Request DTOs align with the apps directory, order, evidence, report, and credential concepts; field names and types are chosen for easy mapping from the mobile side.
## Gap analysis and roadmap
See [docs/BACKEND-GAPS-AND-ROADMAP.md](docs/BACKEND-GAPS-AND-ROADMAP.md) for a full review: what's covered, completed gaps (delete sync, pull/GET, enum validation, rate limiting, audit, tests, Dockerfile), and optional follow-ups (prod profile, unit/tenant scoping, migrations).