74 Commits

Author SHA1 Message Date
alexei.dolgolyov 0a13b6b58c fix(i18n): add missing admin.custom_css labels (en + ru)
Lint & Test / lint-and-check (push) Failing after 5m9s
Lint & Test / test (push) Has been skipped
Lint & Test / build (push) Has been skipped
Lint & Test / docker-build (push) Has been skipped
Lint & Test / audit (push) Has been skipped
These keys were referenced in SettingsForm but absent from both locales, so they rendered as raw keys instead of the intended text.
2026-05-27 23:11:40 +03:00
alexei.dolgolyov 5dcadd1c20 feat(ui): migrate entire UI to "Cozy Home" design
Warm, friendly redesign replacing the generic cold-shadcn look. Built as a
swappable token bundle so other presets can be added later; dark mode and the
user-tunable accent hue are retained.

Foundation
- app.css: warm cream (light) + "dusk" (dark) token system; terracotta accent
  (default hue 16); pastel --room-* palette; vivid --status-* (dots/bars) plus
  AA-legible --status-*-ink (text); soft warm shadows; --radius 1rem; font tokens
- Fonts: Fraunces (display) + Figtree (body), self-hosted in static/fonts
  (no Google CDN) so offline/LAN installs work; system-ui fallbacks kept
- h1/h2/h3 render in Fraunces via base layer

Chrome and surfaces
- Sidebar, Header, home, AppCard/BoardCard, BoardHeader, sections, favorites
- 29 widgets + integration renderers: cozy card shells, room-palette charts
- Default background is a static warm "cozy" glow (mesh demoted, rAF gated on
  prefers-reduced-motion)

System-wide
- Status colors tokenized (no raw bg/text-*-500 or status hex); success/warning
  to status tokens, categorical to room palette, errors to destructive
- Inputs rounded-xl; buttons rounded-xl; cards/dialogs rounded-[1.4rem];
  soft-shadow vocabulary only; focus-visible:ring-primary/30
- Forms, admin tables (now cozy cards), dialogs, popovers, auth screens

a11y: reduced-motion guards; darker status "ink" text for AA on cream.
Known tradeoff: terracotta primary + white button text ~2.96:1 (signature color,
user-tunable).

Verified: svelte-check 0/0, build ok, 274 tests pass, eslint 0 errors.
Design refs + system sheet in design-mockups/.
2026-05-27 23:04:47 +03:00
alexei.dolgolyov f1cfb61d13 feat: production hardening + password reset, metrics, signed webhooks
Lint & Test / lint-and-check (push) Failing after 5m5s
Lint & Test / test (push) Has been skipped
Lint & Test / build (push) Has been skipped
Lint & Test / docker-build (push) Has been skipped
Lint & Test / audit (push) Has been skipped
Security hardening (CRITICAL/HIGH from production-readiness audit):
- Require strong JWT_SECRET + separate INTEGRATION_ENCRYPTION_KEY at boot;
  refuse placeholder defaults. Integration key now derived via HKDF.
- SSRF guard (src/lib/server/utils/safeFetch.ts): DNS-resolves and rejects
  RFC1918/loopback/link-local/IPv4-mapped IPv6/decimal-IP/cloud-metadata.
  Manual redirect handling re-validates each 3xx Location hop. Applied to
  healthcheck, RSS, calendar, metric, system-stats, camera, notifications,
  discovery, apps/preview, and all integration clients.
- API tokens, session refresh tokens, invite tokens, password-reset tokens
  switched from bcrypt to sha256 with @unique indexed lookup (O(1) instead
  of O(N) bcrypt-compares; eliminates a trivial DoS).
- Refresh-token reuse detection via Session.previousTokenHash.
- Permission checks on App PATCH/DELETE and Widget/Section endpoints.
- /api/integrations/alerts now requires auth.
- SVG uploads sanitized through DOMPurify (svg profile, scheme allow-list).
- Custom CSS sanitizer + selector scoping (decodes CSS unicode escapes
  before pattern match, drops forbidden at-rules incl. @import without
  whitespace, strips dangerous url() args). Scoped to .custom-css-scope.
