Files
web-app-launcher/plans/phase-4-7-full-expansion/phase-5-functional-backend.md
T
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

11 KiB

Phase 5: Functional Features — Backend

Status: Complete Parent plan: PLAN.md Domain: backend

Objective

Implement all backend services, API routes, and background jobs for the 8 functional features: favorites, recent apps, uptime dashboard, notifications, tags, multi-URL apps, API tokens, and audit log.

Tasks

5.1 Favorites Service & API

  • Create src/lib/server/services/favoriteService.ts
    • getUserFavorites(userId) — ordered list of user's favorite apps
    • addFavorite(userId, appId) — add app to favorites (append to end)
    • removeFavorite(userId, appId) — remove from favorites
    • reorderFavorites(userId, favoriteIds[]) — update order
  • Create src/routes/api/favorites/+server.ts — GET (list), POST (add), DELETE (remove)
  • Create src/routes/api/favorites/reorder/+server.ts — PATCH (reorder)

5.2 Recent Apps Service & API

  • Create src/lib/server/services/recentAppsService.ts
    • recordClick(userId, appId) — add click record
    • getRecentApps(userId, limit=10) — get most recent unique apps
    • clearHistory(userId) — clear all click history for user
  • Create src/routes/api/recent-apps/+server.ts — GET (list), POST (record click), DELETE (clear)

5.3 Uptime Dashboard Service & API

  • Create src/lib/server/services/uptimeService.ts
    • getUptimeStats(appId, timeRange: '24h'|'7d'|'30d') — uptime percentage, avg response time
    • getUptimeTimeline(appId, timeRange) — status history with timestamps
    • getAllAppsUptime(timeRange) — aggregated uptime for all apps
    • getIncidents(appId?, timeRange) — list of down periods with duration
    • Queries AppStatus table, groups by time windows
  • Create src/routes/api/uptime/+server.ts — GET all apps uptime summary
  • Create src/routes/api/uptime/[appId]/+server.ts — GET single app uptime + timeline

5.4 Notifications Service & API

  • Create src/lib/server/services/notificationService.ts
    • Channel management: create, update, delete, list channels for user
    • sendNotification(userId, appId, event, message) — create notification record + dispatch to channels
    • Dispatchers: sendDiscord(webhookUrl, message), sendSlack(webhookUrl, message), sendTelegram(botToken, chatId, message), sendHttp(url, payload)
    • getNotifications(userId, { unreadOnly?, limit?, offset? }) — paginated notification list
    • markAsRead(notificationId), markAllAsRead(userId)
  • Create src/routes/api/notifications/+server.ts — GET (list), PATCH (mark read)
  • Create src/routes/api/notifications/channels/+server.ts — CRUD for notification channels
  • Create src/routes/api/notifications/channels/[id]/+server.ts — single channel operations
  • Create src/routes/api/notifications/channels/[id]/test/+server.ts — POST to send test notification
  • Integrate with healthcheck scheduler: trigger notifications when app status changes (online->offline, offline->online)
    • Update src/lib/server/jobs/healthcheckScheduler.ts to call notificationService on status change

