feat(mvp): phase 4 - app registry & healthcheck

Add app CRUD API endpoints, healthcheck service with node-cron scheduler,
icon resolver (Lucide, Simple Icons, CDN, uploads), app management UI
with Superforms, health badge component, and Docker health endpoint.
This commit is contained in:
2026-03-24 20:53:50 +03:00
parent 2c001df322
commit 4d941f566f
17 changed files with 962 additions and 21 deletions
+2
View File
@@ -2,6 +2,8 @@
## Current State
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).
## Temporary Workarounds
+2 -2
View File
@@ -30,7 +30,7 @@ Build a self-hosted web application launcher/dashboard for a TrueNAS server envi
- [x] Phase 1: Project Scaffolding & Tooling [backend] → [subplan](./phase-1-scaffolding.md)
- [x] Phase 2: Database Schema & Services Layer [backend] → [subplan](./phase-2-database-services.md)
- [x] Phase 3: Authentication System [fullstack] → [subplan](./phase-3-authentication.md)
- [ ] Phase 4: App Registry & Healthcheck [fullstack] → [subplan](./phase-4-app-healthcheck.md)
- [x] Phase 4: App Registry & Healthcheck [fullstack] → [subplan](./phase-4-app-healthcheck.md)
- [ ] Phase 5: Board, Section & Widget System [fullstack] → [subplan](./phase-5-board-widgets.md)
- [ ] Phase 6: Admin Panel [fullstack] → [subplan](./phase-6-admin-panel.md)
- [ ] Phase 7: UI Polish & Ambient Backgrounds [frontend] → [subplan](./phase-7-ui-polish.md)
@@ -43,7 +43,7 @@ Build a self-hosted web application launcher/dashboard for a TrueNAS server envi
| Phase 1: Scaffolding | backend | ✅ Complete | ✅ | ⬜ | ⬜ |
| Phase 2: Database & Services | backend | ✅ Complete | ⬜ | ⬜ | ⬜ |
| Phase 3: Authentication | fullstack | ✅ Complete | ⬜ | ⬜ | ⬜ |
| Phase 4: App & Healthcheck | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
| Phase 4: App & Healthcheck | fullstack | ✅ Complete | ⬜ | ⬜ | ⬜ |
| Phase 5: Board & Widgets | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
| Phase 6: Admin Panel | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
| Phase 7: UI Polish | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
@@ -1,6 +1,6 @@
# Phase 4: App Registry & Healthcheck
**Status:** ⬜ Not Started
**Status:** ✅ Complete
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** fullstack
@@ -9,20 +9,20 @@ Build the app (service) registry with CRUD operations, the icon resolution syste
## Tasks
- [ ] Task 1: Create `src/routes/api/apps/+server.ts` — GET (list), POST (create)
- [ ] Task 2: Create `src/routes/api/apps/[id]/+server.ts` — GET, PATCH, DELETE
- [ ] Task 3: Create `src/routes/api/apps/[id]/status/+server.ts` — GET healthcheck status
- [ ] Task 4: Implement `src/lib/server/services/healthcheckService.ts` — perform HTTP health checks
- [ ] Task 5: Implement `src/lib/server/jobs/healthcheckScheduler.ts` — node-cron scheduled pings
- [ ] Task 6: Implement `src/lib/server/utils/iconResolver.ts` — resolve icon by type (Lucide, Simple Icons, Dashboard Icons CDN, upload path)
- [ ] Task 7: Create `src/routes/apps/+page.server.ts` — load app list
- [ ] Task 8: Create `src/routes/apps/+page.svelte` — app registry list page
- [ ] Task 9: Create `src/lib/components/app/AppCard.svelte` — app card with status indicator
- [ ] Task 10: Create `src/lib/components/app/AppForm.svelte` — create/edit app form (Superforms)
- [ ] Task 11: Create `src/lib/components/app/AppIconPicker.svelte` — icon selection UI
- [ ] Task 12: Create `src/lib/components/app/AppHealthBadge.svelte` — status badge (online/offline/degraded/unknown)
- [ ] Task 13: Create `src/routes/api/health/+server.ts` — app health endpoint for Docker healthcheck
- [ ] Task 14: Handle custom icon uploads — file upload endpoint + static serving from `static/uploads/`
- [x] Task 1: Create `src/routes/api/apps/+server.ts` — GET (list), POST (create)
- [x] Task 2: Create `src/routes/api/apps/[id]/+server.ts` — GET, PATCH, DELETE
- [x] Task 3: Create `src/routes/api/apps/[id]/status/+server.ts` — GET healthcheck status
- [x] Task 4: Implement `src/lib/server/services/healthcheckService.ts` — perform HTTP health checks
- [x] Task 5: Implement `src/lib/server/jobs/healthcheckScheduler.ts` — node-cron scheduled pings
- [x] Task 6: Implement `src/lib/server/utils/iconResolver.ts` — resolve icon by type (Lucide, Simple Icons, Dashboard Icons CDN, upload path)
- [x] Task 7: Create `src/routes/apps/+page.server.ts` — load app list
- [x] Task 8: Create `src/routes/apps/+page.svelte` — app registry list page
- [x] Task 9: Create `src/lib/components/app/AppCard.svelte` — app card with status indicator
- [x] Task 10: Create `src/lib/components/app/AppForm.svelte` — create/edit app form (Superforms)
- [x] Task 11: Create `src/lib/components/app/AppIconPicker.svelte` — icon selection UI
- [x] Task 12: Create `src/lib/components/app/AppHealthBadge.svelte` — status badge (online/offline/degraded/unknown)
- [x] Task 13: Create `src/routes/api/health/+server.ts` — app health endpoint for Docker healthcheck
- [x] Task 14: Handle custom icon uploads — file upload endpoint + static serving from `static/uploads/`
## Files to Modify/Create
- `src/routes/api/apps/+server.ts`
@@ -55,11 +55,21 @@ Build the app (service) registry with CRUD operations, the icon resolution syste
- ⚠️ Big Bang: pages will be functional but minimally styled until Phase 7
## Review Checklist
- [ ] All tasks completed
- [ ] Code follows project conventions
- [ ] No unintended side effects
- [x] All tasks completed
- [x] Code follows project conventions
- [x] No unintended side effects
- [ ] Build passes
- [ ] Tests pass (new + existing)
## Handoff to Next Phase
<!-- Filled in by the implementation agent after completing this phase. -->
All 14 tasks are implemented. Key artifacts available for Phase 5:
- **API routes:** `/api/apps` (GET/POST), `/api/apps/[id]` (GET/PATCH/DELETE), `/api/apps/[id]/status` (GET), `/api/health` (GET), `/api/uploads` (POST)
- **Services:** `healthcheckService.ts` provides `checkAppHealth()` and `checkAllApps()`; `healthcheckScheduler.ts` provides `startScheduler()`/`stopScheduler()` using node-cron
- **Icon resolution:** `iconResolver.ts` maps all 4 icon types (lucide, simple, url, emoji) to renderable objects; `AppCard.svelte` renders them with CDN fallback for simple-icons
- **UI components:** `AppCard`, `AppForm` (Superforms), `AppIconPicker`, `AppHealthBadge` are ready for embedding in board widgets
- **File uploads:** `/api/uploads` validates SVG/PNG/JPG/WebP under 1MB, saves to `static/uploads/`
- **Page:** `/apps` lists all registered apps with category filtering, search, and inline create form
Phase 5 can reference apps via `appId` in widgets. The `appService.findAll()` and `appService.findById()` include latest status in responses. The healthcheck scheduler should be started from `hooks.server.ts` or a startup hook in Phase 8.