- Backup restore validates SQLite magic header, takes a safety snapshot,
  uses atomic rename, re-applies pragmas.
- SQLite WAL + busy_timeout + foreign_keys + synchronous=NORMAL at startup.
- Healthcheck scheduler was dead code; wired in hooks.server.ts with
  HMR-safe singleton, concurrency cap, overlap prevention, retention jobs
  for AppClick/Notification/AuditLog. Composite indexes added on hot paths.
- Security headers (CSP, HSTS-on-https, X-Frame-Options, Permissions-Policy)
  emitted on every response.
- Account-enumeration mitigation on login (dummy bcrypt on no-user/oauth
  branches) + rate limiting on login/register/onboarding/refresh/invite/
  password-reset.
- OAuth callback sanitizes IdP error_description before echoing.

New features:
- Custom +error.svelte pages (root + boards + admin) via shared
  ErrorState component. Inverted hierarchy (status as label, title as hero).
- /forgot-password + /reset-password + admin-mediated /admin/password-resets
  page. SHA256 tokens, 24h TTL, all sessions revoked on apply.
- /invite page for manual invite-token redemption.
- /api/metrics Prometheus exposition with optional METRICS_TOKEN bearer
  auth. Counters for login/healthcheck/notification/integration; gauges
  for users/boards/apps + per-status app counts.
- Webhook HMAC-SHA256 signing for HTTP notification channels (optional
  shared secret + configurable signature header, default X-Signature-256).
- PATCH /api/users/me/password for self-service password change.
- Persistent uploads at /app/data/uploads with served-from-volume handler
  at /uploads/[...path]. SVGs served with CSP: sandbox.
- /api/health does a DB ping; returns 503 on disconnect.
- Public /status filtered to guest-accessible-board apps when unauthenticated.
- Audit log coverage: LOGIN_SUCCESS/FAILED, LOGOUT, OAUTH_LOGIN,
  OAUTH_USER_PROVISIONED, SESSION_REVOKED, API_TOKEN_*, INVITE_*,
  APP_UPDATED, PASSWORD_CHANGED, PASSWORD_RESET_*.

Performance:
- Board page: removed double findAll() over-fetch; include links + appTags
  in board query; widgets lazy-loaded via dynamic imports (marked,
  DOMPurify, hls.js, integration renderers).
- uptimeService.getAllAppsUptime: single batched query instead of N+1.
- 30s in-memory user-locals cache; invalidated on user mutation.
- pruneOldStatuses: single window-function DELETE instead of N+1.

Code quality:
- Typed error classes (NotFoundError, PermissionError, RateLimitError,
  IntegrationError) with toHttpError mapper.
- Locals.user shape exposes avatarUrl and narrows role via guard.
- App input types derived from Zod schemas via z.infer.
- 274 tests passing (up from 212); 62 new tests covering SSRF guard,
  CSS sanitizer, SVG sanitizer, rate limiter.

CI / Docker / config:
- Test workflow adds build, docker-build, audit jobs. Release workflow
  uses buildx multi-arch (amd64+arm64) with provenance + SBOM.
- Dockerfile uses tini, multi-stage prune, persistent uploads dir, single
  prisma migrate deploy (no destructive db push fallback).
- docker-compose: JWT_SECRET + INTEGRATION_ENCRYPTION_KEY required at
  startup, log rotation, resource limits.
- README documents breaking-change upgrade path.

Bug fixes from UI/UX review:
- ~55 missing i18n keys added to en/ru (auth flows, error pages, admin
  nav, register invite banner, settings.card_style).
- Hardcoded English on login replaced with $t('auth.remember_me').
- Admin nav uses i18n keys; mobile horizontal-scroll layout.
- Page <title> tags standardized.
- Password-resets: separated error/info/success surfaces, ConfirmDialog
  replaces window.confirm.
