Commit Graph

67 Commits

Author SHA1 Message Date
alexei.dolgolyov feec97fe9e fix: resolve NPM access list name on page load instead of showing ID 2026-04-05 12:56:47 +03:00
alexei.dolgolyov 7550fe9e32 feat: CPU/RAM limits per stage, NPM access list (global + per-project)
Resource limits:
- Add cpu_limit (cores) and memory_limit (MB) fields to Stage model
- Pass limits to Docker container via NanoCPUs and Memory in HostConfig
- Add CPU/Memory fields to stage creation form in project detail
- 0 = unlimited (default)

NPM access list:
- Add npm_access_list_id to Settings (global default) and Project (per-project override)
- Per-project overrides global when > 0
- NPM provider passes access_list_id when configuring proxy hosts
- Add GET /api/settings/npm-access-lists endpoint to list NPM access lists
- Add access list picker on NPM settings page (global)
- Add access list ID field on project edit form (per-project)
- DB migrations for all new columns
2026-04-05 12:44:26 +03:00
alexei.dolgolyov 12d78bec99 fix: instance link includes domain, project delete cleans up containers and proxies
- InstanceCard appends settings domain to subdomain link (stage-dev-app.example.com instead of just stage-dev-app)
- Project deletion now removes Docker containers and proxy routes before deleting DB records
- Pass domain from settings to InstanceCard via project detail page
2026-04-05 02:38:32 +03:00
alexei.dolgolyov 0993b3a54e fix: NPM remote toggle uses onchange handler instead of to prevent spurious saves 2026-04-05 02:33:28 +03:00
alexei.dolgolyov b54481aff8 fix: NPM remote toggle auto-save, proxy resync on remote change, webhook URL as path
- Remote NPM toggle now auto-saves immediately when toggled
- Toggling npm_remote triggers proxy resync (re-creates routes with server_ip or container name)
- Webhook URL shows just the path (/api/webhook/{secret}) instead of full URL with wrong domain
- Fix tag dropdown: resolve registry ID from name before fetching tags
- Remove unused fmt import
2026-04-05 02:27:41 +03:00
alexei.dolgolyov 195ef3e7e5 feat: NPM remote mode for cross-machine deployments
- Add npm_remote setting: when enabled, proxy forwards to server_ip with
  published host ports instead of Docker container names
