Přeskočit obsah

Implementační plán — referenční dokument pro Claude

Tento soubor je hustý přehled co, kde a v jakém pořadí implementovat. Načti ho místo všech spec souborů když potřebuješ kontext pro implementaci. Detaily vždy v příslušném docs/spec/*.md.


Repozitářová struktura

avax-platform/
├── CLAUDE.md                    ← vždy načíst jako první
├── docs/spec/                   ← specifikace (načíst jen relevantní)
├── backend/                     ← FastAPI (Python 3.12)
│   ├── app/
│   │   ├── main.py
│   │   ├── core/               (config, db, redis, s3, security)
│   │   ├── models/             (SQLAlchemy ORM modely)
│   │   ├── schemas/            (Pydantic schémata)
│   │   ├── routers/            (auth, org, catalog, sync, chat, support, admin)
│   │   ├── services/           (business logika)
│   │   ├── workers/            (Celery tasky)
│   │   └── migrations/         (Alembic)
│   ├── tests/
│   └── pyproject.toml
├── frontend/                    ← Next.js 15 (web + admin portál)
├── desktop/
│   └── launcher/                ← egui 0.31 launcher (Rust, nativní GPU)
├── sdk/
│   ├── python/                  ← avaxis-sdk-python
│   ├── dotnet/                  ← avaxis-sdk-dotnet
│   └── java/                    ← avaxis-sdk-java
├── ai-service/                  ← FastAPI mikroslužba na vm-ai
└── infrastructure/
    ├── nginx/                   ← nginx.conf per subdoména
    ├── docker/                  ← docker-compose.dev.yml
    └── scripts/                 ← VM setup skripty

Databázové tabulky — pořadí vytvoření (migrace)

Migrace 001 — základ:
  companies, users, audit_log, gdpr_consents

Migrace 002 — auth:
  company_roles, user_company_roles, avaxis_user_roles
  tfa_qr_sessions, tfa_backup_codes, contact_tokens, contact_requests, contacts

Migrace 003 — katalog:
  apps, app_versions, app_version_files
  company_subscriptions, subscription_history, company_channel_assignments
  app_installs

Migrace 004 — zálohy a storage:
  backup_snapshots, sync_state, company_storage, company_storage_history
  company_data_retention, company_trial_config

Migrace 005 — chat:
  conversations, conversation_members, messages
  message_reads, message_reactions, message_attachments

Migrace 006 — support:
  support_tickets, support_agents, ticket_notes, ticket_transfers
  kb_articles (s pgvector column: embedding vector(768))

Migrace 007 — portál:
  company_registrations, platform_settings, portal_notifications
  gdpr_requests

Migrace 008 — login history (partitioned):
  login_history PARTITION BY RANGE (created_at)

API endpointy — přehled per modul

/auth

POST /auth/login                → { access_token, refresh_token, offline_jwt }
POST /auth/refresh              → { access_token, refresh_token }
POST /auth/logout
POST /auth/register/company     → vytvoří firmu + company_admin
POST /auth/register/user        → přidá uživatele do existující firmy (dle IČO)
POST /auth/password-reset/request
POST /auth/password-reset/confirm
GET  /auth/license              → offline JWT pro aktuální device
POST /auth/2fa/setup            → { qr_uri, backup_codes }
POST /auth/2fa/verify           → ověří code před aktivací
POST /auth/2fa/disable          → pouze support_agent nebo super_admin

/org

GET  /org/companies/{id}
PUT  /org/companies/{id}
GET  /org/companies/{id}/users
POST /org/companies/{id}/users
PUT  /org/users/{id}
DELETE /org/users/{id}          → soft delete (anonymizace)
GET  /org/roles
POST /org/roles
POST /org/roles/{id}/assign
POST /org/apps/{app_id}/assign  → přidělení aplikace roli nebo uživateli
DELETE /org/apps/{app_id}/assign/{target_id}

/catalog

GET  /catalog/apps              → launcher endpoint (verze + size_mb per app)  ← PŘIDÁNO F2
GET  /catalog                   ?company_id, ?user_id
GET  /catalog/{slug}
GET  /catalog/{slug}/versions
GET  /catalog/{slug}/check-update  ?installed_version, ?platform
POST /apps/{slug}/file-urls     → presigned GET URLs pro změněné soubory
POST /apps/{slug}/install-report
POST /apps/{slug}/log-upload-url

/sync + /backup

GET  /sync/{slug}/manifest
POST /sync/{slug}/upload        → presigned PUT URLs
POST /sync/{slug}/commit
GET  /sync/{slug}/download
GET  /backup/{slug}/history
POST /backup/{slug}/restore/{snapshot_id}
POST /backup/{slug}/manual
GET  /storage/usage

/chat

GET  /conversations
GET  /conversations/{id}/messages    ?before, ?limit=50
POST /conversations
POST /conversations/{id}/invite
POST /conversations/{id}/leave
DELETE /conversations/{id}/members/{user_id}
POST /conversations/{id}/messages
PUT  /conversations/{id}/messages/{msg_id}
DELETE /conversations/{id}/messages/{msg_id}
POST /conversations/{id}/messages/{msg_id}/reactions  { emoji }
PUT  /conversations/{id}/mode   { mode: open|moderated|questions|announcement }
PUT  /conversations/{id}/messages/{msg_id}/hide  { hidden: bool }
PUT  /conversations/{id}/messages/{msg_id}/approve
POST /conversations/{id}/export
POST /chat/upload-url           → presigned PUT pro přílohu
PUT  /conversations/{id}/members/me/notifications  { pref }
POST /contacts/token
POST /contacts/connect          { token }
PUT  /contacts/requests/{id}    { action: accept|reject }

/support

GET  /support/queue
GET  /support/tickets           ?status, ?agent_id
GET  /support/tickets/{id}
POST /support/tickets/{id}/assign
POST /support/tickets/{id}/priority
POST /support/tickets/{id}/status   { status, message_to_customer }
POST /support/tickets/{id}/transfer { to_agent_id, note }
POST /support/tickets/{id}/notes    { content }
GET  /support/tickets/{id}/ai-draft
POST /support/tickets/{id}/csat     { score: 1-5, comment }
PUT  /support/agents/me/availability { is_available }
GET  /support/stats
GET  /kb/articles               ?q, ?category, ?app
POST /kb/articles
PUT  /kb/articles/{id}
POST /kb/articles/{id}/helpful  { helpful: bool }

/admin (portal)

GET  /admin/registrations       ?status=pending
PUT  /admin/registrations/{id}  { action: approve|reject, note }
GET  /admin/companies           ?q, ?status, ?plan
GET  /admin/companies/{id}
PUT  /admin/companies/{id}/plan
POST /admin/companies/{id}/trial  { days }
GET  /admin/companies/{id}/storage
GET  /admin/users               ?q, ?company_id, ?status
POST /admin/users/{id}/impersonate
POST /admin/users/{id}/reset-password
PUT  /admin/users/{id}/2fa-disable
GET  /admin/apps
POST /admin/apps
POST /admin/apps/{slug}/versions
PUT  /admin/apps/{slug}/versions/{ver}/promote  { to_channel }
PUT  /admin/apps/{slug}/rollback
GET  /admin/apps/{slug}/logs
GET  /admin/settings
PUT  /admin/settings            { key, value }
GET  /admin/stats

/webhooks

POST /webhooks/stripe           → invoice.paid, payment_failed, subscription.deleted

WebSocket protokol

# Připojení
wss://ws.avaxis.cz?token={access_token}

# Klient → server
{ "type": "subscribe", "conversations": ["conv_id1", "conv_id2"] }
{ "type": "unsubscribe", "conversations": ["conv_id1"] }
{ "type": "typing", "conv_id": "...", "is_typing": true }

# Server → klient
{ "type": "message",  "data": { ...message } }
{ "type": "reaction", "data": { message_id, emoji, action, count } }
{ "type": "typing",   "data": { conv_id, user_id, is_typing } }
{ "type": "member",   "data": { conv_id, action, user_id } }
{ "type": "hidden",   "data": { conv_id, message_id } }   moderace
{ "type": "approved", "data": { conv_id, message_id } }   pre-moderace
{ "type": "update_available", "data": { app, version, changelog } }
{ "type": "support_online", "data": { is_online } }

Celery fronty a tasky

# Fronta: default
send_email(template, to, context)
send_push_notification(user_id, title, body)
calculate_company_storage(company_id)
embed_kb_article(article_id)

# Fronta: ai
generate_support_draft(ticket_id)
generate_offline_response(ticket_id)
embed_resolved_ticket(ticket_id)

# Fronta: gc
mark_expired_backups()          # označí backup_snapshots expired
process_gc_queue()              # physical delete pending_delete ze S3
archive_old_chat_messages()     # starší než retention → expired
cleanup_app_logs()              # starší než retention → smazat ze S3

# Celery Beat schedule (crontab):
"mark_expired_backups":    crontab(hour=1, minute=0)
"process_gc_queue":        crontab(hour=3, minute=0)
"calculate_storage_all":   crontab(hour=4, minute=0)
"check_expiring_subs":     crontab(minute="*/60")
"premium_backup":          crontab(minute=0)       # každou hodinu
"standard_backup":         crontab(hour=2, minute=0)
"weekly_storage_alert":    crontab(day_of_week=1, hour=9)