- Auth pages have matching lucide icon badges.
- Webhook secret has eye toggle and monospace input.
- text-green-500 → text-emerald-500 to match codebase convention.

Pre-existing CI lint failures cleaned up (31 errors → 0): each-key
attributes added, unused-svelte-ignore comments removed, two any casts
typed, dead skeleton components removed, /boards/[id]/edit redirect to
inline edit mode.

Tests: 274 / 274 passing
Type check: 0 errors / 0 warnings
Build: green
2026-05-26 19:51:21 +03:00
alexei.dolgolyov 38335e925b feat(auth): admin invite links
Lint & Test / lint-and-check (push) Failing after 5m4s
Lint & Test / test (push) Has been skipped
Replaces the blunt registrationEnabled toggle with per-invite access.
Invites are tokenized, single-use, optionally locked to an email, can
grant user or admin role, and expire (default 7d, max 90d).

- Invite model with tokenHash (bcrypt), email, role, expiresAt,
  usedAt/usedByUserId.
- inviteService: create, list, revoke, findInviteByToken, consumeInvite.
  Token is shown exactly once at creation.
- /admin/invites page: list with status (Active/Used/Expired), generate
  with email lock + role + custom expiry, copy one-shot URL, revoke.
- /register?invite=TOKEN: accepts invite even when registrationEnabled
  is false; shows a banner; enforces email lock; applies the invite's
  role on creation; consumes the invite on success.
- Linked from the admin navbar.
2026-04-16 04:00:18 +03:00
alexei.dolgolyov 9cab7262e6 feat(auth): sessions page to list and revoke devices
- /settings/sessions: list user's active sessions with label, IP,
  last-used, expires, user-agent. Highlights the current device.
- Revoke one session (/api/sessions/:id DELETE) or all-other sessions
  (/api/sessions DELETE). Admins can revoke any session.
- Revoking the current session clears cookies and kicks the user to
  /login.
- Wired into the main settings page.
2026-04-16 03:46:33 +03:00
alexei.dolgolyov b9f3a2ca0b feat(auth): Session model + remember-me
Replace the single `user.refreshToken` column with a proper Session
table so users can have multiple concurrent sessions (phone, laptop,
etc.), each with their own refresh token, expiry, label, and
remember-me flag.

- Add Session model (id, userId, tokenHash, label, userAgent,
  ipAddress, rememberMe, lastUsedAt, expiresAt).
- Drop `User.refreshToken` and `User.refreshTokenExpiresAt`.
- authService: new createSession/validateSession/rotateSession/
  revokeSession/listUserSessions helpers; remove refresh-token-on-user
  functions.
- sessionCookies helper now issues a session_id cookie alongside
  access_token and refresh_token; rotateSessionCookies keeps the same
  session id on refresh.
- Login form adds a "Keep me signed in for 30 days" checkbox;
  TTL is 7d by default, 30d with remember-me.
- User-Agent parsed into a friendly label ("Chrome on Windows") for
  the upcoming sessions page.
- hooks.server.ts, refresh endpoint, logout, register, oauth callback,
  and onboarding all switched to the new session API.
2026-04-16 03:41:52 +03:00
alexei.dolgolyov 3fa30f72a3 feat(auth): auto-login after onboarding, consolidate session cookies
Lint & Test / lint-and-check (push) Failing after 5m1s
Lint & Test / test (push) Has been skipped
- Extract session cookie issuance into sessionCookies.ts helper; remove
  duplicated COOKIE_BASE blocks from login, register, oauth callback/authorize,
  refresh handler, hooks.server.ts, and onboarding.