- Deployer looks up assigned host port via InspectContainerPort in remote mode
- Auto-remove stale containers with same name before creating new ones
- Add Remote NPM toggle with warning on NPM settings page
- DB migration + schema for npm_remote column
2026-04-05 02:18:06 +03:00
alexei.dolgolyov f71f2275a2 fix: per-event delete button, Docker network default, polling interval duration parsing
- Add per-event delete button (trash icon on hover) in event log entries
- Set Docker network default to 'docker-watcher' in DB schema + migration for existing DBs
- Parse Go duration strings (5m, 1h) to seconds in settings UI, convert back on save
- Clear error when network is empty in deployer instead of hidden fallback
2026-04-05 02:02:03 +03:00
alexei.dolgolyov c26c41e6a1 feat: enable proxy toggle on quick deploy, event log clearing, and UX fixes
- Add enable_proxy toggle to Quick Deploy form (defaults to on)
- Add DELETE /api/events/log/{id} and DELETE /api/events/log endpoints
- Add Clear All button with confirmation on Events page
- Rename "NPM Proxy" to "Enable Proxy" on stage form (provider-agnostic)
- Fix polling interval validation (min 60s) and number input trim errors
- Fix domain field no longer required in settings
2026-04-05 01:50:19 +03:00
alexei.dolgolyov 187e302f4a feat: proxy routes page, OIDC login fix, NPM test connection, webhook URL fix, and UX improvements
- Add /proxies page showing deploy-managed proxy routes with project/stage links, search, and status
- Add GET /api/proxies endpoint joining instances with project/stage names
- Add POST /api/settings/npm/test endpoint for NPM connection validation
- Add GET /api/auth/mode public endpoint for auth mode detection
- Add NPM Test Connection button with validation on save
- Fix OIDC SSO button only shown when auth_mode is oidc
- Fix webhook URL showing empty when domain not set (fallback to request host)
- Fix quick deploy double-tag (image:latest:latest) by splitting tag from image URL
- Fix trim() errors on number inputs in deploy and settings forms
- Fix NPM client auto-append /api to base URL
- Sanitize NPM test error messages (no raw HTML)
- Remove healthcheck field from Quick Deploy form
- Fix env vars placeholder newline
- Make domain field optional in settings
- Set polling interval minimum to 60s
- Add Proxies and Events to sidebar navigation
- Fix SSL cert name flash on NPM settings page
- Fix empty state icon on proxies page
2026-04-05 01:27:54 +03:00
alexei.dolgolyov 1aa9c3f0e9 feat: separate NPM and Traefik settings tabs, add Events to sidebar nav
- Create /settings/npm page with NPM credentials + SSL certificate picker
- Create /settings/traefik page with entrypoint, cert resolver, network, API URL + labels reference
- Dynamic settings nav: NPM/Traefik tabs only visible when respective provider is selected
- Remove inline Traefik config from general settings page
- Remove old credentials page (replaced by NPM tab)
- Add Events page to sidebar navigation
- Fix SystemHealthCard after standalone proxy removal
2026-04-04 23:27:00 +03:00
alexei.dolgolyov 308547a3d7 refactor: remove standalone proxies, add Traefik provider with Docker labels
Standalone proxy removal:
- Delete store, API handlers, proxy manager, health monitor, validator, hints
- Delete frontend pages (proxies list, create, edit) and components (ProxyCard, ProxyForm, ProxyFilter, ProxyGroup, ValidationChecklist)
- Remove proxy routes from router, nav items, dashboard references
- Clean up SystemHealthCard to remove proxy section

Traefik provider:
- Add TraefikProvider implementing proxy.Provider via Docker labels
- ContainerLabels() returns traefik.enable, router rule, entrypoints, service port, TLS cert resolver, docker network
- ConfigureRoute() returns router name (labels handle routing at container creation)
- DeleteRoute() is no-op (container removal auto-deregisters)
- Ping() checks Traefik API health (optional)
- Wire ContainerLabels into deployer (executeDeploy + blueGreenDeploy)
- Add Traefik settings: entrypoint, cert_resolver, network, api_url
- Add traefik option to proxy provider selector in settings UI
- Show conditional Traefik config fields
- Add i18n keys (EN + RU)
2026-04-04 22:54:31 +03:00
alexei.dolgolyov 216bd7e2db fix: UI/UX consistency overhaul — fix 8 bugs, standardize design system
Bug fixes:
- Backup refresh no longer re-renders entire page (separate refreshing state)
- SSL cert button no longer flickers when no certs available
- Volume mode selector rewritten to use proper scope system (7 scopes)
- Navigation flicker eliminated when returning from env/volumes pages
- Logout button moved to sidebar footer near theme/locale controls
- Subdomain pattern now shows variable hint tooltip ({project}, {stage}, etc.)
- SSL certificate selector moved to Credentials page with auto-save
- Projects page now has search/filter by name, image, or registry

