LocalAuthority / .doc / database.md
Newer Older
277 lines | 9.331kb
Bogdan Timofte authored 4 days ago
1
# SQLite Database
2

            
3
Madagascar Local Authority folosește SQLite ca store runtime pentru registry și Work Orders.
4

            
5
Locația implicită în checkout:
6

            
7
```text
8
var/host-manager.sqlite
9
```
10

            
11
Locația runtime pe jumper:
12

            
13
```text
14
/usr/local/xdev-host-manager/var/host-manager.sqlite
15
```
16

            
17
Path-ul poate fi schimbat cu:
18

            
19
```text
20
HOST_MANAGER_DB=/path/to/host-manager.sqlite
21
```
22

            
23
## Rol
24

            
25
SQLite este sursa de adevăr runtime. Fișierele din `config/` nu mai sunt store-ul live:
26

            
27
- `config/hosts.yaml` seed-uiește documentul `hosts_yaml` dacă baza este nouă.
28
- `config/work-orders.yaml` seed-uiește documentul `work_orders_yaml` dacă baza este nouă.
29
- `config/local-hosts.tsv` este manifest generat explicit din registry-ul SQLite.
30
- `/download/hosts.yaml`, `/download/local-hosts.tsv` și `/download/monitoring.json` sunt randate din SQLite.
31

            
32
Aplicația nu face commit/push automat după modificări. Exportul și arhivarea rămân pași operaționali separați.
33

            
34
## Schema curentă
35

            
36
Schema este creată automat de `scripts/host_manager.pl` la prima citire/scriere a store-ului.
37

            
38
```sql
39
CREATE TABLE IF NOT EXISTS documents (
40
    name TEXT PRIMARY KEY,
41
    content TEXT NOT NULL,
42
    updated_at TEXT NOT NULL
43
);
44
```
45

            
Bogdan Timofte authored 4 days ago
46
### Catalog
47

            
48
| Obiect | Tip | Rol |
49
|--------|-----|-----|
50
| `documents` | table | Document-store runtime pentru registry și Work Orders |
51
| `sqlite_autoindex_documents_1` | index intern SQLite | Indexul automat creat pentru `PRIMARY KEY (name)` |
52

            
53
Nu există alte tabele, view-uri, trigger-e sau indexuri definite de aplicație în schema curentă.
54

            
55
### Tabel: `documents`
56

            
57
| Coloană | Tip declarat | Null | Default | Cheie | Descriere |
58
|---------|--------------|------|---------|-------|-----------|
59
| `name` | `TEXT` | `NOT NULL` implicit prin PK | none | `PRIMARY KEY` | Identificatorul documentului operațional. Valorile cunoscute sunt `hosts_yaml` și `work_orders_yaml`. |
60
| `content` | `TEXT` | `NOT NULL` | none | none | Payload-ul documentului, serializat ca YAML strict compatibil cu parserul aplicației. |
61
| `updated_at` | `TEXT` | `NOT NULL` | none | none | Timestamp ISO UTC setat de aplicație la insert/update. |
62

            
63
### Chei
64

            
65
| Tabel | Cheie | Coloane | Observații |
66
|-------|------|---------|------------|
67
| `documents` | Primary key | `name` | Garantează un singur rând pentru fiecare document operațional. |
68

            
69
SQLite implementează cheia primară textuală prin indexul intern `sqlite_autoindex_documents_1`.
70

            
71
### Indexuri
72

            
73
| Index | Tabel | Coloane | Unic | Creat de | Rol |
74
|-------|-------|---------|------|----------|-----|
75
| `sqlite_autoindex_documents_1` | `documents` | `name` | da | SQLite | Lookup rapid pentru `SELECT ... WHERE name = ?` și enforcement pentru primary key. |
76

            
77
Nu există indexuri explicite definite de aplicație. Nu există index pe `updated_at`, pentru că aplicația nu face query-uri de tip listare istorică sau filtrare temporală.
78

            
79
### Relații
80

            
81
Nu există foreign keys între tabele, pentru că schema curentă are o singură tabelă.
82

            
83
Relațiile de business sunt în interiorul YAML-ului din `content`, nu modelate relational. De exemplu:
84

            
85
- hosturile, numele, rolurile și sursele sunt structuri YAML în documentul `hosts_yaml`
86
- checklist-ul și acțiunile unui Work Order sunt structuri YAML în documentul `work_orders_yaml`
87
- acțiunile Work Order referă `host_id` textual în YAML, nu printr-un `FOREIGN KEY`
88

            
89
`PRAGMA foreign_keys = ON` este activat ca pregătire pentru o schemă viitoare cu tabele relaționale, dar în schema curentă nu are efect practic.
90

            
91
### Constrângeri
92

            
93
| Tabel | Constrângere | Efect |
94
|-------|--------------|-------|
95
| `documents` | `PRIMARY KEY (name)` | Nu permite două documente cu același `name`. |
96
| `documents` | `content TEXT NOT NULL` | Nu permite documente fără payload. |
97
| `documents` | `updated_at TEXT NOT NULL` | Nu permite rânduri fără timestamp de modificare. |
98

            
99
Nu există `CHECK` constraints pentru valorile permise în `name`, formatul YAML sau formatul timestamp-ului. Aceste validări sunt făcute în codul Perl prin parserul strict YAML și prin fluxurile API.
100

            
101
### Operațiuni SQL folosite de aplicație
102

            
103
Citire document:
104

            
105
```sql
106
SELECT content FROM documents WHERE name = ?;
107
```
108

            
109
Insert/update document:
110

            
111
```sql
112
INSERT INTO documents (name, content, updated_at)
113
VALUES (?, ?, ?)
114
ON CONFLICT(name) DO UPDATE
115
SET content = excluded.content,
116
    updated_at = excluded.updated_at;
117
```
118

            
119
Creare schemă:
120

            
121
```sql
122
CREATE TABLE IF NOT EXISTS documents (
123
    name TEXT PRIMARY KEY,
124
    content TEXT NOT NULL,
125
    updated_at TEXT NOT NULL
126
);
127
```
128

            
Bogdan Timofte authored 4 days ago
129
Setări aplicate la conectare:
130

            
131
```sql
132
PRAGMA journal_mode = WAL;
133
PRAGMA foreign_keys = ON;
134
```
135

            
Bogdan Timofte authored 4 days ago
136
### Versiune schemă
137

            
138
Nu există încă tabelă `schema_migrations` sau `schema_version`. Schema este implicit versiunea 1 și este definită de blocul `CREATE TABLE IF NOT EXISTS documents` din `scripts/host_manager.pl`.
139

            
Bogdan Timofte authored 4 days ago
140
În schema curentă există o singură tabelă generică, `documents`. Conținutul operațional rămâne serializat în formatul YAML strict deja folosit de aplicație. Alegerea este deliberată pentru migrarea minimă: sursa de adevăr se mută din working tree în SQLite fără să schimbăm încă parserul, UI-ul sau formatul exporturilor.
141

            
142
## Documente
143

            
144
### `hosts_yaml`
145

            
146
Conține registry-ul de hosturi în formatul `config/hosts.yaml`.
147

            
148
Este citit de:
149

            
150
- `GET /api/hosts`
151
- `GET /download/hosts.yaml`
152
- `GET /download/local-hosts.tsv`
153
- `GET /download/monitoring.json`
154
- `POST /api/render/local-hosts-tsv`
155

            
156
Este scris de:
157

            
158
- `POST /api/hosts/upsert`
159
- `POST /api/hosts/delete`
160
- `POST /api/work-orders/confirm`, când o acțiune modifică registry-ul
161

            
162
Aplicația normalizează la salvare:
163

            
164
```yaml
165
policy:
166
  storage_authority: "sqlite"
167
  runtime_database: "<HOST_MANAGER_DB>"
168
```
169

            
170
### `work_orders_yaml`
171

            
172
Conține Work Orders în formatul `config/work-orders.yaml`.
173

            
174
Este citit de:
175

            
176
- `GET /api/work-orders`
177
- `POST /api/work-orders/checklist`
178
- `POST /api/work-orders/confirm`
179

            
180
Este scris de:
181

            
182
- `POST /api/work-orders/checklist`
183
- `POST /api/work-orders/confirm`
184

            
185
## Seed inițial
186

            
187
La prima accesare a unui document:
188

            
189
1. Aplicația caută rândul în `documents`.
190
2. Dacă rândul există, conținutul din SQLite câștigă.
191
3. Dacă rândul lipsește, aplicația citește fișierul seed din `config/`.
192
4. Dacă fișierul seed lipsește, aplicația creează un document gol valid.
193
5. Documentul este inserat în SQLite și de atunci SQLite devine autoritatea.
194

            
195
Important: după seed, modificările ulterioare ale fișierelor `config/hosts.yaml` sau `config/work-orders.yaml` nu schimbă automat baza existentă.
196

            
197
## Fișiere SQLite
198

            
199
Cu WAL activ, runtime-ul poate avea trei fișiere:
200

            
201
```text
202
var/host-manager.sqlite
203
var/host-manager.sqlite-wal
204
var/host-manager.sqlite-shm
205
```
206

            
207
Toate trei aparțin store-ului runtime. Nu se comit în git și nu se înlocuiesc prin deploy de cod.
208

            
209
## Inspecție
210

            
211
Pe jumper:
212

            
213
```bash
214
cd /usr/local/xdev-host-manager
215
sqlite3 var/host-manager.sqlite '.schema'
Bogdan Timofte authored 4 days ago
216
sqlite3 var/host-manager.sqlite '.tables'
217
sqlite3 var/host-manager.sqlite 'pragma table_info(documents);'
218
sqlite3 var/host-manager.sqlite 'pragma index_list(documents);'
219
sqlite3 var/host-manager.sqlite 'pragma index_info(sqlite_autoindex_documents_1);'
220
sqlite3 var/host-manager.sqlite 'pragma foreign_key_list(documents);'
Bogdan Timofte authored 4 days ago
221
sqlite3 var/host-manager.sqlite 'select name, updated_at, length(content) from documents order by name;'
222
sqlite3 var/host-manager.sqlite 'select content from documents where name = "hosts_yaml";'
223
sqlite3 var/host-manager.sqlite 'select content from documents where name = "work_orders_yaml";'
224
```
225

            
226
Pentru export YAML curent:
227

            
228
```bash
229
curl -fsS -b cookies.txt https://hosts.madagascar.xdev.ro/download/hosts.yaml
230
```
231

            
232
Endpoint-urile de download sunt OTP-protected; exemplul presupune o sesiune validă.
233

            
234
## Backup
235

            
236
Backup recomandat, cu serviciul oprit sau prin API-ul SQLite:
237

            
238
```bash
239
cd /usr/local/xdev-host-manager
240
sqlite3 var/host-manager.sqlite ".backup 'backups/host-manager/host-manager.sqlite.$(date +%Y%m%d_%H%M%S).bak'"
241
```
242

            
243
Pentru o copie brută, include și fișierele WAL/SHM sau oprește serviciul înainte:
244

            
245
```bash
246
sudo systemctl stop host-manager
247
sudo cp -a var/host-manager.sqlite* backups/host-manager/
248
sudo systemctl start host-manager
249
```
250

            
251
## Restore
252

            
253
Restore-ul înlocuiește sursa de adevăr runtime și trebuie făcut explicit:
254

            
255
```bash
256
sudo systemctl stop host-manager
257
sudo cp backups/host-manager/host-manager.sqlite.YYYYMMDD_HHMMSS.bak var/host-manager.sqlite
258
sudo chown host-manager:host-manager var/host-manager.sqlite
259
sudo systemctl start host-manager
260
curl -fsS http://127.0.0.1:8088/healthz >/dev/null
261
```
262

            
263
Dacă se restaurează o copie brută, tratează `host-manager.sqlite`, `host-manager.sqlite-wal` și `host-manager.sqlite-shm` ca set coerent.
264

            
265
## Evoluție probabilă
266

            
267
Schema generică `documents` este potrivită pentru tranziția curentă. Dacă aplicația are nevoie de query-uri structurale, validări SQL sau merge/audit mai granular, următorul pas ar trebui să fie o migrare versionată către tabele dedicate, de exemplu:
268

            
269
- `hosts`
270
- `host_names`
271
- `host_roles`
272
- `host_sources`
273
- `work_orders`
274
- `work_order_checklist`
275
- `work_order_actions`
276

            
277
Până atunci, contractul stabil este numele documentelor din `documents` și formatele YAML randate de aplicație.