- Derive cookie secure flag from ORIGIN (https://...) instead of NODE_ENV so
  plain-HTTP production deploys don't silently drop cookies.
- Auto-login admin after onboarding completes; UI does a full reload so
  hooks.server.ts picks up the new session.
- Harden onboarding: reject duplicate admin creation, flip onboardingComplete
  atomically to prevent concurrent completions, error out if no admin found.
- Fix Dockerfile CMD operator precedence: node build now always runs after
  migrate deploy || db push.
- Wire ORIGIN env through docker-compose.
2026-04-16 03:28:46 +03:00
alexei.dolgolyov 2c9c36605d fix: consolidate migrations and fix Prisma ESM compatibility
Release / docker (push) Successful in 1m2s
Release / release (push) Successful in 4s
Lint & Test / lint-and-check (push) Failing after 5m11s
Lint & Test / test (push) Has been skipped
- Replace 3 partial migrations with single init migration from schema
- Fixes missing backupEnabled, integrationType, and other columns
- Move @prisma/client to dependencies for adapter-node externalization
- Add ssr.external to prevent Vite bundling Prisma (fixes __dirname error)
v0.0.1
2026-04-10 21:23:00 +03:00
alexei.dolgolyov 1f7e040049 fix: resolve Prisma __dirname ESM error in production Docker build
Lint & Test / lint-and-check (push) Failing after 5m5s
Release / docker (push) Successful in 1m23s
Release / release (push) Successful in 4s
Lint & Test / test (push) Has been skipped
Move @prisma/client and prisma to dependencies so adapter-node
externalizes them instead of bundling. Add ssr.external config
for Vite to prevent inlining Prisma's CJS engine loader.
2026-04-10 21:17:39 +03:00
alexei.dolgolyov 85b4576991 fix: remove deprecated --skip-generate flag from Prisma db push
Release / release (push) Successful in 4s
Lint & Test / lint-and-check (push) Failing after 5m1s
Lint & Test / test (push) Has been skipped
Release / docker (push) Successful in 1m0s
2026-04-10 20:31:24 +03:00
alexei.dolgolyov 32b874f4a3 fix(ci): use jq instead of python3, handle existing releases via PATCH
Release / docker (push) Successful in 32s
Release / release (push) Successful in 6s
Lint & Test / lint-and-check (push) Failing after 5m3s
Lint & Test / test (push) Has been skipped
2026-04-10 20:23:17 +03:00
alexei.dolgolyov bf907c7858 fix(ci): add OCI labels to associate Docker image with Gitea repo
Release / docker (push) Successful in 4s
Release / release (push) Failing after 4s
Lint & Test / lint-and-check (push) Has been cancelled
Lint & Test / test (push) Has been cancelled
2026-04-10 20:19:27 +03:00
alexei.dolgolyov 402a0b34c1 fix: use COPY --chown instead of recursive chown in Dockerfile
Release / docker (push) Successful in 55s
Release / release (push) Successful in 4s
Lint & Test / test (push) Has been cancelled
Lint & Test / lint-and-check (push) Has started running
Avoids slow chown -R over node_modules (thousands of files).
2026-04-10 20:15:24 +03:00
alexei.dolgolyov 0ebf6bd652 fix(ci): remove redundant test gate from release workflow
Lint & Test / lint-and-check (push) Failing after 5m14s
Lint & Test / test (push) Has been skipped
Release / release (push) Has been cancelled
Release / docker (push) Has been cancelled
Code is already validated by test.yml on push to master.
The release workflow now goes straight to docker build + release.
2026-04-10 20:04:03 +03:00
alexei.dolgolyov 124a7679b3 chore: release v0.0.1
Lint & Test / lint-and-check (push) Failing after 5m19s
Lint & Test / test (push) Has been skipped
Release / test (push) Failing after 5m14s
Release / docker (push) Has been skipped
Release / release (push) Has been skipped
- Rename CI workflow to test.yml (lint & test only)
- Add release.yml (Docker push + Gitea release on v* tag)
- Add README.md and RELEASE_NOTES.md
- Bump version to 0.0.1
2026-04-10 19:39:27 +03:00
alexei.dolgolyov 76ce85c9bb fix: polish empty states and status page layout
CI / lint-and-check (push) Failing after 5m4s
CI / test (push) Has been skipped
CI / docker-build (push) Has been skipped
- Boards: improved empty state with create button for admins
- Status: 2-column grid layout, colored left border by status, better empty state
2026-04-10 19:06:53 +03:00
alexei.dolgolyov aedc91e321 perf: batch-load app status history server-side to eliminate N+1 requests
- Load all app sparkline history in a single server query
- Pass preloadedHistory to AppCard to skip client-side fetch
- Polish empty state with icon, hint text, and add button
2026-04-10 19:06:43 +03:00
alexei.dolgolyov b5166d9768 refactor: header user menu with bits-ui dropdown, collapsible sidebar boards
- Replace manual click-outside menu with DropdownMenu from bits-ui
- Add collapsible boards section in sidebar with chevron toggle
- Add max-height scroll for boards list
2026-04-10 19:06:29 +03:00
alexei.dolgolyov 44e1849821 fix: resolve all linter errors and a11y warnings
CI / test (push) Has been cancelled
CI / docker-build (push) Has been cancelled
CI / lint-and-check (push) Has been cancelled
- Fix TS errors: editMode property order, implicit any, string|undefined
- Add $state() to bind:this element refs (IconGrid, EntityPicker, etc.)
- Fix a11y: labels, aria-labels, roles, tabindex on dialogs
- Remove unused imports (tick, svelte-i18n)
- Make AutocompleteInput/TagsInput accept optional string values
2026-04-10 19:05:25 +03:00
alexei.dolgolyov f96cbbca56 chore(i18n): add locale keys for widget resize, delete user, multi-picker 2026-04-10 19:05:11 +03:00
alexei.dolgolyov 5af670fa3c feat: multi-entity picker for status widget app selection
- Create MultiEntityPicker component with search, checkboxes, keyboard nav
- Replace plain checkbox list in status widget config with MultiEntityPicker
- Render app icons properly by type (lucide, simple, url, emoji)
2026-04-10 19:05:03 +03:00
alexei.dolgolyov f559c93e19 feat: widget column span resizing with visual size picker
- Add per-widget colSpan stored in config JSON (no DB migration)
- Replace hardcoded full-width types with configurable span
- Add visual size picker popover in edit mode overlay
- Merge widget config updates for temp widgets in Board changeset
2026-04-10 19:04:54 +03:00
alexei.dolgolyov 65783e35d2 feat: user deletion confirmation modal
Replace inline confirm/delete pattern with ConfirmDialog modal
2026-04-10 19:04:43 +03:00
alexei.dolgolyov 1e3a04f4de perf: optimize cold start by lazy-loading icons and parallelizing DB queries
- Replace barrel `import * as icons` in DynamicIcon with dynamic per-icon imports
- Eagerly connect Prisma client at startup to avoid first-request latency
- Parallelize 4 sequential DB queries in layout server load with Promise.all
2026-04-10 19:04:35 +03:00
alexei.dolgolyov 7beca05eec feat: inline dashboard editing mode with WYSIWYG experience 2026-04-03 00:33:34 +03:00
alexei.dolgolyov 17c8407c07 feat(widget-config): visual app selector grid with search and icons
Replace plain select dropdown with a searchable 2-column grid showing
app icons for the app widget type in the inline config panel.
2026-04-03 00:32:45 +03:00
alexei.dolgolyov c5f5f84c79 feat(app-form): icon picker, tag/category autocomplete, typography
- Replace AppIconPicker text input with visual IconPickerButton for
  lucide icons (grid with search)
- Add AutocompleteInput component for category field with existing
  category suggestions
- Add TagsInput component for tags field with tag pills, autocomplete
  from existing tags, and keyboard navigation
- Add GET /api/apps/suggestions endpoint returning all categories/tags
- Add getAllTags() to appService (merges Tag model + comma-separated)
- Install @tailwindcss/typography plugin to fix prose rendering
  (headings, lists, blockquotes now render in Note/Markdown widgets)
- Fix note widget validator test for new html format
2026-04-03 00:24:08 +03:00
alexei.dolgolyov a6b09aae9c feat(inline-edit): add WYSIWYG inline dashboard editing mode
Replace the disconnected board edit page with inline editing directly
on the board view. Toggle with Ctrl+E or the Edit button. Features:

- Edit mode store with changeset accumulation and batch save
- Floating toolbar (save, discard, add section, board settings, exit)
- Widget hover overlays with edit/delete/drag controls
- Type-specific widget config panels for all 14 widget types
- Section inline editing (title, icon picker, delete)
- "+" buttons for adding widgets and sections inline
- Section-level drag-and-drop reordering via svelte-dnd-action
- Batch save API endpoint (single Prisma transaction)
- Board properties side panel with live theme/wallpaper preview
- Modal widget type picker with search filtering
- Icon picker component with visual grid and search
- Confirmation dialog modal for all destructive actions
- HTML format support for Note widget (in addition to markdown/text)
- Full i18n support (en + ru) for all new UI strings
- Legacy edit page banner linking to new inline mode
2026-04-03 00:01:29 +03:00
alexei.dolgolyov d8f89c65dc Cleanup plans
CI / lint-and-check (push) Failing after 5m3s
CI / test (push) Has been skipped
CI / docker-build (push) Has been skipped
2026-04-02 23:20:08 +03:00
alexei.dolgolyov f6599430e5 Merge branch 'feature/database-backup' 2026-04-02 23:16:24 +03:00
alexei.dolgolyov b0439e39c4 feat(backup): replace JSON import/export with SQLite database backup system
Replace the JSON-based import/export with a proper backup system that copies
the SQLite database file directly. Supports manual on-demand backups, periodic
scheduled backups via node-cron, configurable retention, file download, and
full database restore.

- Add backupService with VACUUM INTO for safe DB copies
- Add backupScheduler following healthcheckScheduler pattern
- Add 6 admin API endpoints (create, list, download, restore, delete, schedule)
- Add BackupPanel UI with backup table, confirmation dialogs, schedule config
- Add backup fields to SystemSettings schema
- Remove old ImportExportPanel, exportService, importService, and related code
2026-04-02 23:16:18 +03:00
alexei.dolgolyov d479726fe3 feat: add app edit page with pre-populated form
CI / lint-and-check (push) Failing after 5m3s
CI / test (push) Has been skipped
CI / docker-build (push) Has been skipped
Add /apps/[id]/edit route that loads existing app data into the form,
allowing users to update app properties. Adds edit pencil button to
AppCard (visible on hover) and i18n keys for both EN and RU.
2026-03-25 22:42:20 +03:00
alexei.dolgolyov 44bbf7b410 fix(service-integrations): resolve type errors and test failures
- Fix CreateAppInput nullable types for integration fields
- Add type casts in IntegrationWidget renderer dispatching
- Guard decryptAppIntegration against missing fields in test mocks
2026-03-25 22:19:56 +03:00
alexei.dolgolyov 55e220bc07 feat(service-integrations): phases 9-10 — media integrations + Planka
- Emby: now playing, library stats, recently added, active streams
- Immich: library stats, recent uploads with formatted storage
- Deluge: active torrents with progress, transfer speed, disk space gauge
- MeTube: download queue progress (no auth required)
- Planka: my cards, overdue cards with red badges, board summary
- All 11 integrations registered in registry
2026-03-25 22:16:27 +03:00
alexei.dolgolyov d73fb9c680 feat(service-integrations): phases 3-8 — six service integrations
- NUT/UPS: TCP protocol client, battery/load gauges, runtime card, power alert banner
- Pi-hole: DNS stats summary, top blocked domains, query log, gravity status
- Portainer: Container summary/list, stack status via Docker API
- Gitea: Recent commits, open PRs, releases across repos
- Nginx Proxy Manager: Proxy hosts, SSL certificate expiry alerts, upstream status
- Authentik: User stats, login events feed, brute force detection alerts
- All integrations registered in registry with auto-discovery
2026-03-25 22:12:31 +03:00
alexei.dolgolyov 50e8519220 feat(service-integrations): phase 2 — integration widget & app form UI
- Add 6 renderer components: StatCard, Gauge, List, Progress, AlertBanner, Chart
- Add IntegrationWidget container with auto-refresh, loading, error states
- Add IntegrationAlertOverlay for layout-level critical alerts
- Add IntegrationConfigFields for dynamic form generation from Zod schemas
- Register integration type in WidgetRenderer
- Extend WidgetCreationForm with integration app/endpoint pickers
- Extend AppForm with integration config section and test connection button
- Add /api/integrations/alerts endpoint
2026-03-25 22:07:51 +03:00
alexei.dolgolyov 114dee57a8 feat(service-integrations): phase 1 — integration architecture foundation
- Add Integration interfaces, registry, cache, encryption, and base helpers
- Add integrationType, integrationConfig, integrationEnabled to App model
- Add integration widget type to constants and validators
- Add integration fields to AppRecord, CreateAppInput, UpdateAppInput
- Update appService with encryption/decryption for integration config
- Add API routes: list integrations, test connection, fetch endpoint data
2026-03-25 22:02:34 +03:00
alexei.dolgolyov c62ca79adb fix: delay search dialog close so link navigation fires first 2026-03-25 21:26:16 +03:00
alexei.dolgolyov bcde710cab fix: search store now parses API envelope response correctly
The search API returns { success, data: [...] } but the store was
looking for data.apps and data.boards (which don't exist). Fixed to
read from data.data[] and also added url/icon fields to search API
response so app results are clickable and show icons.
2026-03-25 21:12:17 +03:00
alexei.dolgolyov 92eeeadec0 perf: batch-load app history to eliminate N+1 fetches on board load
Previously each AppWidget fetched /api/apps/{id}/history individually
on mount, causing N sequential HTTP requests. Now the board page
server load fetches all app histories in a single Prisma query via
getBatchStatusHistory() and passes them to AppWidget via Svelte
context. AppWidget uses the pre-loaded data immediately with a
fallback fetch for non-board contexts.
2026-03-25 15:36:06 +03:00
alexei.dolgolyov 6eb6bba289 feat: Phases 4-7 — Full Feature Expansion (26 features)
8 new widget types, 6 visual enhancements, 8 functional features,
4 quality-of-life improvements. 212 files, 15k+ lines added.
Build, lint, type check, and 222 tests all pass.
2026-03-25 14:58:25 +03:00
alexei.dolgolyov 4326d95bf3 fix: use SVG icon for favicon and PWA manifest 2026-03-25 14:58:25 +03:00
alexei.dolgolyov d90507ad82 fix: add SSRF protection to URL preview endpoint
Block requests to private/reserved IP ranges (10.x, 172.16-31.x,
192.168.x, 127.x, 169.254.x, localhost, ::1) and non-http(s)
schemes in the /api/apps/preview endpoint to prevent server-side
request forgery.
2026-03-25 14:37:17 +03:00
alexei.dolgolyov 819283fa62 fix: use HLS.js for fullscreen camera stream
Fullscreen HLS video now initializes HLS.js via bind:this + $effect
instead of raw <video src>, which only works in Safari. Non-Safari
browsers now correctly play HLS streams in fullscreen mode.
2026-03-25 14:35:07 +03:00
alexei.dolgolyov 215c8fdd46 fix: enforce API token scope on requests
- Add apiTokenScope to App.Locals type definition
- Store token scope in event.locals during API token auth
- Block write operations (POST/PATCH/PUT/DELETE) for read-scoped tokens
- Block admin paths for non-admin-scoped tokens
- Returns 403 with descriptive error message
2026-03-25 14:32:48 +03:00
alexei.dolgolyov 014de026eb fix: address final review blockers
- Add /api/onboarding and /status to PUBLIC_PATHS in hooks.server.ts
  so onboarding wizard and status page work for unauthenticated users
- Add isOnboardingNeeded() guard to POST /api/onboarding to reject
  calls after onboarding is complete (security hardening)
- Add data-app-widget attribute to all AppWidget card variants to
  enable j/k keyboard navigation
2026-03-25 14:29:11 +03:00
alexei.dolgolyov 1c0a7cb850 feat: Phases 4-7 — Full Feature Expansion (26 features)
Phase 4 — New Widget Types:
- Clock/Weather, System Stats, RSS/Feed, Calendar, Markdown,
  Metric/Counter, Link Group, Camera/Stream widgets
- Backend services with caching for each data source
- Full creation form with dynamic config fields per type

Phase 5 — Visual & Styling Enhancements:
- Glassmorphism card style (solid/glass/outline)
- Board-level themes with per-board hue/saturation
- Animated SVG status rings replacing static dots
- Card size options (compact/medium/large)
- Custom CSS injection (admin + per-board, sanitized)
- Wallpaper backgrounds with blur/overlay/parallax

Phase 6 — Functional Features:
- Favorites bar with drag-and-drop reordering
- Recent apps tracking with privacy toggle
- Uptime dashboard page (/status, guest-accessible)
- Notifications system (Discord/Slack/Telegram/HTTP webhooks)
- App tags with filtering in board view
- Multi-URL app cards with expandable sub-links
- Personal API tokens with scoped permissions
- Audit log with retention and admin viewer

Phase 7 — Quality of Life:
- Onboarding wizard (5-step first-launch setup)
- App URL health preview with favicon/title detection
- Board templates (4 built-in + custom import/export)
- Keyboard shortcut overlay (j/k nav, 1-9 boards, ? help)

212 files changed, 15641 insertions, 980 deletions.
Build, lint, type check, and 222 tests all pass.
2026-03-25 14:18:10 +03:00
alexei.dolgolyov 8d7847889e feat: add IconGrid, EntityPicker controls and enhance search panel
CI / lint-and-check (push) Failing after 4m56s
CI / test (push) Has been skipped
CI / docker-build (push) Has been skipped
Port icon grid and entity picker patterns from wled-screen-controller.
IconGrid replaces plain <select> elements with visual icon grids for
known item sets (widget type, icon type, healthcheck method, permission
level). EntityPicker replaces search dropdowns with a command-palette
style overlay with keyboard navigation and filtering.

Enhance SearchDialog with keyboard navigation (arrow keys, Enter,
Escape), grouped results with section headers, active highlight,
and a footer with shortcut hints.
2026-03-25 11:58:21 +03:00
alexei.dolgolyov 54a30ca4ca feat: Phase 3 — Advanced Features
CI / lint-and-check (push) Failing after 4m58s
CI / test (push) Has been skipped
CI / docker-build (push) Has been skipped
- Import/Export: JSON export/import with conflict resolution (skip/overwrite)
- Ping history sparklines: 24h bar charts on app widgets with uptime %
- User theme overrides: per-user preferences (hue, saturation, mode, bg, locale)
- PWA: service worker, manifest, offline page, install prompt
- Auto-discovery: Docker socket + Traefik API scanning with approval UI
- Quick-add bookmarklet: popup-based add from any page
- Multi-tab sync: BroadcastChannel for theme + data changes
- Security: execFile for Docker commands, Zod on all API inputs, import limits
- 222 tests across 20 test files, all passing
2026-03-25 01:32:43 +03:00
alexei.dolgolyov 395ed821b7 fix: address all final review findings for Phase 3
- CRITICAL: Fix command injection in discoveryService (execFile instead
  of exec, path validation regex)
- CRITICAL: Add Zod validation on discover API endpoint
- HIGH: Add Zod validation on discover/approve endpoint
- HIGH: Add array length limits to import schema (1000/100/100)
- HIGH: Fix theme broadcast echo loop (setTimeout vs queueMicrotask)
- MEDIUM: Singleton BroadcastChannel instead of create-per-send
- MEDIUM: Exclude sensitive APIs from service worker cache
- MEDIUM: Fix TypeScript cast errors in exportService tests
2026-03-25 01:28:24 +03:00