Consistency improvements:
- New Breadcrumb component replaces 5 inline implementations
- New IconArrowLeft, IconChevronDown components replace inline SVGs
- All inline spinners replaced with IconLoader component
- 10 semantic badge classes with dark mode variants in tokens.css
- Global disabled button cursor-not-allowed rule
- Raw inputs in auth page replaced with FormField components
- Missing aria-labels added to icon-only buttons
- Error panels standardized to use design tokens
2026-04-04 21:34:36 +03:00
alexei.dolgolyov 27ec23921d fix: restore Docker health indicator and fix empty expand when hints unavailable 2026-04-04 20:51:11 +03:00
alexei.dolgolyov 97d2980e95 feat: proxy provider UI, webhook URL fix, and dev-server key persistence
- Add proxy provider selector (None/NPM) to settings page with radio cards
- Show warning when switching to None about existing NPM routes
- Conditionally hide SSL certificate picker and NPM credentials when provider is None
- Fix webhook URL regenerate not updating UI (field name mismatch: url vs webhook_url)
- Fix dev-server.sh to persist ENCRYPTION_KEY across restarts via data/.dev-key
- Fix selected radio card background visibility in dark mode
2026-04-04 20:33:08 +03:00
alexei.dolgolyov 6667abf03c fix: quick deploy duplicate detection, logout UX, backup toggle, CSP, SSE guard, and migration
- Detect existing projects with same image on quick deploy; show conflict dialog with options
- Move logout button to sidebar header as icon-only
- Replace backup checkbox with ToggleSwitch component
- Allow unsafe-inline in CSP script-src for SvelteKit hydration
- Guard SSE connection behind isAuthenticated() check
- Add notification_url ALTER TABLE migration for existing databases
- Restore RegisterPersistentLogger on event bus
2026-04-04 14:40:59 +03:00
alexei.dolgolyov 3743e7fe45 fix: refactor auth settings to use api.ts, fix type alignment, OIDC token exchange
- Add auth management functions to api.ts (getAuthSettings, listUsers, etc.)
- Refactor auth settings page to use centralized api.ts instead of raw fetch (FUNC-H2)
- Add loading skeleton to auth settings page (UX-M16)
- Add exchangeOidcToken() for httpOnly cookie OIDC flow (SEC-H3)
- Fix Settings TypeScript type: has_npm_password boolean (FUNC-L)
- Add last_alive_at to Instance type (FUNC-L)
2026-04-04 14:07:26 +03:00
alexei.dolgolyov 04c1411f5d fix: extract hardcoded English strings to i18n system with Russian translations
- Extract ~40 hardcoded strings from project detail, deploy, settings, credentials, registries, auth, env editor pages
- Add corresponding Russian translations
- Replace native confirm() default labels with i18n keys in ConfirmDialog
- Fix InstanceCard pluralization to use i18n
2026-04-04 13:03:05 +03:00
alexei.dolgolyov 3f6858513f fix: frontend UX improvements (SSE status, responsive tables, dark mode, login toggle, theme)
- Add SSE connection status banner showing when real-time updates are lost (UX-H8, UX-M1)
- Add password visibility toggle on login page (UX-H10)
- Add dark mode variants to stat card backgrounds (UX-M11)
- Add overflow-x-auto to tables for mobile responsiveness (UX-H9)
- Add flex-wrap to stage header for mobile overflow (UX-H11)
- Fix theme store system preference listener reactivity (UX-M12)
- Parallelize registry health checks (UX-L4)
2026-04-04 12:53:39 +03:00
alexei.dolgolyov c6693a2ef5 Merge branch 'worktree-agent-a71dc2ea'
# Conflicts:
#	internal/api/settings.go
#	web/src/routes/projects/[id]/volumes/+page.svelte
#	web/src/routes/settings/auth/+page.svelte
2026-04-04 12:35:31 +03:00
alexei.dolgolyov f6f758c4e7 fix: ConfirmDialog accessibility and standardize destructive action confirmations
- Add focus trap, Escape key handling, ARIA attributes to ConfirmDialog
- Replace native confirm() with ConfirmDialog for stage, registry, user deletion
- Add confirmation dialogs for env variable and volume deletion
2026-04-04 12:34:08 +03:00
alexei.dolgolyov a9c7775bb7 feat: configuration backup management with manual and auto backup
Add backup/restore functionality for the SQLite database. Users can
trigger manual backups, configure automatic backups on an interval
with retention policies, list/download/delete backups, and restore
from any backup.

