1c0a7cb850
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.
190 lines
11 KiB
Markdown
190 lines
11 KiB
Markdown
# Phase 5: Functional Features — Backend
|
|
|
|
**Status:** ✅ Complete
|
|
**Parent plan:** [PLAN.md](./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
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/favorites/+server.ts` — GET (list), POST (add), DELETE (remove)
|
|
- [x] Create `src/routes/api/favorites/reorder/+server.ts` — PATCH (reorder)
|
|
|
|
### 5.2 Recent Apps Service & API
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/recent-apps/+server.ts` — GET (list), POST (record click), DELETE (clear)
|
|
|
|
### 5.3 Uptime Dashboard Service & API
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/uptime/+server.ts` — GET all apps uptime summary
|
|
- [x] Create `src/routes/api/uptime/[appId]/+server.ts` — GET single app uptime + timeline
|
|
|
|
### 5.4 Notifications Service & API
|
|
|
|
- [x] 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)`
|
|
- [x] Create `src/routes/api/notifications/+server.ts` — GET (list), PATCH (mark read)
|
|
- [x] Create `src/routes/api/notifications/channels/+server.ts` — CRUD for notification channels
|
|
- [x] Create `src/routes/api/notifications/channels/[id]/+server.ts` — single channel operations
|
|
- [x] Create `src/routes/api/notifications/channels/[id]/test/+server.ts` — POST to send test notification
|
|
- [x] 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
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/tags/+server.ts` — GET (list), POST (create)
|
|
- [x] Create `src/routes/api/tags/[id]/+server.ts` — PATCH (update), DELETE (delete)
|
|
- [x] 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
|
|
|
|
- [x] 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)
|
|
- [x] Create `src/routes/api/apps/[id]/links/+server.ts` — CRUD for app links
|
|
- [x] Update existing app GET endpoints to include links in response
|
|
|
|
### 5.7 API Tokens Service & API
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/tokens/+server.ts` — GET (list), POST (generate)
|
|
- [x] Create `src/routes/api/tokens/[id]/+server.ts` — DELETE (revoke)
|
|
- [x] 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
|
|
|
|
- [x] 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
|
|
- [x] Create `src/routes/api/admin/audit-log/+server.ts` — GET (list, admin only)
|
|
- [x] 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()`
|
|
- [x] 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
|
|
|
|
- [x] All tasks completed
|
|
- [x] 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.
|