Synchronizace & Zálohy — Specifikace¶
Přehled¶
Každá firma má vyhrazený prostor na S3 s limitem velikosti. Synchronizace zajišťuje konzistenci dat mezi desktopem a webem. Zálohy jsou implicitně zapnuty — aplikace používají S3 komunikaci implementovanou v tomto modulu. Klientská data (mzdové listy, doklady, účetnictví) podléhají zákonné archivační povinnosti, nikoli GDPR výmazu — ta se vztahuje pouze na identifikační data uživatelů uložená odděleně.
Úložný prostor — limity per firma¶
Výchozí limit: 1 GB per firma
Soft limit: 5 GB → kritické upozornění (zálohy stále běží)
Hard limit: dle zakoupeného balíčku
Zakoupení většího prostoru: firma si dokoupí v portálu nebo přes support.
Předpoklad: 1 GB bude pro většinu firem dostatečné.
Chování při překročení limitů¶
0–1 GB → normální provoz
1 GB → SOFT LIMIT PŘEKROČEN
• Upozornění company adminovi: e-mail + in-app notifikace
• Zálohy pokračují beze změny
• Portál zobrazí varování v detailu firmy
1–5 GB → zálohy stále běží, každý týden opakované upozornění
5 GB → KRITICKÉ PŘEKROČENÍ
• Okamžitý e-mail + in-app notifikace company adminovi
• Notifikace Avaxis (portál — detail firmy)
• Zálohy nových dat se ZASTAVÍ (sync čte, nezapisuje nová data)
• Stávající zálohy zůstanou dostupné
• Firma musí dokoupit prostor nebo smazat staré zálohy
> 5 GB → pouze po ručním navýšení limitu super adminem
Zobrazení velikosti v portálu¶
Správa portálu → detail firmy → záložka Přehled:
Úložiště S3:
████████░░░░░░░░░░░░ 780 MB / 1 000 MB (78 %)
[Dokoupit prostor]
Rozpad dle aplikace:
Fakturace Pro 420 MB (zálohy dat + přílohy)
Účetnictví 280 MB
Nastavení 80 MB
Datový model — sledování velikosti¶
company_storage (
company_id UUID REFERENCES companies(id) PRIMARY KEY,
used_bytes BIGINT DEFAULT 0,
limit_bytes BIGINT DEFAULT 1073741824, -- 1 GB
soft_limit_bytes BIGINT DEFAULT 5368709120, -- 5 GB (kritické)
last_calculated_at TIMESTAMPTZ
)
-- Denní agregace per aplikace pro graf v portálu
company_storage_history (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
date DATE,
used_bytes BIGINT,
PRIMARY KEY (company_id, app_id, date)
)
Velikost se přepočítává při každém uploadu/smazání zálohy a denně jako batch job (S3 ListObjects pro přesné číslo).
S3 struktura — firemní prostor¶
Data jsou organizována per firma, ne per uživatel — zálohy patří firmě.
s3://avaxis/companies/{company-id}/
├── users/
│ └── {user-id}/
│ ├── license.jwt ← offline licence cache
│ └── identity.json ← GDPR data (viz sekce GDPR)
│
├── settings/
│ └── {user-id}/
│ └── {app-slug}/
│ ├── current.snapshot
│ └── history/{ISO-timestamp}.snapshot
│
└── data/
└── {app-slug}/
├── manifest.json ← metadata aktuálního stavu
├── latest/ ← aktuální data (web přístup)
│ ├── db.sqlite ← nebo jiný formát
│ └── files/ ← přílohy, doklady
└── history/
└── {ISO-timestamp}/ ← point-in-time zálohy
├── db.sqlite
└── files/
GDPR a zákonná archivace — oddělení dat¶
KLÍČOVÉ ROZHODNUTÍ:
Klientská data (mzdové listy, faktury, účetnictví) NEJSOU GDPR data
ve smyslu práva na výmaz — jsou to zákonně archivovaná obchodní data.
Firmy je musí uchovávat dle zákona (5–10 let dle typu dokladu).
GDPR se vztahuje POUZE na identifikační data uživatele platformy:
• Jméno uživatele
• E-mailová adresa
• Případně telefon
Tato data jsou uložena ODDĚLENĚ v identity.json:
identity.json — izolovaná GDPR data¶
{
"user_id": "usr_abc123",
"name": "Jan Novák",
"email": "jan.novak@firma.cz",
"phone": "+420601234567",
"created_at": "2024-01-15T08:00:00Z"
}
Uložen v: companies/{company-id}/users/{user-id}/identity.json
Při GDPR anonymizaci (právo na výmaz):
→ identity.json se přepíše na:
{ "user_id": "usr_abc123",
"name": "Smazaný uživatel",
"email": "deleted_usr_abc123@avaxis.deleted",
"anonymized_at": "2026-04-21T10:00:00Z" }
→ Veškerá ostatní data (doklady, zálohy) zůstanou beze změny
(jsou to firemní, ne osobní data)
→ V aplikaci se uživatelovo jméno nahradí "Smazaný uživatel"
Zákonná archivace klientských dat¶
Klientská data podléhají zákonné archivaci — firma si nastaví dobu
uchování dle svých zákonných povinností:
Typ dat Zákonná povinnost Doporučené nastavení
──────────────────────────────────────────────────────────────
Mzdové listy 30 let (ZP §305) 30 let
Účetní doklady 5 let (ZÚ §31) 10 let (s rezervou)
Daňové doklady 10 let (DŘ §148) 10 let
Faktury 10 let 10 let
Nastavení per firma per aplikace:
company_data_retention (
company_id UUID,
app_id UUID,
retention_years INTEGER, -- zákonná povinnost firmy
updated_by UUID,
updated_at TIMESTAMPTZ
)
Expirování záloh: po uplynutí retention_years se zálohy NESMAŽOU automaticky.
Označí se jako expired=true a čekají na garbage collector (viz sekce níže).
Firma dostane upozornění 90 dní před expirací.
Backup konfigurace — implicitní záloha¶
Záloha je zapnuta implicitně pro všechny aplikace. Aplikace nemusí nic konfigurovat — používají S3 komunikaci implementovanou v tomto modulu.
Nastavení v launcheru (Nastavení → Zálohy):
☑ Automaticky zálohovat data na server (doporučeno)
Frekvence: [Při každé změně ▼]
Aplikace:
☑ Fakturace Pro Poslední záloha: dnes 14:23 (45 MB)
☑ Účetnictví Poslední záloha: dnes 09:01 (32 MB)
☐ Sklad Záloha vypnuta uživatelem
[Zálohovat nyní] [Správa záloh]
Uživatel NEMŮŽE mazat zálohy přímo — pouze žádat o výmaz přes portál/support. Fyzické mazání provádí výhradně Garbage Collector (viz sekce níže).
Manifest.json — synchronizační metadata¶
{
"app": "fakturace-pro",
"company_id": "comp_xyz",
"user_id": "usr_abc123",
"version": 47,
"last_modified": "2026-04-21T10:30:00Z",
"device_id": "desktop-win-xyz",
"files": [
{
"path": "db.sqlite",
"checksum": "sha256:a1b2c3...",
"size": 2048576
},
{
"path": "files/faktury/",
"checksum": "sha256:d4e5f6...",
"size": 10485760
}
],
"total_size": 12533152
}
Synchronizační protokol¶
Sync cyklus (desktop → S3 → web)¶
Aplikace oznámí launcheru změnu dat (app.sync.request() přes SDK):
│
▼
Sync Agent (součást launcheru):
1. Načte lokální manifest, vypočítá checksums změněných souborů
2. Stáhne manifest.json ze S3
3. Porovná:
Lokální = Remote → nic
Lokální novější → upload změněných souborů → nový manifest → snapshot
Remote novější → stáhni změněné soubory do lokálu
KONFLIKT → viz sekce Konflikt
Web aplikace čte z S3/latest/ — vždy aktuální po sync
Detekce a řešení konfliktu¶
Konflikt: obě strany změnily data od posledního sync
Řešení:
SQLite DB → SDK poskytuje merge hook (app implementuje)
pokud hook chybí → kopie s suffixem .conflict
Soubory → zachová obě verze, uživatel rozhodne v aplikaci
Nastavení → Remote vítězí (méně kritické)
Upload flow (manifest-based, stejný princip jako app-catalog)¶
Sync Agent → API: které soubory se změnily?
API → Sync Agent: presigned PUT URL pro každý změněný soubor
Sync Agent → S3: upload přímo (nezatěžuje API)
Sync Agent → API: commit (nový manifest + snapshot)
Zálohy nastavení (zdarma, všechny plány)¶
Trigger: aplikace zapíše přes app.paths.settings (SDK)
→ Launcher detekuje změnu (file watcher)
→ Komprimuje → settings.snapshot (gzip)
→ Upload: companies/{company-id}/settings/{user-id}/{app-slug}/
→ Rotace: uchováváme posledních 10 snapshotů
Zálohy klientských dat¶
Automatické zálohy¶
Frekvence:
Standard: denně ve 02:00 (lokální čas)
Premium: každých 6 hodin
Proces:
1. SDK zavolá backup hook aplikace (app.sync.request())
2. Aplikace dokončí neuložené operace
3. Launcher zkopíruje data z app.paths.data → snapshot
4. Upload do companies/{company-id}/data/{app-slug}/history/
5. Zapíše metadata do backup_snapshots
Ruční záloha: uživatel může spustit kdykoli z launcheru
Retention (dle zákonné povinnosti firmy)¶
Nastaveno per firma per aplikace v company_data_retention
Default: 10 let pro všechna data (bezpečná hodnota)
Upozornění 90 dní před automatickým smazáním
Obnovení zálohy¶
GET /backup/{app-slug}/history
Vrátí: seznam snapshotů (timestamp, velikost, typ)
POST /backup/{app-slug}/restore/{snapshot-id}
→ presigned URL pro stažení
→ launcher zastaví aplikaci, přepíše data, spustí aplikaci
→ před obnovením automaticky záloha aktuálního stavu
Šifrování¶
| Typ dat | Šifrování |
|---|---|
| Přenos | TLS 1.3 (HTTPS) |
| S3 uložení | AES-256 SSE-S3 (serverové) |
| identity.json | AES-256 SSE-KMS (vlastní klíč pro GDPR data) |
| Klientská data (Premium) | E2E: klíč odvozený z hesla firmy (PBKDF2-SHA256) |
Sync & Backup API¶
GET /sync/{app-slug}/manifest
Vrátí: aktuální manifest.json ze S3
POST /sync/{app-slug}/upload
Body: { files: [{path, checksum, size}] }
Vrátí: { presigned_urls: [{path, url, expires_at}] }
POST /sync/{app-slug}/commit
Body: { manifest: {...} }
→ zapíše manifest, vytvoří snapshot, aktualizuje company_storage
GET /sync/{app-slug}/download
Body: { files: [{path}] }
Vrátí: { presigned_urls: [{path, url}] }
GET /backup/{app-slug}/history
POST /backup/{app-slug}/restore/{snapshot-id}
POST /backup/{app-slug}/manual
→ spustí ruční zálohu okamžitě
GET /storage/usage
Vrátí: { used_bytes, limit_bytes, breakdown_by_app: [...] }
Datový model¶
backup_snapshots (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
user_id UUID REFERENCES users(id),
app_id UUID REFERENCES apps(id),
s3_key TEXT NOT NULL,
type VARCHAR(20), -- 'settings' | 'data'
trigger VARCHAR(20), -- 'auto' | 'manual' | 'pre-restore'
size_bytes BIGINT,
checksum VARCHAR(64),
is_encrypted BOOLEAN,
created_at TIMESTAMPTZ
)
sync_state (
company_id UUID REFERENCES companies(id),
user_id UUID REFERENCES users(id),
app_id UUID REFERENCES apps(id),
device_id VARCHAR(255),
manifest_version INTEGER,
last_synced_at TIMESTAMPTZ,
PRIMARY KEY (user_id, app_id, device_id)
)
company_storage (
company_id UUID REFERENCES companies(id) PRIMARY KEY,
used_bytes BIGINT DEFAULT 0,
limit_bytes BIGINT DEFAULT 1073741824, -- 1 GB
soft_limit_bytes BIGINT DEFAULT 5368709120, -- 5 GB
last_calculated_at TIMESTAMPTZ
)
company_storage_history (
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
date DATE,
used_bytes BIGINT,
PRIMARY KEY (company_id, app_id, date)
)
company_data_retention (
company_id UUID REFERENCES companies(id),
app_id UUID REFERENCES apps(id),
retention_years INTEGER DEFAULT 10,
updated_by UUID REFERENCES users(id),
updated_at TIMESTAMPTZ,
PRIMARY KEY (company_id, app_id)
)
Garbage Collector — bezpečné mazání záloh¶
Žádná záloha se nikdy nesmaže automaticky bez explicitního schválení. GC je samostatný backend job který pracuje s frontou označených souborů.
Životní cyklus zálohy¶
active → záloha platná, přístupná
expired → retention_years uplynul, označeno systémem
→ záloha stále přístupná, čeká na GC
→ firma upozorněna 90 dní před expirací
pending_delete → super admin nebo firma schválila smazání
→ GC ji zpracuje v nejbližším běhu
deleted → fyzicky smazáno ze S3, záznam v DB zachován
(s3_key = NULL, deleted_at = timestamp)
Kdo může označit zálohu ke smazání¶
Firma (company admin):
→ Může požádat o smazání expired záloh přes portál
→ Žádost jde ke schválení super adminovi Avaxis
→ Důvod: úspora místa, zákonná lhůta uplynula
Super admin (Avaxis):
→ Může schválit žádost firmy
→ Může označit zálohy ke smazání přímo (např. při ukončení smlouvy)
→ Vždy s poznámkou důvodu (audit log)
Systém (automaticky):
→ NIKDY nemaže bez schválení — pouze označí jako expired
GC job — průběh¶
Spouštění: denně ve 03:00 UTC (mimo pracovní dobu)
1. Načte všechny záznamy se stavem pending_delete
2. Pro každý záznam:
a. Ověří že záznam má platný důvod a schválení v audit logu
b. Smaže soubor ze S3 (S3 DeleteObject)
c. Aktualizuje backup_snapshots: s3_key=NULL, status='deleted', deleted_at=now()
d. Aktualizuje company_storage (odečte bytes)
e. Zapíše do audit_log: { action: 'gc_deleted', snapshot_id, size_bytes }
3. Odešle report super adminovi (kolik souborů, kolik MB uvolněno)
Pokud S3 mazání selže:
→ Záznam zůstane pending_delete, zkusí se znovu příští den
→ Po 3 neúspěšných pokusech → alert super adminovi
Datový model — rozšíření backup_snapshots¶
backup_snapshots (
id UUID PRIMARY KEY,
company_id UUID REFERENCES companies(id),
user_id UUID REFERENCES users(id),
app_id UUID REFERENCES apps(id),
s3_key TEXT, -- NULL po smazání
type VARCHAR(20),
trigger VARCHAR(20),
size_bytes BIGINT,
checksum VARCHAR(64),
is_encrypted BOOLEAN,
status VARCHAR(20) DEFAULT 'active',
-- 'active' | 'expired' | 'pending_delete' | 'deleted'
expired_at TIMESTAMPTZ, -- kdy přešlo do expired
delete_requested_by UUID REFERENCES users(id),
delete_approved_by UUID REFERENCES users(id),
delete_reason TEXT,
deleted_at TIMESTAMPTZ,
gc_attempts INTEGER DEFAULT 0,
created_at TIMESTAMPTZ
)
S3 bezpečnostní model — uživatelé nemají delete práva¶
Uživatelé NIKDY nezískají S3 přístup s právem mazat soubory. Veškerý přístup k S3 probíhá přes presigned URL generované API serverem.
Presigned URL typy které API vydává klientům:
GET (čtení) → stažení zálohy, sync download ✓
PUT (zápis) → upload zálohy, sync upload ✓
DELETE → NIKDY nevydáváme klientům ✗
IAM politika pro S3 bucket avaxis:
Klienti (presigned URL): s3:GetObject, s3:PutObject
API server: s3:GetObject, s3:PutObject, s3:ListBucket
GC job (backend only): s3:GetObject, s3:PutObject, s3:DeleteObject
Avaxis admini (konzole): plný přístup (jen interní, ne přes aplikaci)
Výsledek:
→ Uživatel nemůže smazat zálohu ani při kompromitaci svého účtu
→ Zálohy jsou chráněny i před company adminem
→ Jediný způsob mazání je přes schválený GC job
Rozhodnuté otázky¶
| Otázka | Rozhodnutí |
|---|---|
| Úložný limit | 1 GB per firma default, dokoupení dostupné |
| Soft limit | 5 GB — zálohy běží, kritické upozornění |
| Hard limit | > 5 GB — zálohy zastaveny, nutné navýšení |
| Velikost v portálu | Zobrazena v detailu firmy + rozpad per aplikace |
| Backup konfigurace | Implicitně zapnuto, nastavení v launcheru per aplikace |
| Backup API | S3 komunikace implementována v sync-backup, sdílena všemi apps |
| GDPR data | Pouze name + email → identity.json, anonymizovatelný odděleně |
| Klientská data | Nejsou GDPR — zákonná archivace (mzdy 30 let, účetnictví 10 let) |
| Retention zálohy | Per firma per aplikace, default 10 let, konfigurovatelné |
| Automatické mazání | ZAKÁZÁNO — zálohy se pouze označí jako expired |
| Garbage Collector | Backend job, denně 03:00 UTC, maže jen pending_delete |
| Schválení mazání | Firma žádá → super admin schvaluje → GC provede |
| S3 delete práva | Klienti NIKDY — pouze GC job (IAM politika) |
| Presigned URL | Pouze GET + PUT — žádné DELETE pro klienty |
| Upozornění před expirací | 90 dní předem |
| Šifrování identity.json | SSE-KMS (vlastní klíč) — odděleno od ostatních dat |
Otevřené otázky¶
Všechny otázky k sync-backup.md jsou vyřešeny.
Poslední aktualizace: 2026-04-21