- Backup engine using VACUUM INTO (safe with WAL mode)
- Backup metadata tracked in DB, files stored in DATA_DIR/backups/
- Settings: backup_enabled, backup_interval_hours, backup_retention_count
- API: POST/GET/DELETE /api/backups, download, restore endpoints
- Autobackup via cron scheduler with configurable interval
- Retention: prune on startup, after each backup (manual and auto)
- Orphan cleanup: removes backup files without metadata on startup
- Restore: replaces DB and triggers graceful server shutdown
- Settings UI: /settings/backup with toggle, interval, retention config
- Backup list with download, delete, restore actions
- i18n: English and Russian translations
2026-04-02 15:32:15 +03:00
alexei.dolgolyov 6bb4781158 fix: address final review findings
- CRITICAL: Add binaries and .svelte-kit/ to .gitignore, remove from tracking
- HIGH: Return error from computeExpectedFQDNs to prevent mass DNS
  deletion on transient DB errors during sync
- MEDIUM: Log error in rollback DNS cleanup when GetSettings fails
2026-04-02 15:04:53 +03:00
alexei.dolgolyov 670948f113 fix: address code review findings for DNS management
- CRITICAL: Change DNS zones endpoint from GET to POST to avoid
  leaking API token in URL query parameters
- HIGH: Add sync.RWMutex to protect dnsProvider field in Server,
  Deployer, and proxy Manager against concurrent read/write races
- HIGH: Capture old DNS provider reference synchronously before
  launching background cleanup goroutine
- HIGH: Use getDNS()/getDNSProviderLocked() accessors instead of
  direct field reads in all DNS operations
2026-04-02 14:54:15 +03:00
alexei.dolgolyov c730cfaa45 feat: Cloudflare DNS management with automatic record sync
Add flexible DNS management to Docker Watcher. By default, wildcard DNS
is assumed (current behavior). When disabled, users can configure a
Cloudflare DNS provider with API token and zone selection. DNS A records
are automatically created/updated/deleted in sync with proxy consumers
(deployed instances and standalone proxies).

- Settings: wildcard_dns toggle, dns_provider, cloudflare credentials
- Cloudflare client: Provider interface with EnsureRecord/DeleteRecord/ListRecords
- DNS lifecycle hooks in deployer and proxy manager (best-effort)
- Settings UI: DNS config section with provider picker, zone selector, test button
- DNS Records page at /dns with filtering, sync status, reconciliation
- Records visible in both wildcard and managed modes
- Cleanup on provider change: removes old records when switching modes
2026-04-02 14:49:21 +03:00
alexei.dolgolyov 582e7e39e3 feat(volume-browser): absolute scope with allowlist security
- Add 'absolute' volume scope for direct host paths (NFS, external mounts)
- Allowlist in settings: allowed_volume_paths (JSON array of prefixes)
- Validation: absolute source must be under an allowed prefix
- Empty allowlist = absolute scope disabled entirely
- Settings API exposes/validates allowed_volume_paths
- Frontend type updated with absolute scope
2026-04-01 23:31:27 +03:00
alexei.dolgolyov 0491849f0f fix(volume-browser): address security review findings
Critical fixes:
- IDOR: verify volume belongs to project before resolving path
- Upload: override global 1MB body limit for upload endpoint (100MB)

High-priority fixes:
- Symlink escape: use filepath.EvalSymlinks in safePath validation
- Remove host filesystem path from browse API response
- Sanitize Content-Disposition filenames, force application/octet-stream
- Strip directory components from upload filenames
2026-04-01 23:17:35 +03:00
alexei.dolgolyov aacdd255a9 feat(volume-browser): phase 3 - editor integration & polish
- Browse and Download buttons on each volume row in the editor table
- Download entire volume as ZIP directly from the editor (no browser needed)
- File type icons for common extensions in browser
- Ephemeral volumes excluded from browse/download actions
2026-04-01 23:06:19 +03:00
alexei.dolgolyov 6b54a72ec9 feat(volume-browser): phase 2 - file browser UI
- Browse route: /projects/{id}/volumes/{volId}/browse
- Directory listing with file icons, sizes, dates
- Breadcrumb navigation, click-to-navigate directories
- Download entire volume or folder as ZIP
- Upload files via file picker
- i18n EN/RU for all browser strings
2026-04-01 23:04:30 +03:00
alexei.dolgolyov 8fb959f81f feat: volume scopes redesign — replace shared/isolated with 6 scopes
Replace confusing shared/isolated volume modes with explicit scopes:
- instance: per-deploy isolated directory
- stage: shared within a stage across deploys
- project: shared across all stages
- project_named: named group within a project
- named: global named volume across projects
- ephemeral: tmpfs in-memory mount