5.5 Tags Service & API

  • Create src/lib/server/services/tagService.ts
    • CRUD for tags: create, update (name, color), delete, findAll
    • addTagToApp(appId, tagId), removeTagFromApp(appId, tagId)
    • getAppsByTag(tagId) — apps with a specific tag
    • getTagsForApp(appId) — tags for a specific app
  • Create src/routes/api/tags/+server.ts — GET (list), POST (create)
  • Create src/routes/api/tags/[id]/+server.ts — PATCH (update), DELETE (delete)
  • Create src/routes/api/apps/[id]/tags/+server.ts — GET (app's tags), POST (add tag), DELETE (remove tag)

5.6 Multi-URL Apps Service & API

  • Extend src/lib/server/services/appService.ts
    • addAppLink(appId, { label, url, icon, order }) — add secondary URL
    • updateAppLink(linkId, { label?, url?, icon?, order? }) — update link
    • removeAppLink(linkId) — delete link
    • reorderAppLinks(appId, linkIds[]) — update order
    • Include links in app queries (eager load)
  • Create src/routes/api/apps/[id]/links/+server.ts — CRUD for app links
  • Update existing app GET endpoints to include links in response

5.7 API Tokens Service & API

  • Create src/lib/server/services/apiTokenService.ts
    • generateToken(userId, name, scope, expiresAt?) — generate random token, store hash
    • revokeToken(tokenId, userId) — delete token
    • listTokens(userId) — list tokens (without hash, with last used)
    • validateToken(tokenString) — hash and compare, check expiry, update lastUsedAt
  • Create src/routes/api/tokens/+server.ts — GET (list), POST (generate)
  • Create src/routes/api/tokens/[id]/+server.ts — DELETE (revoke)
  • Update auth middleware to also check for Bearer token in Authorization header
    • src/lib/server/middleware/authenticate.ts — add API token validation path

5.8 Audit Log Service & API

  • Create src/lib/server/services/auditLogService.ts
    • logAction(userId, action, entityType, entityId, details?) — record audit event
    • getAuditLogs({ action?, entityType?, userId?, startDate?, endDate?, limit?, offset? }) — filtered, paginated query
    • pruneOldLogs(retentionDays) — delete logs older than retention period
  • Create src/routes/api/admin/audit-log/+server.ts — GET (list, admin only)
  • Add audit log calls to existing admin operations:
    • User CRUD, Board CRUD, App CRUD, Settings changes, Import/Export
    • Update relevant services/routes to call auditLogService.logAction()
  • Add pruning cron job to healthcheck scheduler (or create separate job):
    • Run daily, prune based on SystemSettings retention config (default 90 days)

Files to Modify/Create

  • src/lib/server/services/favoriteService.ts — new
  • src/lib/server/services/recentAppsService.ts — new
  • src/lib/server/services/uptimeService.ts — new
  • src/lib/server/services/notificationService.ts — new
  • src/lib/server/services/tagService.ts — new
  • src/lib/server/services/apiTokenService.ts — new
  • src/lib/server/services/auditLogService.ts — new
  • src/lib/server/services/appService.ts — modify (multi-URL links)
  • src/lib/server/middleware/authenticate.ts — modify (API token auth)
  • src/lib/server/jobs/healthcheckScheduler.ts — modify (notification triggers)
  • src/routes/api/favorites/+server.ts — new
  • src/routes/api/favorites/reorder/+server.ts — new
  • src/routes/api/recent-apps/+server.ts — new
  • src/routes/api/uptime/+server.ts — new
  • src/routes/api/uptime/[appId]/+server.ts — new
  • src/routes/api/notifications/+server.ts — new
  • src/routes/api/notifications/channels/+server.ts — new
  • src/routes/api/notifications/channels/[id]/+server.ts — new
  • src/routes/api/notifications/channels/[id]/test/+server.ts — new
  • src/routes/api/tags/+server.ts — new
  • src/routes/api/tags/[id]/+server.ts — new
  • src/routes/api/apps/[id]/tags/+server.ts — new
  • src/routes/api/apps/[id]/links/+server.ts — new
  • src/routes/api/tokens/+server.ts — new
  • src/routes/api/tokens/[id]/+server.ts — new
  • src/routes/api/admin/audit-log/+server.ts — new
  • Various existing route files — modify (add audit logging)

Acceptance Criteria

  • All services handle errors gracefully
  • API token auth works alongside existing JWT auth (check both)
  • Notification dispatchers handle webhook failures without crashing
  • Audit logging doesn't slow down the operations it logs (fire-and-forget pattern)
  • Uptime calculations handle edge cases (no data, all unknown, timezone issues)
  • All new endpoints require appropriate auth (user-level or admin-level)
  • Favorites and recent apps are per-user, properly isolated

Notes

  • For notification dispatchers, use simple fetch() calls — no need for a queue system at this scale
  • API token generation: use crypto.randomBytes(32).toString('hex') for the token, store bcrypt hash
  • Audit logging should be non-blocking: catch and log errors but don't fail the parent operation
  • Uptime calculation: group AppStatus records by time windows, calculate online/(online+offline) percentage
  • Tag colors: store as hex string (e.g., '#ef4444')

Review Checklist

  • All tasks completed
  • Code follows project conventions
  • No unintended side effects
  • Build passes (Big Bang: code quality check only)
  • Tests pass (Big Bang: skipped for intermediate phase)

Handoff to Next Phase

What was done

  • Created 7 new backend services: favoriteService, recentAppsService, uptimeService, notificationService, tagService, apiTokenService, auditLogService
  • Extended appService with multi-URL link management (addAppLink, updateAppLink, removeAppLink, reorderAppLinks, getAppLinks) and eager-loaded links in findAll/findById
  • Created 16 new API route files across favorites, recent-apps, uptime, notifications (+ channels + test), tags, app tags, app links, tokens, admin audit-log
  • Updated authenticate.ts middleware with extractBearerToken helper
  • Updated hooks.server.ts to validate API tokens from Authorization header as fallback when no JWT session exists
  • Updated healthcheckScheduler.ts to track status transitions and broadcast notifications on status changes (online/offline/degraded), plus added daily audit log pruning cron job
  • Added audit logging calls to 8 existing route files: users CRUD, apps CRUD, boards CRUD, admin settings, admin import, admin export

What the next phase needs to know

  • All new API endpoints require authentication via JWT cookie or Bearer API token
  • Favorites and recent apps are per-user, isolated by userId
  • Notification dispatchers are fire-and-forget — they catch errors and never throw
  • Audit logging is non-blocking (void return, catches errors internally)
  • API token validation iterates all tokens to bcrypt-compare; at scale this could be slow (consider indexing on a prefix for optimization)
  • The broadcastNotification() function sends to all users with enabled channels — used by healthcheck scheduler
  • Uptime stats return null for uptimePercentage/avgResponseTime when no data exists
  • Tags use the AppTag junction table (not the legacy comma-separated App.tags field)
  • App links are eager-loaded in appService.findAll() and findById() queries
  • Audit log pruning runs daily at midnight with 90-day default retention

Potential concerns

  • API token validation scans all tokens in the database for bcrypt comparison. For large numbers of tokens, consider a two-step lookup (store a non-secret prefix for indexing, then bcrypt the full token).
  • The healthcheck scheduler tracks previous statuses in memory (Map). On server restart, the first check after restart will not detect transitions since the map is empty.
  • Notification channel configs are stored as JSON strings — the dispatcher trusts the shape after JSON.parse. Invalid configs are silently skipped.