AI Service (vm-ai:9000)

POST /ai/support-draft
  Vstup:  { ticket_id, customer_message, priority }
  Logika: embed → pgvector top-5 → if priority<=2 or conf<0.7: Claude else: Ollama GPU
  Výstup: { draft, model_used, confidence, sources: [{type, id, title}] }

POST /ai/offline-response
  Vstup:  { ticket_id, customer_message }
  Logika: embed → pgvector → Ollama GPU (rychlost) → Claude fallback
  Výstup: { response, model_used }

POST /ai/embed
  Vstup:  { text }
  Výstup: { embedding: [float * 768] }  ← Ollama nomic-embed-text

POST /ai/search
  Vstup:  { query, limit, document_type }
  Výstup: { results: [{ id, type, title, score }] }

AVAX App SDK — povinné rozhraní

# Minimální implementace (Python příklad)
from avaxis_sdk import AvaxApp

app = AvaxApp(slug="moje-aplikace")
app.connect()          # připojí Named pipe / Unix socket k launcheru
app.ready()            # launcher ví že app startovala

app.paths.data         # %APPDATA%/Avaxis/apps/{slug}/data/
app.paths.settings     # %APPDATA%/Avaxis/apps/{slug}/settings/

app.log.error(msg, exception=None, context={})
app.log.warn(msg)
app.log.info(msg)

