Soubor jako uloziste, kdyz neni databaze

Mockup se k databazi nedostane, takze pribyl treti rezim: JSON soubor. Prezije
restart procesu i containeru, ale ne redeploy - filesystem containeru je
docasny. Je to mezistupen, ne nahrada databaze, a tak je to i napsane v portalu.

| Rezim    | Kdy                               | Restart | Redeploy |
| -------- | --------------------------------- | ------- | -------- |
| postgres | DATABASE_URL i SECRETS_KEY        | prezije | prezije  |
| file     | neni DB, ale je DATA_DIR          | prezije | ne       |
| memory   | ani jedno, nebo nejde zapsat      | ne      | ne       |

Rozhodnuti zustava na jednom miste (src/data/connectorStore.ts).

Pridano:
- src/data/snapshot.ts: atomicky zapis (.tmp a prejmenovani), slucovani zapisu
  a dokonceni rozepsaneho zapisu pri SIGTERM. Bez atomickeho zapisu by pad
  uprostred nechal polovicni JSON, ktery se pri startu nenacte. Rozbity soubor
  se prejmenuje na .broken a jede se dal - aplikace, ktera nenastartuje, je pro
  AppFactory nefunkcni sluzba
- src/data/connectors/local.ts: jeden kod pro pamet i soubor, lisi se jen tim,
  kam se zapisuje. Nahrazuje memory.ts, dve implementace by se casem rozesly
- klic k sifrovani se mimo databazi vygeneruje do DATA_DIR/secrets.key s pravy
  0600, takze sifrovani funguje bez nastaveni. Chrani to proti nahodnemu
  precteni JSONu, ne proti pristupu k disku - klic lezi vedle dat a je to tak
  napsane i v portalu. U databaze se negeneruje vubec: kdo ma zalohu tabulky,
  ma i klic ze stejneho stroje
- DATA_DIR v konfiguraci, data/ v .gitignore a .dockerignore
- hlaska v portalu rozlisuje tri nasledky: pamet, soubor a databaze

