Monorepo: Gitea CI, docs, auth/sync, backend APIs, gitignore
- Add Gitea Actions workflow; point README to gitea.d-bis.org/Sankofa_Phoenix/SMOA - Expand .gitignore for Spring H2 data, secrets, Kotlin .kotlin/, tooling - Track docs/api/generated ReDoc bundle; refresh api docs README - Android: network/auth/sync, UI shell, tests; backend credentials/integrity APIs - Docs, scripts (generate-api-docs), modules and core updates Made-with: Cursor
This commit is contained in:
+203
-280
@@ -1,298 +1,221 @@
|
||||
# SMOA Database Schema Documentation
|
||||
# SMOA database schema (server + device)
|
||||
|
||||
**Version:** 1.0
|
||||
**Last Updated:** 2024-12-20
|
||||
**Status:** Draft - In Progress
|
||||
**Version:** 2.0
|
||||
**Last updated:** 2026-03-23
|
||||
|
||||
This document aligns **SMOA backend** (Flyway `V1__baseline.sql`, H2 or PostgreSQL) with **on-device Room** entities in this repository. Older rows that described only a generic local `credentials` table **without** a matching entity are removed.
|
||||
|
||||
---
|
||||
|
||||
## Database Overview
|
||||
## 1. Server database (SMOA backend)
|
||||
|
||||
### Database Technology
|
||||
- **Database:** SQLite (via Room)
|
||||
- **Version:** SQLite 3.x
|
||||
- **Location:** Local device storage
|
||||
- **Encryption:** AES-256-GCM encryption
|
||||
Source of truth: `backend/src/main/resources/db/migration/V1__baseline.sql`.
|
||||
JPA entities: `backend/src/main/kotlin/com/smoa/backend/domain/*.kt`.
|
||||
|
||||
### Database Purpose
|
||||
SMOA uses Room database for local data storage, providing:
|
||||
- Offline data access
|
||||
- Fast local queries
|
||||
- Encrypted data storage
|
||||
- Data synchronization support
|
||||
### 1.1 `directory_entries`
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| id | VARCHAR(255) PK | Directory entry id |
|
||||
| name | VARCHAR(255) | Display name |
|
||||
| title | VARCHAR(255) | Job title |
|
||||
| unit | VARCHAR(255) | Unit / org |
|
||||
| phone_number | VARCHAR(255) | Phone |
|
||||
| extension | VARCHAR(255) | Extension |
|
||||
| email | VARCHAR(255) | Email |
|
||||
| secure_routing_id | VARCHAR(255) | Secure routing |
|
||||
| role | VARCHAR(255) | Role |
|
||||
| clearance_level | VARCHAR(255) | Clearance |
|
||||
| last_updated | BIGINT | Epoch ms |
|
||||
|
||||
### 1.2 `orders`
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| order_id | VARCHAR(255) PK | Order id |
|
||||
| order_type | VARCHAR(255) | Enum string (see backend validation) |
|
||||
| title | VARCHAR(255) | Title |
|
||||
| content | CLOB | Body |
|
||||
| issued_by | VARCHAR(255) | Issuer |
|
||||
| issued_to | VARCHAR(255) | Recipient |
|
||||
| issue_date | TIMESTAMP | Issued |
|
||||
| effective_date | TIMESTAMP | Effective |
|
||||
| expiration_date | TIMESTAMP | Expiry |
|
||||
| status | VARCHAR(255) | Status enum |
|
||||
| classification | VARCHAR(255) | Classification |
|
||||
| jurisdiction | VARCHAR(255) | Jurisdiction |
|
||||
| case_number | VARCHAR(255) | Case |
|
||||
| updated_at | BIGINT | Epoch ms |
|
||||
|
||||
### 1.3 `evidence`
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| evidence_id | VARCHAR(255) PK | Evidence id |
|
||||
| case_number | VARCHAR(255) | Case |
|
||||
| description | CLOB | Description |
|
||||
| evidence_type | VARCHAR(255) | Type enum |
|
||||
| collection_date | TIMESTAMP | Collected |
|
||||
| collection_location | VARCHAR(255) | Location |
|
||||
| collection_method | VARCHAR(255) | Method |
|
||||
| collected_by | VARCHAR(255) | Collector |
|
||||
| current_custodian | VARCHAR(255) | Custodian |
|
||||
| storage_location | VARCHAR(255) | Storage |
|
||||
| updated_at | BIGINT | Epoch ms |
|
||||
|
||||
### 1.4 `credentials` (server)
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| credential_id | VARCHAR(255) PK | Credential id |
|
||||
| holder_id | VARCHAR(255) | Subject / holder |
|
||||
| credential_type | VARCHAR(255) | Canonical type (`SmoaCredentialType` + legacy; see `docs/reference/IDENTITY-TEMPLATE-ALIGNMENT.md`) |
|
||||
| issuer | VARCHAR(255) | Issuing authority label |
|
||||
| issued_at | BIGINT | Epoch ms (optional) |
|
||||
| expires_at | BIGINT | Epoch ms (optional) |
|
||||
| payload_json | CLOB | JSON object: format-specific fields, optional `issuingAuthority`, `completeCredentialDomain`, `extensions` |
|
||||
| updated_at | BIGINT | Epoch ms |
|
||||
|
||||
**Issuance validation:** Parse `payload_json` as JSON, then validate with `docs/schemas/credential-payload-*.schema.json` for the given `credential_type` on the sync **request body** (not duplicated inside payload).
|
||||
|
||||
### 1.5 `reports`
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| report_id | VARCHAR(255) PK | Report id |
|
||||
| report_type | VARCHAR(255) | Type enum |
|
||||
| title | VARCHAR(255) | Title |
|
||||
| format | VARCHAR(255) | PDF, XML, JSON, CSV, EXCEL |
|
||||
| generated_date | BIGINT | Epoch ms |
|
||||
| generated_by | VARCHAR(255) | Generator |
|
||||
| content | BLOB | Optional binary |
|
||||
| metadata_json | CLOB | Optional JSON |
|
||||
| updated_at | BIGINT | Epoch ms |
|
||||
|
||||
### 1.6 `sync_audit_log`
|
||||
|
||||
| Column | SQL type | Description |
|
||||
|--------|-----------|-------------|
|
||||
| id | BIGINT IDENTITY PK | Surrogate key |
|
||||
| resource_type | VARCHAR(255) | Resource |
|
||||
| resource_id | VARCHAR(255) | Id |
|
||||
| operation | VARCHAR(255) | Operation |
|
||||
| success | BOOLEAN | Outcome |
|
||||
| principal | VARCHAR(255) | Optional actor |
|
||||
| timestamp | TIMESTAMP | Event time |
|
||||
|
||||
---
|
||||
|
||||
## Schema Diagrams
|
||||
## 2. On-device Room (implemented modules)
|
||||
|
||||
### Entity Relationship Diagram
|
||||
[To be added: ER diagram showing all entities and relationships]
|
||||
Room uses SQLite; module-specific databases. Below match Kotlin `@Entity` types.
|
||||
|
||||
### 2.1 Directory — `directory_entries`
|
||||
|
||||
`modules/directory/.../DirectoryEntity.kt`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|--------|
|
||||
| id | TEXT PK | |
|
||||
| name | TEXT | |
|
||||
| title | TEXT? | |
|
||||
| unit | TEXT | |
|
||||
| phoneNumber | TEXT? | camelCase in Kotlin → snake in DB per Room |
|
||||
| extension | TEXT? | |
|
||||
| email | TEXT? | |
|
||||
| secureRoutingId | TEXT? | |
|
||||
| role | TEXT? | |
|
||||
| clearanceLevel | TEXT? | |
|
||||
| lastUpdated | INTEGER | Epoch ms |
|
||||
|
||||
### 2.2 Orders — `orders`
|
||||
|
||||
`modules/orders/.../OrderEntity.kt`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|--------|
|
||||
| orderId | TEXT PK | |
|
||||
| orderType | TEXT | Enum persisted via converter |
|
||||
| title | TEXT | |
|
||||
| content | TEXT | |
|
||||
| issuedBy | TEXT | |
|
||||
| issuedTo | TEXT? | |
|
||||
| issueDate | INTEGER / Date | Type converters |
|
||||
| effectiveDate | | |
|
||||
| expirationDate | | |
|
||||
| status | | Enum |
|
||||
| classification | TEXT? | |
|
||||
| jurisdiction | TEXT | |
|
||||
| caseNumber | TEXT? | |
|
||||
| createdAt | | |
|
||||
| updatedAt | | |
|
||||
|
||||
### 2.3 Evidence — `evidence`
|
||||
|
||||
`modules/evidence/.../EvidenceEntity.kt`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|--------|
|
||||
| evidenceId | TEXT PK | |
|
||||
| caseNumber | TEXT | |
|
||||
| description | TEXT | |
|
||||
| evidenceType | | Enum |
|
||||
| collectionDate | | |
|
||||
| collectionLocation | TEXT | |
|
||||
| collectionMethod | TEXT | |
|
||||
| collectedBy | TEXT | |
|
||||
| currentCustodian | TEXT | |
|
||||
| storageLocation | TEXT? | |
|
||||
| createdAt | | |
|
||||
| updatedAt | | |
|
||||
|
||||
### 2.4 Custody — `custody_transfers`
|
||||
|
||||
`modules/evidence/.../CustodyTransferEntity.kt`
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|--------|
|
||||
| transferId | TEXT PK | |
|
||||
| evidenceId | TEXT FK → evidence | |
|
||||
| timestamp | Date | |
|
||||
| fromCustodian | TEXT | |
|
||||
| toCustodian | TEXT | |
|
||||
| reason | TEXT | |
|
||||
| evidenceCondition | TEXT | |
|
||||
| signatureData | BLOB? | |
|
||||
| notes | TEXT? | |
|
||||
|
||||
### 2.5 Credentials cache — `credential_cache`
|
||||
|
||||
`modules/credentials/.../CredentialCacheEntity.kt` (encrypted Room DB `credential_cache_database`)
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|--------|
|
||||
| credentialId | TEXT PK | |
|
||||
| holderId | TEXT | |
|
||||
| credentialType | TEXT | |
|
||||
| issuer | TEXT? | |
|
||||
| issuedAt | INTEGER? | Epoch ms |
|
||||
| expiresAt | INTEGER? | Epoch ms |
|
||||
| payloadJson | TEXT? | JSON string |
|
||||
| updatedAt | INTEGER | Epoch ms |
|
||||
|
||||
Populate from pull/sync merge logic in application code when wired; schema matches server `credentials` for offline display.
|
||||
|
||||
### 2.6 Reports (device)
|
||||
|
||||
No Room `ReportEntity` in this repo; reports remain server-backed unless a local entity is added later.
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
## 3. Indexes (server)
|
||||
|
||||
### User Table
|
||||
|
||||
#### Table: users
|
||||
- **Purpose:** Store user information
|
||||
- **Primary Key:** user_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| user_id | TEXT | PRIMARY KEY | Unique user identifier |
|
||||
| username | TEXT | NOT NULL, UNIQUE | Username |
|
||||
| email | TEXT | | Email address |
|
||||
| role | TEXT | NOT NULL | User role |
|
||||
| created_at | INTEGER | NOT NULL | Creation timestamp |
|
||||
| updated_at | INTEGER | NOT NULL | Update timestamp |
|
||||
|
||||
### Credential Table
|
||||
|
||||
#### Table: credentials
|
||||
- **Purpose:** Store digital credentials
|
||||
- **Primary Key:** credential_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| credential_id | TEXT | PRIMARY KEY | Unique credential identifier |
|
||||
| user_id | TEXT | NOT NULL, FOREIGN KEY | User who owns credential |
|
||||
| type | TEXT | NOT NULL | Credential type |
|
||||
| title | TEXT | NOT NULL | Credential title |
|
||||
| issuer | TEXT | NOT NULL | Issuing authority |
|
||||
| issue_date | INTEGER | | Issue date (Unix timestamp) |
|
||||
| expiration_date | INTEGER | | Expiration date |
|
||||
| status | TEXT | NOT NULL | Status (active, expired, revoked) |
|
||||
| barcode_data | TEXT | | PDF417 barcode data |
|
||||
| created_at | INTEGER | NOT NULL | Creation timestamp |
|
||||
| updated_at | INTEGER | NOT NULL | Update timestamp |
|
||||
|
||||
**Foreign Keys:**
|
||||
- user_id → users(user_id)
|
||||
|
||||
### Order Table
|
||||
|
||||
#### Table: orders
|
||||
- **Purpose:** Store digital orders
|
||||
- **Primary Key:** order_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| order_id | TEXT | PRIMARY KEY | Unique order identifier |
|
||||
| order_type | TEXT | NOT NULL | Order type |
|
||||
| title | TEXT | NOT NULL | Order title |
|
||||
| content | TEXT | NOT NULL | Order content |
|
||||
| issued_by | TEXT | NOT NULL | Issuing authority |
|
||||
| issued_to | TEXT | | Recipient |
|
||||
| issue_date | INTEGER | NOT NULL | Issue date |
|
||||
| effective_date | INTEGER | NOT NULL | Effective date |
|
||||
| expiration_date | INTEGER | | Expiration date |
|
||||
| status | TEXT | NOT NULL | Order status |
|
||||
| created_at | INTEGER | NOT NULL | Creation timestamp |
|
||||
| updated_at | INTEGER | NOT NULL | Update timestamp |
|
||||
|
||||
### Evidence Table
|
||||
|
||||
#### Table: evidence
|
||||
- **Purpose:** Store evidence items
|
||||
- **Primary Key:** evidence_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| evidence_id | TEXT | PRIMARY KEY | Unique evidence identifier |
|
||||
| case_number | TEXT | NOT NULL | Case number |
|
||||
| description | TEXT | NOT NULL | Evidence description |
|
||||
| type | TEXT | NOT NULL | Evidence type |
|
||||
| collection_date | INTEGER | NOT NULL | Collection date |
|
||||
| collection_location | TEXT | | Collection location |
|
||||
| collected_by | TEXT | NOT NULL | Collector |
|
||||
| current_custodian | TEXT | NOT NULL | Current custodian |
|
||||
| storage_location | TEXT | | Storage location |
|
||||
| created_at | INTEGER | NOT NULL | Creation timestamp |
|
||||
| updated_at | INTEGER | NOT NULL | Update timestamp |
|
||||
|
||||
### Custody Transfer Table
|
||||
|
||||
#### Table: custody_transfers
|
||||
- **Purpose:** Track evidence custody transfers
|
||||
- **Primary Key:** transfer_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| transfer_id | TEXT | PRIMARY KEY | Unique transfer identifier |
|
||||
| evidence_id | TEXT | NOT NULL, FOREIGN KEY | Evidence item |
|
||||
| from_custodian | TEXT | NOT NULL | Transferring custodian |
|
||||
| to_custodian | TEXT | NOT NULL | Receiving custodian |
|
||||
| transfer_date | INTEGER | NOT NULL | Transfer date |
|
||||
| reason | TEXT | | Transfer reason |
|
||||
| evidence_condition | TEXT | | Evidence condition |
|
||||
| signature | TEXT | | Digital signature |
|
||||
| created_at | INTEGER | NOT NULL | Creation timestamp |
|
||||
|
||||
**Foreign Keys:**
|
||||
- evidence_id → evidence(evidence_id)
|
||||
|
||||
### Report Table
|
||||
|
||||
#### Table: reports
|
||||
- **Purpose:** Store generated reports
|
||||
- **Primary Key:** report_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| report_id | TEXT | PRIMARY KEY | Unique report identifier |
|
||||
| template | TEXT | NOT NULL | Report template |
|
||||
| format | TEXT | NOT NULL | Report format (PDF, XML, JSON, CSV) |
|
||||
| parameters | TEXT | | Report parameters (JSON) |
|
||||
| generated_by | TEXT | NOT NULL | Generator user |
|
||||
| generated_at | INTEGER | NOT NULL | Generation timestamp |
|
||||
| file_path | TEXT | | Report file path |
|
||||
| file_size | INTEGER | | File size in bytes |
|
||||
|
||||
### Audit Log Table
|
||||
|
||||
#### Table: audit_logs
|
||||
- **Purpose:** Store audit trail records
|
||||
- **Primary Key:** log_id
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|--------|------|-------------|-------------|
|
||||
| log_id | TEXT | PRIMARY KEY | Unique log identifier |
|
||||
| event_type | TEXT | NOT NULL | Event type |
|
||||
| user_id | TEXT | | User who triggered event |
|
||||
| module | TEXT | | Module where event occurred |
|
||||
| action | TEXT | NOT NULL | Action performed |
|
||||
| resource | TEXT | | Resource affected |
|
||||
| result | TEXT | NOT NULL | Result (success, failure) |
|
||||
| details | TEXT | | Additional details (JSON) |
|
||||
| timestamp | INTEGER | NOT NULL | Event timestamp |
|
||||
| ip_address | TEXT | | IP address (if applicable) |
|
||||
- `idx_sync_audit_timestamp` on `sync_audit_log(timestamp)`
|
||||
|
||||
---
|
||||
|
||||
## Indexes
|
||||
|
||||
### Performance Indexes
|
||||
- **users(username):** Index on username for login
|
||||
- **credentials(user_id):** Index on user_id for user credential queries
|
||||
- **credentials(status):** Index on status for status queries
|
||||
- **orders(status):** Index on order status
|
||||
- **orders(order_type):** Index on order type
|
||||
- **evidence(case_number):** Index on case number
|
||||
- **audit_logs(timestamp):** Index on timestamp for time-based queries
|
||||
- **audit_logs(user_id):** Index on user_id for user audit queries
|
||||
|
||||
---
|
||||
|
||||
## Data Dictionary
|
||||
|
||||
### Data Elements
|
||||
|
||||
#### User Data Elements
|
||||
- **user_id:** Unique identifier for users
|
||||
- **username:** User login name
|
||||
- **role:** User role (administrator, operator, viewer, auditor)
|
||||
|
||||
#### Credential Data Elements
|
||||
- **credential_id:** Unique identifier for credentials
|
||||
- **type:** Credential type (id, badge, license, permit, other)
|
||||
- **status:** Credential status (active, expired, revoked)
|
||||
|
||||
#### Order Data Elements
|
||||
- **order_id:** Unique identifier for orders
|
||||
- **order_type:** Order type (authorization, assignment, search_warrant, etc.)
|
||||
- **status:** Order status (draft, pending_approval, approved, issued, etc.)
|
||||
|
||||
#### Evidence Data Elements
|
||||
- **evidence_id:** Unique identifier for evidence
|
||||
- **type:** Evidence type (physical, digital, biological, chemical, firearm, document)
|
||||
- **current_custodian:** Current custodian of evidence
|
||||
|
||||
---
|
||||
|
||||
## Migrations
|
||||
|
||||
### Migration History
|
||||
|
||||
#### Migration 1: Initial Schema
|
||||
- **Version:** 1
|
||||
- **Date:** 2024-01-01
|
||||
- **Description:** Initial database schema creation
|
||||
|
||||
#### Migration 2: Add Audit Logging
|
||||
- **Version:** 2
|
||||
- **Date:** 2024-02-01
|
||||
- **Description:** Add audit log table and indexes
|
||||
|
||||
### Migration Procedures
|
||||
|
||||
#### Applying Migrations
|
||||
1. **Backup Database:** Backup current database
|
||||
2. **Review Migration:** Review migration script
|
||||
3. **Test Migration:** Test migration in staging
|
||||
4. **Apply Migration:** Apply migration to production
|
||||
5. **Verify Migration:** Verify migration success
|
||||
|
||||
#### Rollback Procedures
|
||||
1. **Identify Migration:** Identify migration to rollback
|
||||
2. **Backup Current:** Backup current database
|
||||
3. **Restore Previous:** Restore previous database version
|
||||
4. **Verify Rollback:** Verify rollback success
|
||||
|
||||
---
|
||||
|
||||
## Data Protection
|
||||
|
||||
### Encryption
|
||||
- **At Rest:** AES-256-GCM encryption
|
||||
- **Key Storage:** Hardware-backed key storage
|
||||
- **Key Management:** Automatic key rotation
|
||||
|
||||
### Access Control
|
||||
- **Database Access:** Application-only access
|
||||
- **User Access:** Role-based data access
|
||||
- **Audit Logging:** All access logged
|
||||
|
||||
---
|
||||
|
||||
## Backup and Recovery
|
||||
|
||||
### Backup Procedures
|
||||
- **Automated Backups:** Daily automated backups
|
||||
- **Backup Location:** Encrypted backup storage
|
||||
- **Backup Retention:** 90 days
|
||||
|
||||
### Recovery Procedures
|
||||
- **Full Recovery:** Complete database restoration
|
||||
- **Partial Recovery:** Selective data restoration
|
||||
- **Point-in-Time Recovery:** Recovery to specific point
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Query Optimization
|
||||
- **Indexes:** Strategic index placement
|
||||
- **Query Tuning:** Optimized queries
|
||||
- **Caching:** Query result caching
|
||||
|
||||
### Database Maintenance
|
||||
- **Vacuum:** Regular database vacuum
|
||||
- **Analyze:** Regular statistics update
|
||||
- **Optimization:** Periodic optimization
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Architecture Documentation](../architecture/ARCHITECTURE.md)
|
||||
- [Administrator Guide](../admin/SMOA-Administrator-Guide.md)
|
||||
- [Backup and Recovery Procedures](../operations/SMOA-Backup-Recovery-Procedures.md)
|
||||
|
||||
---
|
||||
|
||||
**Document Owner:** Database Administrator
|
||||
**Last Updated:** 2024-12-20
|
||||
**Status:** Draft - In Progress
|
||||
**Next Review:** 2024-12-27
|
||||
## 4. References
|
||||
|
||||
- Backend Flyway: `backend/src/main/resources/db/migration/V1__baseline.sql`
|
||||
- Identity / payload schemas: `docs/reference/IDENTITY-TEMPLATE-ALIGNMENT.md`, `docs/schemas/`
|
||||
- Tenant model: `docs/security/TENANT-THREAT-MODEL.md`
|
||||
|
||||
Reference in New Issue
Block a user