app.sync.request()     # požádá launcher o okamžitý sync

@app.on_shutdown
def handle_shutdown():
    # uložit stav
    app.shutdown_ok()

@app.on_update_pending
def handle_update(version):
    app.update_ok()    # nebo app.update_defer()
IPC zprávy launcher → app:
  { "type": "shutdown", "reason": "user_request"|"update_ready" }
  { "type": "update_pending", "version": "2.1.4" }
  { "type": "sync_complete", "status": "ok" }

IPC zprávy app → launcher:
  { "type": "ready", "version": "2.1.3", "pid": 1234 }
  { "type": "heartbeat" }
  { "type": "shutdown_ok" }
  { "type": "update_ok" }
  { "type": "update_defer" }
  { "type": "sync_request" }
  { "type": "log", "level": "ERROR", "message": "...", "stack": "..." }

Manifest soubory (S3)

App version manifest

{
  "app": "fakturace-pro",
  "version": "2.1.4",
  "channel": "stable",
  "released_at": "ISO timestamp",
  "changelog": "Markdown text nebo null",
  "files": [
    { "path": "app.exe", "checksum": "sha256:...", "size": 5242880, "platform": "win" },
    { "path": "assets/logo.png", "checksum": "sha256:...", "size": 51200, "platform": "any" }
  ]
}

