Fix all build/type/lint errors (zod 3.25 compat wrapper, Svelte 5 fixes), write 115 unit tests across 10 test files, expand seed script with demo data, update Docker config with migration on startup.
9.7 KiB
Feature Context: Web App Launcher — MVP
Current State
Phase 8 (Integration, Testing & Deployment) is complete. All build errors, type errors, and lint errors resolved. 115 tests pass across 10 test files covering all services, utilities, and validators. Key fixes: (1) Created src/lib/utils/zod-adapter.ts to wrap sveltekit-superforms zod adapter for zod 3.25+ compatibility — the new zod version's stricter type inference makes z.object() return types incompatible with superforms' ZodObjectType constraint; (2) Fixed JWT expiresIn type cast in authService; (3) Reordered private field initialization in ThemeStore to fix $derived referencing #systemPreference before init; (4) Fixed curly brace escaping in SettingsForm placeholder; (5) Added {#each} keys across 6 components; (6) Removed unused imports; (7) Disabled svelte/no-navigation-without-resolve lint rule for static routes; (8) Changed vitest environment from jsdom to node. Seed script expanded with regular demo user, 7 sample apps (Plex, Nextcloud, Gitea, Home Assistant, Grafana, Portainer, Pi-hole), 3 sections, idempotent re-seeding. Dockerfile updated with prisma migrate on container startup. All four checks pass: npm run build, npm run check (0 errors), npm run lint (0 errors), npm test (115/115 pass).
Phase 7 (UI Polish & Ambient Backgrounds) is complete. All 24 tasks implemented. Three Svelte 5 rune-based stores created: theme.svelte.ts (dark/light/system mode cycling, HSL primary color with --primary-h/--primary-s/--primary-l CSS variables set via JS, background type selection, all persisted to localStorage, auto-applies dark/light class to <html>), ui.svelte.ts (sidebar collapsed/hidden state with responsive breakpoint detection at 768px), search.svelte.ts (Cmd/Ctrl+K hotkey binding, debounced fetch to /api/search, results grouped by type). Layout system: MainLayout.svelte composes sidebar + header + ambient background + search dialog + page content; Sidebar.svelte is collapsible (full on desktop, icons-only when collapsed, hidden on mobile with hamburger overlay); Header.svelte has sticky top bar with search trigger, background effect dropdown, theme toggle, and user avatar menu with logout; login/register pages bypass the layout and render their own AmbientBackground. Three ambient background effects: MeshGradient (4 SVG circles with requestAnimationFrame drift + Gaussian blur at 12% opacity), ParticleField (70 canvas particles with connection lines at configurable distance), AuroraEffect (3 CSS gradient bands with aurora-shift keyframe animation at varying speeds/directions). Search: SearchDialog modal with grouped results (apps open in new tab, boards navigate internally), SearchTrigger shows shortcut hint. CSS enhancements in app.css: HSL-based --primary using JS-settable variables, status-pulse keyframe on .status-online, .card-hover class (scale 1.02 + elevated shadow), .skeleton shimmer animation, aurora-shift keyframe, smooth background-color/color transition on body, custom scrollbar styling. app.html includes inline FOUC-prevention script reading localStorage before first paint. Page transitions via {#key $page.url.pathname} + Svelte fade. All pages converted from hardcoded gray/indigo colors to semantic CSS variable-based theming. Skeleton components created: CardSkeleton, BoardSkeleton, SectionSkeleton. +layout.server.ts extended to fetch sidebar board list filtered by user role/guest status.
Phase 4 (App Registry & Healthcheck) is complete. All app CRUD API routes are implemented at /api/apps (GET/POST) and /api/apps/[id] (GET/PATCH/DELETE) with Zod validation and auth middleware. Status history is served from /api/apps/[id]/status. The healthcheck service performs HTTP HEAD/GET requests with AbortController timeouts, mapping responses to online/offline/degraded/unknown. The scheduler uses node-cron (default: every 60 seconds) with an initial delayed check on startup. Icon resolution supports lucide, simple-icons (CDN), direct URL, and emoji types. The app registry UI at /apps renders cards in a responsive grid with category filtering and an inline Superforms create form. Custom icon uploads are handled at /api/uploads with type (SVG/PNG/JPG/WebP) and size (<1MB) validation, saving to static/uploads/. A Docker healthcheck endpoint at /api/health returns 200 with no auth. All Svelte components use runes mode ($state, $derived, $props).
Phase 3 (Authentication System) is complete. The full local authentication flow is implemented: login, registration, logout, and JWT token refresh. hooks.server.ts validates access tokens on every request, injects event.locals.user/session, and silently rotates expired tokens via refresh tokens. Protected routes redirect to /login; guest-accessible board routes are exempt. Login and registration pages use Superforms + Zod with inline validation errors. Registration respects the SystemSettings.registrationEnabled toggle. Reusable middleware helpers (requireAuth, requireAdmin, requireRole) are available for downstream phases. The root layout injects user session into all page data. The root page redirects to the default board or login. jwt.ts and password.ts are thin re-exports from authService (no duplication). Build does not pass yet (Big Bang strategy — expected).
Phase 5 (Board, Section & Widget System) is complete. All 20 tasks implemented: 5 API route files for board/section/widget CRUD (/api/boards, /api/boards/[id], /api/boards/[id]/sections, /api/boards/[id]/sections/[sid], /api/boards/[id]/sections/[sid]/widgets), 3 page routes for board list (/boards), board view (/boards/[boardId]), and board editor (/boards/[boardId]/edit), plus 9 Svelte components across board/section/widget directories. Board list API filters by permissions: admins see all, regular users see boards where they have VIEW+ permission via permissionService.checkPermission(), guests see only isGuestAccessible boards. Board view loads the full hierarchy (board -> sections -> widgets -> app -> latest status) via boardService.findBoardById. The board editor uses SvelteKit form actions (updateBoard, addSection/updateSection/deleteSection, addWidget/deleteWidget) with use:enhance for progressive enhancement. Section collapse uses Svelte's built-in slide transition. Widget grid is responsive CSS grid (2 cols mobile, 3 tablet, 4 desktop). AppWidget reuses AppHealthBadge for status display.
Phase 6 (Admin Panel) is complete. All 18 tasks implemented: admin layout with requireAdmin guard in +layout.server.ts and nav bar linking Users/Groups/Settings plus Back to Dashboard. User management at /admin/users supports full CRUD via Superforms (create with email/displayName/password/role, inline role editing, delete with confirmation) plus group membership management (add/remove users from groups). Group management at /admin/groups supports CRUD with inline editing, member count display, and default-group toggle. System settings at /admin/settings configures auth mode (local/oauth/both), registration toggle, OAuth fields (stored, non-functional in MVP), default theme (dark/light), default primary color (hex), and healthcheck defaults (JSON). Four admin components created: UserTable.svelte, GroupTable.svelte, SettingsForm.svelte, and PermissionEditor.svelte (reusable with onGrant/onRevoke callback props for entity/target/level selection). Six REST API route files added: /api/users (GET/POST), /api/users/[id] (GET/PATCH/DELETE), /api/groups (GET/POST), /api/groups/[id] (GET/PATCH/DELETE), /api/admin/settings (GET/PATCH) — all admin-only. Global search endpoint at /api/search?q=term searches apps by name/description/category and boards by name/description, filtering results by user permissions via permissionService.checkPermission. Self-deletion protection prevents admin from deleting their own account. All forms use Superforms + Zod validation schemas from $lib/utils/validators.ts.
Temporary Workarounds
- Permission model uses polymorphic pattern (entityType/targetType strings) without FK relations to avoid SQLite dual-FK constraint issues. Queries are done manually in
permissionService.ts. - JSON fields (backgroundConfig, config, healthcheckDefaults) are stored as String in SQLite and parsed at the application layer.
package.jsonprisma.seedconfig triggers a deprecation warning — migrate toprisma.config.tswhen upgrading to Prisma 7.
Cross-Phase Dependencies
- Phase 2 depends on Phase 1 (project scaffolding, Prisma setup)
- Phase 3 depends on Phase 2 (user/group models, auth service) ✅
- Phase 4 depends on Phase 2 (app model, services layer)
- Phase 5 depends on Phase 2 (board/section/widget models) and Phase 4 (app widget references apps)
- Phase 6 depends on Phases 3-5 (admin needs auth, app, board entities)
- Phase 7 depends on Phase 1 (Tailwind, shadcn-svelte) and Phase 5 (board layout to polish)
- Phase 8 depends on all prior phases
Implementation Notes
- Big Bang strategy: intermediate phases may not build/pass tests. Only Phase 8 must result in a fully working build.
- SQLite with Prisma — single file DB at
data/launcher.db - All env config via environment variables;
.env.exampleprovided as template - Svelte 5 runes mode: use
$state,$derived,$effect— NOT legacy stores for component state - shadcn-svelte uses Bits UI primitives — each component is a local file, not a library import
App.Localsusesemail+displayNamefields (aligned with User model, updated in Phase 2)- Prisma client singleton at
src/lib/server/prisma.ts— use this for all DB access - Services export pure async functions (not classes), use immutable patterns
tsxdevDependency added for running the seed script