Files
smoa/backend/README.md
defiQUG 5a8c26cf5d Backend, sync, infra, docs: ETag, API versioning, k8s, web scaffold, Android 16, domain stubs
- Backend: ShallowEtagHeaderFilter for /api/v1/*, API-VERSIONING.md, README (tenant, CORS, Flyway, ETag)
- k8s: backend-deployment.yaml (Deployment, Service, Secret/ConfigMap)
- Web: scaffold with directory pull, 304 handling, touch-friendly UI
- Android 16: ANDROID-16-TARGET.md; BuildConfig STUN/signaling, SMOAApplication configures InfrastructureManager
- Domain: CertificateManager revocation stub, ReportService signReports, ZeroTrust/ThreatDetection minimal docs
- TODO.md and IMPLEMENTATION_STATUS.md updated; communications README for endpoint config

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 20:37:01 -08:00

121 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).