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:
co-authored by
Claude Opus 5
parent
61cf29878f
commit
6e3d0640ff
@@ -2,6 +2,7 @@ import {
|
||||
AlertTriangle,
|
||||
CheckCircle2,
|
||||
Eye,
|
||||
Info,
|
||||
EyeOff,
|
||||
Pencil,
|
||||
Plus,
|
||||
@@ -118,17 +119,9 @@ export default function Connectors() {
|
||||
|
||||
{/*
|
||||
Kam se uklada. Bez teto hlasky se clovek divi, kam se podely jeho
|
||||
konektory po restartu, a hleda chybu v aplikaci.
|
||||
konektory, a hleda chybu v aplikaci - presne to se stalo.
|
||||
*/}
|
||||
{storage?.ephemeral && (
|
||||
<p className="flex items-start gap-2 rounded-card border border-warn-400/40 bg-warn-500/8 p-3 text-sm text-warn-400">
|
||||
<AlertTriangle className="mt-0.5 size-4 shrink-0" />
|
||||
<span>
|
||||
Přístupové údaje se ukládají jen do paměti serveru a restart je smaže.
|
||||
<span className="mt-0.5 block text-white/50">{storage.reason}</span>
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{storage && storage.mode !== 'postgres' && <StorageNotice storage={storage} />}
|
||||
|
||||
<DataState loading={loading} error={error} onRetry={load}>
|
||||
{(connectors?.length ?? 0) === 0 ? (
|
||||
@@ -165,6 +158,46 @@ export default function Connectors() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hlaska o ulozisti.
|
||||
*
|
||||
* Rozlisuje tri veci, protoze pro uzivatele znamenaji uplne jiny nasledek:
|
||||
* pamet (ztrata pri restartu), soubor (ztrata pri redeployi) a databaze
|
||||
* (bez ztraty, hlaska se nezobrazuje).
|
||||
*/
|
||||
function StorageNotice({ storage }: { storage: StorageStatus }) {
|
||||
const severe = storage.mode === 'memory';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-start gap-2 rounded-card border p-3 text-sm',
|
||||
severe ? 'border-warn-400/40 bg-warn-500/8' : 'border-ink-600/70 bg-ink-850/50',
|
||||
)}
|
||||
>
|
||||
{severe ? (
|
||||
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-warn-400" />
|
||||
) : (
|
||||
<Info className="mt-0.5 size-4 shrink-0 text-white/40" />
|
||||
)}
|
||||
<div>
|
||||
<p className={severe ? 'text-warn-400' : 'text-white/70'}>
|
||||
{severe
|
||||
? 'Přístupové údaje se ukládají jen do paměti serveru, restart je smaže.'
|
||||
: 'Data se ukládají do souboru na serveru. Restart přežijí, nové nasazení ne.'}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs text-white/45">{storage.reason}</p>
|
||||
{storage.secrets === 'file' && (
|
||||
<p className="mt-0.5 text-xs text-white/35">
|
||||
Údaje jsou zašifrované klíčem, který leží ve stejné složce. Chrání to proti
|
||||
náhodnému přečtení souboru, ne proti přístupu k disku serveru.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------- mala karta
|
||||
|
||||
function ConnectorCard({
|
||||
|
||||
@@ -292,13 +292,19 @@ export interface Connector {
|
||||
ready: boolean;
|
||||
}
|
||||
|
||||
/** Kam se uklada a jestli to prezije restart. */
|
||||
/** Kam se uklada a co to prezije. */
|
||||
export interface StorageStatus {
|
||||
mode: 'postgres' | 'memory';
|
||||
/** Proc se jede v pameti. null u databaze. */
|
||||
reason: string | null;
|
||||
mode: 'postgres' | 'file' | 'memory';
|
||||
/** Kratke vysvetleni pro uzivatele. */
|
||||
reason: string;
|
||||
/** true = restart procesu data smaze. */
|
||||
ephemeral: boolean;
|
||||
/** true = redeploy containeru data smaze, i kdyz restart ne. */
|
||||
lostOnRedeploy: boolean;
|
||||
/** Odkud je klic k sifrovani tajnych hodnot. */
|
||||
secrets: 'env' | 'file' | 'none';
|
||||
/** Kde data lezi. null u pameti. */
|
||||
location: string | null;
|
||||
}
|
||||
|
||||
export interface ConnectorTestResult {
|
||||
|
||||
Reference in New Issue
Block a user