Sync manifest (S3: companies/{id}/data/{slug}/manifest.json)

{
  "app": "fakturace-pro",
  "company_id": "uuid",
  "user_id": "uuid",
  "version": 47,
  "last_modified": "ISO timestamp",
  "device_id": "string",
  "files": [
    { "path": "db.sqlite", "checksum": "sha256:...", "size": 2048576 }
  ]
}

Klíčové konstanty a konfigurace

# JWT
ACCESS_TOKEN_TTL  = 15 * 60        # 15 minut
REFRESH_TOKEN_TTL = 30 * 24 * 3600 # 30 dní
OFFLINE_JWT_TTL   = 30 * 24 * 3600 # 30 dní
OFFLINE_GRACE     = 7 * 24 * 3600  # 7 dní grace po expiraci

# Storage
COMPANY_STORAGE_DEFAULT = 1 * 1024**3   # 1 GB
COMPANY_STORAGE_SOFT    = 5 * 1024**3   # 5 GB (kritické upozornění)

# Update notifikace
UPDATE_TRAY_INTERVAL = 4 * 3600  # každé 4 hodiny

# Zálohy
SETTINGS_SNAPSHOT_COUNT = 10     # posledních 10 snapshotů nastavení
DEFAULT_TRIAL_DAYS  = 7
DEFAULT_GRACE_DAYS  = 7
DEFAULT_RETENTION_YEARS = 10

# Chat
LARGE_GROUP_THRESHOLD = 20       # >= 20 členů → seen_count místo per-user
CHAT_ATTACHMENT_MAX   = 50 * 1024**2  # 50 MB

# Support SLA (minuty)
SLA_FIRST_RESPONSE = { 1: 15, 2: 60, 3: 240, 4: 1440 }

# AI
AI_CONFIDENCE_THRESHOLD = 0.7    # pod tímto → Claude místo Ollama
AI_LOCAL_HOST  = "http://10.0.1.6:11434"   # vm-gpu Ollama
AI_SERVICE_HOST = "http://10.0.1.5:9000"   # vm-ai

F3 Desktop Launcher — implementační přehled

Stack

Jazyk:     Rust (stable)
GUI:       egui 0.31 + eframe (nativní GPU renderer, bez WebView)
Renderer:  wgpu (DirectX 12 / Vulkan / Metal)
Async:     tokio (runtime v separátním vláknu)
HTTP:      reqwest (rustls-tls)
Keyring:   keyring (bezpečné uložení refresh_token)
Build:     cargo-xwin (cross-compile Windows exe na Linux)

Soubory

desktop/launcher/src/
├── main.rs        — NativeOptions, perzistence nastavení (eframe storage), build_icon
├── app.rs         — AvaxApp struct, draw(), všechny draw_* metody
├── theme.rs       — AppTheme enum, Palette, FontSizes, AppSettings, apply()
├── types.rs       — Screen enum, Session, AppInfo, AppEvent, InstallState
├── api.rs         — api::get(), api::post() přes reqwest
└── installer.rs   — install(), launch(), uninstall(), installed_version()

Screen enum (všechny obrazovky)

Login, Novinky, Blog,
Knihovna, Obchod,
Chat,
Podpora, Gdpr, Smlouvy, OSystemu, OKlientu,
Aktualizace, Nastaveni,
MujProfil, DetailUctu, Predvolby, MojePredplatne

Horní menu lišta (MENUBAR_H = 46px)