Includes schema migration (shared→project, isolated→instance),
backward-compatible deployer resolution, scope metadata API endpoint,
and redesigned volume editor UI with scope guide cards and hints.
2026-03-31 23:22:43 +03:00
alexei.dolgolyov 1a8dfefa77 feat: Docker diagnostic hints on disconnection
- Classify Docker errors into categories (socket_not_found, connection_refused,
  permission_denied, timeout, tls_error) with platform-specific hints
- Enrich GET /api/health with structured diagnostics (category, hints, platform)
- Expandable hints panel in sidebar when Docker is disconnected
- "Retry now" button for immediate re-check
- Collapsible raw error details for advanced users
2026-03-30 14:05:00 +03:00
alexei.dolgolyov 4041252028 fix: Docker health indicator shows immediately after login
Use $effect instead of onMount to start SSE and health polling,
so they activate on client-side navigation after login without
requiring a full page reload.
2026-03-30 13:54:35 +03:00
alexei.dolgolyov 37cfa090ac feat: global Docker health indicator and graceful degradation
- GET /api/health endpoint returning Docker connectivity status
- Sidebar shows Docker connection dot (green=connected, red=disconnected)
- Stale scanner returns store-only results when Docker is unavailable
- Polls health every 30s
2026-03-30 13:43:33 +03:00
alexei.dolgolyov 71aeb615b3 fix(observability): router conflict, logout button, missing i18n
- Fix chi duplicate Route() panic by consolidating read/write routes
  into single Route blocks with nested admin Group
- Add logout button to sidebar with token cleanup
- Add missing settingsAuth.password i18n key
2026-03-30 12:26:22 +03:00
alexei.dolgolyov e0a648fb0c fix(observability): address final review findings
Critical fixes:
- Fix StaleContainer frontend type to match nested backend response shape
- Guard ContainerID[:12] slice against empty/short IDs in ListAllProxies