Overeno bez databaze: konektor s vyplnenymi udaji prezil restart, v JSONu jsou
hodnoty sifrovane a plaintext v nem neni. Pote s databazi: rezim postgres
funguje dal a klic vedle dat se nevygeneroval. Kontejner i data/ po overeni
smazany.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-08-12 15:18:05 +02:00
co-authored by Claude Opus 5
parent 61cf29878f
commit 6e3d0640ff
12 changed files with 334 additions and 273 deletions
+2 -2
View File
@@ -36,8 +36,8 @@ React aplikaci ze slozky `dist/public`.
| Sprava clenstvi z portalu | chybi | memberships jdou zmenit jen v kodu |
| Bugs a wishes | chybi | vyvojarska agenda, samostatna evidence vedle ticketu |
| Beh automatizaci | chybi | ulozeny strom se nevykonava, neni runtime |
| Databaze pro konektory | hotovo | Postgres, udaje sifrovane. Bez DATABASE_URL jede pamet |
| Databaze pro zbytek | chybi | automatizace, rozlozeni a tickety jsou v pameti |
| Uloziste konektoru | hotovo | Postgres, nebo JSON soubor. Udaje vzdy sifrovane |
| Uloziste pro zbytek | chybi | automatizace, rozlozeni a tickety jsou v pameti |
| Odesilani e-mailu z formulare | chybi | poptavka se zatim jen loguje |
## Znama omezeni
+64 -18
View File
@@ -1,30 +1,39 @@
# 14 - Databaze
Naprogramovano a overeno proti Postgresu 16. Zatim se do databaze ukladaji
**konektory**, tedy pristupove udaje k sluzbam. Zbytek je v pameti procesu,
poradi dalsich kroku je na konci.
Naprogramovano a overeno. Zatim se ukladaji **konektory**, tedy pristupove udaje
k sluzbam. Zbytek je v pameti procesu, poradi dalsich kroku je na konci.
## Databaze je volitelna, ale rezimy jsou oddelene
Ukladat jde tremi zpusoby a rezim se vybira sam podle toho, co je k dispozici.
Aplikace jede ve dvou rezimech a rozdil se resi **na jednom miste**,
v `src/data/connectorStore.ts`. Nikde jinde se nezjistuje, jestli databaze je -
kdyby se to rozlezlo po kodu, jedno misto by se zapomnelo.
## Tri rezimy, jedno rozhrani
| Rezim | Kdy | Prezije restart |
| ---------- | --------------------------------------- | --------------- |
| `postgres` | je `DATABASE_URL` **i** `SECRETS_KEY` | ano |
| `memory` | jinak | ne |
Rozdil se resi **na jednom miste**, v `src/data/connectorStore.ts`. Nikde jinde
se nezjistuje, ktery rezim jede - kdyby se to rozlezlo po kodu, jedno misto by
se zapomnelo a chovalo by se pak jinak nez zbytek.
| Rezim | Kdy | Prezije restart | Prezije redeploy |
| ---------- | -------------------------------------- | --------------- | ---------------- |
| `postgres` | je `DATABASE_URL` i `SECRETS_KEY` | ano | ano |
| `file` | neni databaze, ale je `DATA_DIR` | ano | **ne** |
| `memory` | ani jedno, nebo nejde zapsat | ne | ne |
Rezim `file` je pro mockup. Filesystem containeru je docasny, takze soubor
prezije restart procesu i containeru, ale nove nasazeni ho smaze. Je to
mezistupen, ne nahrada databaze.
Nasazeny mockup tedy jede v rezimu `file` a v portalu je to napsane. Lokalne
s Postgresem jede `postgres`.
Chybejici databaze **nesmi shodit start**. Container, ktery nenastartuje, je pro
AppFactory nefunkcni sluzba (AGENTS.md). Misto toho se do logu napise, proc se
jede v pameti, a portal to ukaze na strance Konektory.
AppFactory nefunkcni sluzba (AGENTS.md). Misto toho se do logu napise, ktery
rezim jede a proc, a portal to ukaze na strance Konektory.
Databaze potrebuje **oboji**. Bez klice by se pristupove udaje ukladaly
v plaintextu, a to je horsi nez ztratit je pri restartu - tabulku vidi kazda
zaloha a kazdy dump pri ladeni.
Stejne tak: kdyz jsou migrace nastavene, ale selzou, jede se dal v pameti.
Psat do rozbiteho schematu je horsi nez neukladat.
Stejne tak: kdyz jsou migrace nastavene, ale selzou, jede se dal bez databaze.
Psat do rozbiteho schematu je horsi nez psat do souboru.
## Promenne
@@ -34,6 +43,7 @@ Psat do rozbiteho schematu je horsi nez neukladat.
| `SECRETS_KEY` | klic pro sifrovani pristupovych udaju, **secret** |
| `DATABASE_POOL_MAX` | kolik spojeni si vezme jedna instance, vychozi 10 |
| `DATABASE_SSL` | `true` u spravovanych databazi, ktere vyzaduji TLS |
| `DATA_DIR` | slozka pro JSON mimo databazi, vychozi `./data`. Prazdna hodnota vypne i soubor |
`SECRETS_KEY` ma byt nahodny retezec, ne heslo:
@@ -62,7 +72,40 @@ Migrace se pousti samy pri startu. Kontrola, ze to jede z databaze:
curl -s http://localhost:3000/health/ready
```
Bez promennych `npm run dev` funguje dal, jen v pameti.
Bez promennych `npm run dev` funguje dal, jen se uklada do `./data`.
## Soubor misto databaze
`src/data/snapshot.ts` a `src/data/connectors/local.ts`.
Ukladani je **jeden kod pro pamet i soubor**, lisi se jen tim, kam se zapisuje.
Kdyby to byly dve implementace, jedna by se casem opravila a druha ne.
| Vlastnost | Jak a proc |
| ---------------- | ----------------------------------------------------------- |
| Atomicky zapis | nejdriv `.tmp`, pak prejmenovani. Pad uprostred zapisu jinak nechá polovicni JSON, ktery se pri startu nenacte |
| Slucovani zapisu | deset uprav za sebou znamena jeden zapis na disk |
| Zapis pri ukonceni | `SIGTERM` dokonci rozepsany zapis, jinak by se posledni zmena ztratila |
| Rozbity soubor | prejmenuje se na `.broken`, zaloguje a jede se s prazdnymi daty. Aplikace, ktera nenastartuje, je pro AppFactory nefunkcni sluzba |
| Sifrovani | tajne hodnoty jsou v souboru zasifrovane, plaintext nikdy |
### Klic mimo databazi
Bez `SECRETS_KEY` si aplikace v rezimu `file` vygeneruje klic do
`DATA_DIR/secrets.key` (prava 0600). Diky tomu funguje sifrovani bez jakehokoliv
nastaveni.
**Rekneme si nahlas, co to je a co ne.** Klic lezi ve stejne slozce jako data,
takze to chrani proti nahodnemu precteni JSONu, **ne** proti nekomu, kdo ma
pristup k disku serveru nebo k zaloze slozky. Do provozu patri klic ze secretu,
tedy `SECRETS_KEY`.
U databaze se klic vedle dat **negeneruje vubec**. Nemelo by to smysl: kdo ma
zalohu tabulky, ma i klic ze stejneho stroje. Proto je `allowKeyFile` parametr,
ne automatika.
Kdyz nejde ani jedno, tajne hodnoty se do souboru neukladaji a zbytek konektoru
ano. Radsi je zadat znovu nez je mit v souboru citelne.
## Migrace
@@ -169,13 +212,16 @@ Proti Postgresu 16 v kontejneru:
| `PATCH` bez tajneho pole tajne pole nesmaze | ano |
| Prepnuti vychoziho konektoru | ano |
| Smazani vychoziho preda priznak zbylemu | ano |
| Bez `DATABASE_URL` jede pametovy rezim a rekne to | ano |
| Bez `DATABASE_URL` jede souborovy rezim a rekne to | ano |
| Konektor v souborovem rezimu prezije restart procesu | ano |
| Tajne hodnoty jsou v JSONu sifrovane, plaintext nikde | ano |
| U databaze se klic vedle dat nevygeneruje | ano |
## Co chybi
| Chybi | Poznamka |
| ---------------------------- | ----------------------------------------------------- |
| Automatizace v databazi | dalsi na rade, je to to, co si clovek nastavi |
| Automatizace v ulozisti | dalsi na rade, je to to, co si clovek nastavi. Pujde do souboru i do databaze |
| Rozlozeni dashboardu | male a samostatne, hned po automatizacich |
| Tickety a incidenty | naposled, dnes je generuje simulace |
| Uzivatele, firmy, resitele | v prototypu je to spis konfigurace nez data |
+26
View File
@@ -2,6 +2,32 @@
Nejnovejsi nahore.
## 2026-08-12 - soubor jako uloziste bez databaze
Mockup se k databazi nedostane, takze pribyl treti rezim: JSON soubor.
Prezije restart procesu i containeru, ale ne redeploy.
### Pridano
- `src/data/snapshot.ts`: atomicky zapis (`.tmp` a prejmenovani), slucovani
zapisu a dokonceni rozepsaneho zapisu pri `SIGTERM`. Rozbity soubor se
prejmenuje na `.broken`, zaloguje a jede se s prazdnymi daty - aplikace, ktera
nenastartuje, je pro AppFactory nefunkcni sluzba.
- `src/data/connectors/local.ts`: jeden kod pro pamet i soubor, lisi se jen tim,
kam se zapisuje. Nahrazuje `memory.ts` - dve implementace by se casem rozesly.
- Klic k sifrovani se mimo databazi vygeneruje do `DATA_DIR/secrets.key`, takze
sifrovani funguje bez nastaveni. U databaze se negeneruje: kdo ma zalohu
tabulky, ma i klic ze stejneho stroje.
- `DATA_DIR` v konfiguraci, `data/` v `.gitignore` a `.dockerignore`.
- Hlaska v portalu rozlisuje tri nasledky: pamet (ztrata pri restartu), soubor
(ztrata pri redeployi) a databaze (bez ztraty, hlaska se nezobrazuje).
### Overeno
Bez databaze: konektor s vyplnenymi udaji prezil restart, v JSONu jsou hodnoty
sifrovane a plaintext v nem neni. S databazi: rezim `postgres` funguje dal
a klic vedle dat se nevygeneroval.
## 2026-08-12 - databaze pro konektory
Konektory se ukladaji do Postgresu, pristupove udaje sifrovane.