[🔵 AVAX ▾] [Novinky] [Software ▾] [Chat 🔴N] [Nápověda ▾]  |  [Jméno ▾] [_ □ ✕]
- Všechna tlačítka stejný flat styl (nav_btn) — konzistentní bez ohledu na dropdown - Dropdown = popup_below_widget s ui.memory_mut(|m| m.toggle_popup(id)) - Window drag: Sense::click_and_drag() + ViewportCommand::StartDrag

Témata (AppTheme)

Téma Pozadí Akcent
DarkBlue #0d131f #3d8bff
Dark #111111 #888888
Green #0a140a #2ecc71
Light #d8d8e8 #2050cc
Black #000000 #505050
Pink #1f0a14 #ff3d9a

Každé téma vrací Palette struct — barvy použity v painter calls přes self.palette.*. Egui widgets získávají barvy z apply(ctx, theme, font_sizes)ctx.set_style(style).

FontSizes (persistováno)

menubar: f32   // horní lišta
dropdown: f32  // dropdown položky (= TextStyle::Button)
subnav: f32    // lišta 2 (sub-navigace)
body: f32      // tělo textu
heading: f32   // nadpisy stránek
small: f32     // metadata, popisy

Persistence (eframe storage)

// main.rs — ukládání
fn save(&mut self, storage: &mut dyn eframe::Storage) {
    eframe::set_value(storage, "avax_settings", self.inner.get_settings());
}
// Načtení při startu
let settings = cc.storage
    .and_then(|s| eframe::get_value(s, "avax_settings"))
    .unwrap_or_default();
Uloženo do: %APPDATA%\avax launcher\data\ (Windows)

Login flow

POST /auth/login  → { access_token, refresh_token, offline_jwt }
GET  /auth/me     → { email, name, company_name, system_role }
GET  /catalog/apps (po login) → Vec<AppInfo>

WDAC poznámka (Windows Application Control)

Exe zkompilovaný bez code-signing je blokován WDAC na firemních strojích. Spouštět přes Claude Code terminál (!) nebo zakoupit OV certifikát před produkcí.


Fáze implementace — závislosti

F0 Příprava infrastruktury
  └─► F1 Backend základ (auth, org)
        ├─► F2 Katalog + distribuce
        │     └─► F3 Desktop launcher
        ├─► F4 Chat systém
        │     └─► F5 Support + AI  (závisí na F4 pro support kanál)
        ├─► F6 Správa portálu      (závisí na F1+F2)
        └─► F7 Web + nasazení      (závisí na F1–F6)

F3 a F4 lze vyvíjet paralelně po dokončení F1+F2. F5 a F6 lze vyvíjet paralelně po dokončení F4.


Env proměnné (přehled)

# Databáze
DATABASE_URL=postgresql+asyncpg://avaxis:pass@10.0.1.3/avaxis_prod
REDIS_URL=redis://10.0.1.3:6379/0

# S3
S3_ENDPOINT=https://s3.avaxis.cz
S3_BUCKET=avaxis
S3_API_ACCESS_KEY=...      # GET + PUT + List
S3_API_SECRET_KEY=...
S3_GC_ACCESS_KEY=...       # + DELETE (jen pro GC worker)
S3_GC_SECRET_KEY=...

# JWT (RS256)
JWT_PRIVATE_KEY_PATH=/etc/avaxis/jwt_private.pem
JWT_PUBLIC_KEY_PATH=/app/jwt_public.pem  # bundlován v launcheru

# AI
ANTHROPIC_API_KEY=...
AI_SERVICE_URL=http://10.0.1.5:9000
OLLAMA_URL=http://10.0.1.6:11434

# E-mail
SMTP_HOST=mail.avaxis.cz
SMTP_PORT=587
SMTP_USER=noreply@avaxis.cz
SMTP_PASSWORD=...

# Seed (pouze při prvním nasazení)
AVAXIS_COMPANY_ICO=...
SUPERADMIN_EMAIL=...
SUPERADMIN_PASSWORD=...

Načti tento soubor na začátku implementační session místo všech spec souborů. Pro detaily konkrétní komponenty pak načti příslušný docs/spec/.md.*