High-priority fixes:
- Support comma-separated severity/source in event log filtering (IN clause)
- Eliminate N+1 queries in ListAllProxies and FindStaleInstances (pre-load maps)
- Stop leaking internal error messages to API clients (use slog + generic msgs)
2026-03-30 11:47:16 +03:00
alexei.dolgolyov 7c57c740b4 feat(observability): phase 8 - container stats, notifications & dashboard
Add container monitoring and notification system:
- Docker Stats API: real-time CPU/memory for running containers
- Webhook notifications for errors (deploy failures, stale, proxy unhealthy)
- Event log auto-pruning (daily, 30-day retention)
- ContainerStats component with auto-polling progress bars
- SystemHealthCard dashboard widget with running/proxy/error counts
- Full EN/RU i18n for stats and system health
2026-03-30 11:37:25 +03:00
alexei.dolgolyov 79a40f3d9c feat(observability): phases 4-7 - complete frontend UI (big bang)
Add all frontend pages for observability & proxy management:
- Proxy Viewer: /proxies with grouped view, filtering, health indicators
- Proxy Creation: form with live validation, diagnostic hints, edit/delete
- Stale Containers: /containers/stale with dashboard widget, cleanup actions
- Event Log: /events with filters, pagination, real-time SSE streaming
- Navigation: proxies and events links in sidebar
- i18n: full EN/RU translations for all new features
- Settings: stale threshold configuration
2026-03-30 11:29:10 +03:00
alexei.dolgolyov 7a85441b81 feat(observability): phase 3 - direct proxy creation with validation
Add standalone proxy management:
- Multi-step validation pipeline (DNS, TCP, HTTP) with diagnostic hints
- Proxy lifecycle: create/update/delete via NPM API with SSL auto-assign
- Periodic health monitoring (5min) with event log on status transitions
- Unified /api/proxies/all endpoint merging standalone + managed proxies
- Frontend types and API functions for downstream UI phases
2026-03-30 11:19:55 +03:00
alexei.dolgolyov c38b7d4c78 feat(observability): phase 1 - schema, models & event log backend
Add database foundation for observability features:
- event_log table with severity/source filtering and pagination
- standalone_proxies table for user-created reverse proxies
- stale_threshold_days setting (default 7 days)
- Auto-persist warn/error events from event bus to database
- SSE broadcast of persistent events for real-time UI updates
- Frontend types and API functions for downstream UI phases
2026-03-30 10:59:13 +03:00
alexei.dolgolyov 9f284932a1 feat: SSL wildcard certificate picker from NPM
- NPM client: ListCertificates endpoint
- API: GET /api/settings/npm-certificates (wildcard-only filter)
- Settings UI: EntityPicker for selecting wildcard certs
- Deployer: applies certificate_id + ssl_forced to proxy hosts
- Uses HTTPS subdomain URLs when SSL cert is configured
2026-03-29 13:07:58 +03:00
alexei.dolgolyov e94c4f9116 feat: optional NPM proxy per stage
Add enable_proxy boolean to stages (default true). When disabled,
the deployer skips NPM proxy host creation — useful for internal
services, workers, or externally-routed containers. UI shows
toggle in Add Stage form and "No Proxy" badge on stage header.
2026-03-29 12:58:13 +03:00
alexei.dolgolyov 1cfd23c431 feat: base volume path setting
Add global base_volume_path to settings. Relative volume source
paths are automatically prepended with the base path at deploy
time. Absolute paths are used as-is. Configurable in Settings >
General.
2026-03-28 15:21:37 +03:00
alexei.dolgolyov 62a9249abf feat: mark already-added images as disabled in EntityPicker 2026-03-28 15:16:37 +03:00
alexei.dolgolyov 3a644b3b0b fix: hide native number input spinners for clean dark mode look 2026-03-28 14:52:32 +03:00
alexei.dolgolyov f8c2e1ad74 fix: align auto deploy toggle with form field row, add label 2026-03-28 14:51:40 +03:00
alexei.dolgolyov 95fed8bf08 fix: consistent card styling, use ToggleSwitch for auto deploy 2026-03-28 14:48:34 +03:00
alexei.dolgolyov c64f1e5363 feat: inline project editing (name, image, port, healthcheck) 2026-03-28 14:43:27 +03:00
alexei.dolgolyov d3dd2be421 feat: add stage management UI, fix null stages
- Add Stage button with inline form (name, tag pattern, max instances, auto deploy)
- Delete stage button per stage header
- Add createStage/updateStage/deleteStage to API client
- Fix null stages crash on env page
- Fix null slices globally in respondJSON via reflection
2026-03-28 14:40:04 +03:00
alexei.dolgolyov c2040656bd feat: EntityPicker component, replace dropdowns with command palette
Port EntityPalette pattern from wled-screen-controller as Svelte 5
component. Full-screen modal with search, keyboard navigation
(Arrow keys, Enter, Escape), grouped items, current-item accent,
auto-scroll, backdrop blur.

Replace inline image browser dropdowns on Projects and Quick Deploy
pages with EntityPicker. Add EntityPickerItem type and i18n keys.
2026-03-28 14:31:13 +03:00
alexei.dolgolyov 777cafb622 fix: auto-resolve project name from image, fix global scroll
- Auto-fill project name from image path when browsing
- Prevent html/body scroll jump with overflow: hidden
2026-03-28 14:22:49 +03:00
alexei.dolgolyov 74127b89d7 fix: simplify new project form, remove redundant registry field 2026-03-28 14:21:07 +03:00