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.
696 lines
28 KiB
Markdown
696 lines
28 KiB
Markdown
# Web App Launcher — Implementation Plan Prompt
|
|
|
|
## Project Overview
|
|
|
|
Build a **self-hosted web application launcher / dashboard** for a TrueNAS server environment. The app serves as a centralized portal to organize, discover, and navigate to self-hosted services (Plex, Nextcloud, Gitea, Home Assistant, etc.) via customizable boards with live health indicators.
|
|
|
|
**Repository:** `https://git.dolgolyov-family.by/alexei.dolgolyov/web-app-launcher`
|
|
**Git user:** `alexei.dolgolyov` / `dolgolyov.alexei@gmail.com` (Gitea instance)
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
### Framework (Full-Stack)
|
|
|
|
- **SvelteKit** — all-in-one framework: SSR, routing, API routes (`+server.ts`), form actions — single process, no separate backend needed
|
|
- **Svelte 5** (runes mode) — `$state`, `$derived`, `$effect` for reactive state; compiler-based, no virtual DOM, minimal runtime
|
|
- **TypeScript** — strict mode throughout
|
|
|
|
### UI & Styling
|
|
|
|
- **Tailwind CSS v4** — utility-first styling with smooth animation support
|
|
- **shadcn-svelte** (Bits UI primitives) — accessible, unstyled component library; each component is a separate file
|
|
- **Svelte built-in transitions** — `transition:`, `animate:`, `in:/out:` directives for page transitions, expand/collapse, hover effects
|
|
- **svelte/motion** — `tweened` and `spring` stores for ambient background animations
|
|
|
|
### Data & State
|
|
|
|
- **SvelteKit load functions** — server-side data loading with automatic invalidation
|
|
- **Svelte runes** (`$state`, `$derived`) — client-side reactive state (theme, sidebar, UI)
|
|
- **Superforms + Zod** — type-safe form handling with progressive enhancement and server-side validation
|
|
- **Prisma ORM** — type-safe database access, migrations, seeding
|
|
- **SQLite** — zero-config, file-based, perfect for single-server deployment (easy Docker volume mount, simple backups). Migrate to PostgreSQL later if needed.
|
|
|
|
### Auth
|
|
|
|
- **openid-client** — Authentik OIDC/OAuth2 integration
|
|
- **bcrypt + JWT** — local auth with refresh token rotation via HTTP-only cookies
|
|
- **SvelteKit hooks** (`handle`) — auth middleware, session management
|
|
|
|
### Icons
|
|
|
|
- **Lucide Svelte** — 1500+ clean SVG icons for UI chrome
|
|
- **Simple Icons** (via `simple-icons` npm package) — 3000+ brand/service icons (Plex, Nextcloud, Docker, Grafana, etc.) — perfect for self-hosted app logos
|
|
- **Dashboard Icons** (CDN fallback) — community-maintained self-hosted app icon set
|
|
- **Custom upload** — allow users to upload SVG/PNG icons as a fallback
|
|
- **No emojis** — strictly SVG/image icons only
|
|
|
|
### Background Jobs
|
|
|
|
- **node-cron** — scheduled healthcheck pings (runs in SvelteKit server process)
|
|
|
|
### DevOps
|
|
|
|
- **Docker** — multi-stage build (SvelteKit build → Node adapter → lightweight runtime)
|
|
- **docker-compose.yml** — single-command deployment with volume mounts for SQLite + uploads
|
|
- **Gitea Actions** — CI/CD workflows (lint, type-check, test, Docker image push to Gitea Container Registry)
|
|
|
|
---
|
|
|
|
## Functional Requirements
|
|
|
|
### 1. Authentication & Authorization
|
|
|
|
#### Auth Modes (Admin-Configurable)
|
|
|
|
The system supports three auth modes, selectable by admin in settings:
|
|
|
|
- **OAuth only** — all users authenticate via Authentik (OIDC/OAuth2)
|
|
- **Local only** — email/password login with optional registration
|
|
- **Both** — user chooses OAuth or local login on the login page
|
|
- **Guest mode** — unauthenticated users see boards marked as `guest-accessible`
|
|
|
|
#### User Management
|
|
|
|
- Admin can create/edit/delete users manually
|
|
- Self-registration is **disabled by default**; admin toggles it on/off
|
|
- OAuth auto-provisions users on first login (maps Authentik groups to local groups)
|
|
- Users have: `id`, `email`, `displayName`, `avatarUrl`, `authProvider`, `role`, `groupIds[]`
|
|
|
|
#### Groups & Access Control
|
|
|
|
- **Default groups:** `admin`, `user`
|
|
- Admin can create custom groups
|
|
- Permissions are hierarchical: **User-level overrides > Group-level > Default**
|
|
- Permission model per entity (board, section, app):
|
|
- `view` — can see the entity
|
|
- `edit` — can modify the entity
|
|
- `admin` — full control (delete, manage access)
|
|
- Guest access is a separate boolean flag per board
|
|
|
|
#### Session Management
|
|
|
|
- JWT access tokens stored in HTTP-only cookies (managed by SvelteKit hooks)
|
|
- Refresh token rotation (7-day expiry)
|
|
- Server-side session validation in `hooks.server.ts`
|
|
- Logout invalidates refresh token
|
|
|
|
### 2. Apps (Service Registry)
|
|
|
|
Each app represents a self-hosted service:
|
|
|
|
- **url** (required) — base URL of the service
|
|
- **name** (required) — display name
|
|
- **icon** — one of: Simple Icons slug, Lucide icon name, Dashboard Icons ID, or uploaded image path
|
|
- **description** (optional) — short text shown on hover/expand
|
|
- **category/tags** (optional) — for filtering/search
|
|
- **healthcheck** configuration:
|
|
- `enabled` (default: true)
|
|
- `interval` (default: 60s, min: 10s, max: 3600s)
|
|
- `method` — HTTP HEAD/GET to the URL (or custom endpoint)
|
|
- `expectedStatus` — default 200, configurable (e.g., 401 is "alive" for auth-protected services)
|
|
- `timeout` — max wait before marking as down (default: 5s)
|
|
- **Status**: `online | offline | degraded | unknown` — derived from healthcheck results
|
|
- Backend runs healthchecks on a per-app schedule; results are cached and pushed to frontend via polling (SvelteKit invalidation) or SSE
|
|
|
|
### 3. Boards
|
|
|
|
Boards are the primary organizational unit — each board is a full-page layout of sections and widgets.
|
|
|
|
#### Board Properties
|
|
|
|
- `name`, `icon`, `description`
|
|
- `accessLevel` — per-user, per-group, guest-accessible (boolean)
|
|
- `isDefault` — one board can be marked as the landing page
|
|
- `layout` — grid-based responsive layout
|
|
- `backgroundConfig` — ambient background settings (see Appearance)
|
|
|
|
#### Sections (Groups)
|
|
|
|
- Collapsible/expandable containers within a board
|
|
- Properties: `title`, `icon`, `isExpanded` (default state), `order`
|
|
- Contain an ordered list of widgets
|
|
- Smooth expand/collapse animation (Svelte `slide` transition)
|
|
|
|
#### Widgets
|
|
|
|
Widgets are the atomic content units inside sections.
|
|
|
|
**App Widget (MVP):**
|
|
|
|
- Displays app icon, name, status indicator (colored dot/ring), optional description
|
|
- Click opens the app URL in a new tab
|
|
- Hover shows description tooltip + last healthcheck time
|
|
- Visual states: online (green pulse), offline (red), degraded (yellow), unknown (gray)
|
|
|
|
**Future widget types (post-MVP, design the schema to support them):**
|
|
|
|
- **Bookmark widget** — simple URL + label (no healthcheck)
|
|
- **Note widget** — rich text or markdown note
|
|
- **Embed widget** — iframe embed of a service
|
|
- **Status widget** — aggregated status of multiple apps
|
|
|
|
### 4. Search & Navigation
|
|
|
|
- Global search bar (Cmd/Ctrl+K) — searches across all accessible apps and boards
|
|
- Keyboard navigation support
|
|
- Sidebar with board list (collapsible)
|
|
- Breadcrumb navigation within nested views
|
|
|
|
### 5. Admin Panel
|
|
|
|
- User management (CRUD, group assignment)
|
|
- Group management (CRUD, permission templates)
|
|
- App management (CRUD, healthcheck config, bulk import/export)
|
|
- Board management (CRUD, access control)
|
|
- System settings:
|
|
- Auth mode selection
|
|
- Registration toggle
|
|
- OAuth provider configuration (client ID, secret, discovery URL)
|
|
- Default theme / primary color
|
|
- Healthcheck global defaults
|
|
|
|
---
|
|
|
|
## Non-Functional Requirements
|
|
|
|
### Appearance & UX
|
|
|
|
- **Modern, clean design** — inspired by Homarr / Heimdall / Organizr but with smoother polish
|
|
- **Ambient animated backgrounds** — subtle mesh gradient, particle field, or aurora effect (configurable, can be disabled); implemented with Svelte `tweened`/`spring` + CSS/Canvas
|
|
- **Customizable primary color** — HSL-based theme system; admin sets default, users can override
|
|
- **Dark / Light / System theme** — with smooth CSS transition
|
|
- **Smooth animations everywhere** (using Svelte built-in transitions):
|
|
- Page transitions (`in:fade`, `out:fly`)
|
|
- Section expand/collapse (`transition:slide`)
|
|
- Card hover effects (subtle scale + shadow lift via CSS + Svelte `spring`)
|
|
- Status indicator pulse (CSS `@keyframes`)
|
|
- Skeleton loading states
|
|
- **Responsive** — works on desktop, tablet, and mobile
|
|
- **No emojis as icons** — use SVG icon libraries exclusively
|
|
|
|
### File Structure (Modularity)
|
|
|
|
SvelteKit route-based structure with one component per file:
|
|
|
|
```
|
|
src/
|
|
routes/
|
|
+layout.svelte # Root layout (sidebar, header, ambient bg)
|
|
+layout.server.ts # Root auth check, load user session
|
|
+page.svelte # Dashboard / default board redirect
|
|
login/
|
|
+page.svelte # Login page
|
|
+page.server.ts # Login form action
|
|
register/
|
|
+page.svelte # Registration page (if enabled)
|
|
+page.server.ts # Register form action
|
|
auth/
|
|
oauth/
|
|
authorize/+server.ts # Redirect to Authentik
|
|
callback/+server.ts # Handle OAuth callback
|
|
refresh/+server.ts # Token refresh
|
|
boards/
|
|
+page.svelte # Board list
|
|
+page.server.ts # Load boards (filtered by permissions)
|
|
[boardId]/
|
|
+page.svelte # Board view
|
|
+page.server.ts # Load board with sections/widgets
|
|
edit/
|
|
+page.svelte # Board editor
|
|
+page.server.ts # Board update actions
|
|
apps/
|
|
+page.svelte # App registry list
|
|
+page.server.ts # Load apps
|
|
[appId]/
|
|
+server.ts # App CRUD API
|
|
status/+server.ts # Healthcheck status API
|
|
admin/
|
|
+layout.svelte # Admin layout (admin-only guard)
|
|
+layout.server.ts # Admin auth check
|
|
users/
|
|
+page.svelte # User management
|
|
+page.server.ts # User CRUD actions
|
|
groups/
|
|
+page.svelte # Group management
|
|
+page.server.ts # Group CRUD actions
|
|
settings/
|
|
+page.svelte # System settings
|
|
+page.server.ts # Settings update actions
|
|
api/
|
|
apps/+server.ts # App CRUD REST endpoints
|
|
apps/[id]/+server.ts # Single app operations
|
|
apps/[id]/status/+server.ts # Healthcheck status
|
|
boards/+server.ts # Board CRUD
|
|
boards/[id]/+server.ts # Single board operations
|
|
boards/[id]/sections/+server.ts # Section CRUD
|
|
boards/[id]/sections/[sid]/+server.ts # Single section
|
|
boards/[id]/sections/[sid]/widgets/+server.ts # Widget CRUD
|
|
users/+server.ts # User management (admin)
|
|
groups/+server.ts # Group management (admin)
|
|
admin/settings/+server.ts # System settings (admin)
|
|
search/+server.ts # Global search
|
|
health/+server.ts # App healthcheck endpoint
|
|
lib/
|
|
components/
|
|
ui/ # shadcn-svelte primitives (Button, Dialog, etc.)
|
|
layout/
|
|
Sidebar.svelte
|
|
Header.svelte
|
|
MainLayout.svelte
|
|
ThemeToggle.svelte
|
|
board/
|
|
Board.svelte
|
|
BoardHeader.svelte
|
|
BoardCard.svelte
|
|
section/
|
|
Section.svelte
|
|
SectionHeader.svelte
|
|
SectionCollapsible.svelte
|
|
widget/
|
|
AppWidget.svelte
|
|
AppWidgetStatus.svelte
|
|
WidgetContainer.svelte
|
|
WidgetGrid.svelte
|
|
app/
|
|
AppForm.svelte
|
|
AppIconPicker.svelte
|
|
AppHealthBadge.svelte
|
|
AppCard.svelte
|
|
auth/
|
|
LoginForm.svelte
|
|
OAuthButton.svelte
|
|
RegisterForm.svelte
|
|
admin/
|
|
UserTable.svelte
|
|
GroupTable.svelte
|
|
SettingsForm.svelte
|
|
PermissionEditor.svelte
|
|
search/
|
|
SearchDialog.svelte
|
|
SearchResult.svelte
|
|
SearchTrigger.svelte
|
|
background/
|
|
AmbientBackground.svelte
|
|
MeshGradient.svelte
|
|
ParticleField.svelte
|
|
AuroraEffect.svelte
|
|
server/
|
|
services/
|
|
authService.ts
|
|
appService.ts
|
|
boardService.ts
|
|
healthcheckService.ts
|
|
userService.ts
|
|
groupService.ts
|
|
permissionService.ts
|
|
middleware/
|
|
authenticate.ts
|
|
authorize.ts
|
|
guestAccess.ts
|
|
jobs/
|
|
healthcheckScheduler.ts
|
|
utils/
|
|
jwt.ts
|
|
password.ts
|
|
iconResolver.ts
|
|
stores/
|
|
theme.svelte.ts # Svelte 5 rune-based store
|
|
ui.svelte.ts
|
|
search.svelte.ts
|
|
utils/
|
|
constants.ts
|
|
validators.ts
|
|
helpers.ts
|
|
types/
|
|
auth.ts
|
|
app.ts
|
|
board.ts
|
|
widget.ts
|
|
user.ts
|
|
group.ts
|
|
permission.ts
|
|
hooks.server.ts # Auth middleware, session injection
|
|
hooks.client.ts # Client-side error handling
|
|
app.css # Tailwind base + theme variables
|
|
app.d.ts # SvelteKit type augmentation (locals, session)
|
|
prisma/
|
|
schema.prisma
|
|
migrations/
|
|
seed.ts
|
|
static/
|
|
uploads/ # User-uploaded icons
|
|
```
|
|
|
|
### Database Schema (Prisma)
|
|
|
|
Key models:
|
|
|
|
- `User` — id, email, password (nullable for OAuth), displayName, avatarUrl, authProvider, role
|
|
- `Group` — id, name, description, isDefault
|
|
- `UserGroup` — userId, groupId (many-to-many)
|
|
- `App` — id, name, url, icon, iconType, description, category, healthcheckEnabled, healthcheckInterval, healthcheckMethod, healthcheckExpectedStatus, healthcheckTimeout, createdById
|
|
- `AppStatus` — id, appId, status, responseTime, checkedAt (latest N records for history)
|
|
- `Board` — id, name, icon, description, isDefault, isGuestAccessible, backgroundConfig (JSON), createdById
|
|
- `Section` — id, boardId, title, icon, order, isExpandedByDefault
|
|
- `Widget` — id, sectionId, type, order, config (JSON — for AppWidget: `{ appId }`)
|
|
- `Permission` — id, entityType (board/app), entityId, targetType (user/group), targetId, level (view/edit/admin)
|
|
- `SystemSettings` — singleton row: authMode, registrationEnabled, oauthConfig (encrypted JSON), defaultTheme, defaultPrimaryColor, healthcheckDefaults (JSON)
|
|
|
|
### API Design
|
|
|
|
RESTful JSON API via SvelteKit `+server.ts` routes with consistent envelope:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { ... },
|
|
"error": null,
|
|
"meta": { "total": 100, "page": 1, "limit": 20 }
|
|
}
|
|
```
|
|
|
|
Key endpoints (all under `src/routes/api/`):
|
|
|
|
- `POST /api/auth/login` — local login
|
|
- `GET /api/auth/oauth/authorize` — redirect to Authentik
|
|
- `GET /api/auth/oauth/callback` — handle OAuth callback
|
|
- `POST /api/auth/register` — self-registration (if enabled)
|
|
- `POST /api/auth/refresh` — token refresh
|
|
- `GET/POST/PATCH/DELETE /api/apps` — app CRUD
|
|
- `GET /api/apps/:id/status` — latest healthcheck status
|
|
- `GET/POST/PATCH/DELETE /api/boards` — board CRUD (filtered by user permissions)
|
|
- `GET/POST/PATCH/DELETE /api/boards/:id/sections` — section CRUD
|
|
- `GET/POST/PATCH/DELETE /api/boards/:id/sections/:sid/widgets` — widget CRUD
|
|
- `GET/POST/PATCH/DELETE /api/users` — user management (admin)
|
|
- `GET/POST/PATCH/DELETE /api/groups` — group management (admin)
|
|
- `GET/PATCH /api/admin/settings` — system settings (admin)
|
|
- `GET /api/search?q=...` — global search
|
|
- `GET /api/health` — app health endpoint for Docker healthcheck
|
|
|
|
### Docker Deployment
|
|
|
|
```dockerfile
|
|
# Multi-stage: build SvelteKit with Node adapter → slim runtime
|
|
FROM node:22-alpine AS builder
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci
|
|
COPY . .
|
|
RUN npx prisma generate
|
|
RUN npm run build
|
|
|
|
FROM node:22-alpine AS runtime
|
|
WORKDIR /app
|
|
COPY --from=builder /app/build ./build
|
|
COPY --from=builder /app/node_modules ./node_modules
|
|
COPY --from=builder /app/package.json ./
|
|
COPY --from=builder /app/prisma ./prisma
|
|
EXPOSE 3000
|
|
HEALTHCHECK CMD wget -q --spider http://localhost:3000/api/health || exit 1
|
|
ENTRYPOINT ["sh", "-c", "npx prisma migrate deploy && node build"]
|
|
```
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
web-app-launcher:
|
|
build: .
|
|
ports:
|
|
- '3000:3000'
|
|
volumes:
|
|
- ./data:/app/data # SQLite DB
|
|
- ./uploads:/app/uploads # Custom icons
|
|
environment:
|
|
- DATABASE_URL=file:/app/data/launcher.db
|
|
- JWT_SECRET=changeme
|
|
- OAUTH_CLIENT_ID=
|
|
- OAUTH_CLIENT_SECRET=
|
|
- OAUTH_DISCOVERY_URL=
|
|
- ORIGIN=http://localhost:3000
|
|
restart: unless-stopped
|
|
```
|
|
|
|
### CI/CD (Gitea Actions)
|
|
|
|
`.gitea/workflows/ci.yml`:
|
|
|
|
- **On push to any branch:** lint (`eslint`), type-check (`svelte-check`), unit tests (`vitest`)
|
|
- **On push to main:** build Docker image, push to Gitea Container Registry (`git.dolgolyov-family.by/alexei.dolgolyov/web-app-launcher`)
|
|
- **On tag (vX.Y.Z):** build + push tagged image, create Gitea release
|
|
|
|
---
|
|
|
|
## Additional Features
|
|
|
|
### Cool Ideas (Included in Phases)
|
|
|
|
1. **Drag-and-drop board editor** — reorder sections and widgets with `svelte-dnd-action` (Svelte-native, accessible, performant)
|
|
2. **Auto-discovery** — scan a Docker socket or Traefik API to auto-register running containers as apps
|
|
3. **Favicon auto-fetch** — if no icon is selected, attempt to fetch the favicon from the app's URL
|
|
4. **Ping history sparkline** — tiny inline SVG chart showing uptime over last 24h per app
|
|
5. **Import/Export** — JSON export of entire config for backup/migration
|
|
6. **PWA support** — installable on mobile home screen with offline shell (SvelteKit service worker support)
|
|
7. **Multi-tab sync** — broadcast theme/board changes across tabs via BroadcastChannel API
|
|
8. **Quick-add bookmarklet** — browser bookmarklet that adds current page as an app via the API
|
|
9. **Keyboard-first navigation** — Vim-style `j/k` to move between apps, `Enter` to open
|
|
|
|
### MVP Scope (Phase 1)
|
|
|
|
To avoid scope creep, the MVP should include:
|
|
|
|
- Local auth + guest mode (OAuth in Phase 2)
|
|
- App CRUD + healthcheck with status display
|
|
- Single default board with sections and app widgets
|
|
- Admin panel (user/app/board management)
|
|
- Dark theme + ambient background
|
|
- Docker deployment
|
|
- Basic Gitea CI
|
|
|
|
### Phase 2
|
|
|
|
- OAuth/Authentik integration
|
|
- Multi-board support with per-board access control
|
|
- Custom groups + granular permissions
|
|
- Drag-and-drop reordering
|
|
- Global search (Cmd+K)
|
|
- Additional widget types
|
|
|
|
### Phase 3 (DONE)
|
|
|
|
- ~~Auto-discovery (Docker/Traefik)~~ **DONE**
|
|
- ~~Import/Export~~ **DONE**
|
|
- ~~PWA~~ **DONE**
|
|
- ~~Ping history sparklines~~ **DONE**
|
|
- ~~User theme overrides~~ **DONE**
|
|
- ~~Quick-add bookmarklet~~ **DONE**
|
|
- ~~Multi-tab sync~~ **DONE**
|
|
|
|
### Phase 4 — New Widget Types
|
|
|
|
New widget types to expand dashboard capabilities beyond app launching:
|
|
|
|
1. **Clock / Weather Widget**
|
|
- Local time display with configurable timezone
|
|
- Optional weather via free OpenMeteo API (no API key required)
|
|
- Analog or digital clock face, minimal design
|
|
- Config: `{ timezone: string, showWeather: boolean, latitude?: number, longitude?: number, clockStyle: 'analog' | 'digital' }`
|
|
|
|
2. **System Stats Widget**
|
|
- CPU, RAM, disk usage pulled from TrueNAS API or Glances API
|
|
- Tiny gauge/donut charts with auto-refresh
|
|
- Threshold coloring: green (< 60%) → yellow (60-85%) → red (> 85%)
|
|
- Config: `{ sourceUrl: string, sourceType: 'truenas' | 'glances' | 'custom', metrics: string[], refreshInterval: number }`
|
|
|
|
3. **RSS/Feed Widget**
|
|
- Subscribe to any RSS/Atom feed (release notes, changelogs, security advisories)
|
|
- Shows latest N items with title + date, expandable to show summary
|
|
- Config: `{ feedUrl: string, maxItems: number, showSummary: boolean }`
|
|
|
|
4. **Calendar Widget**
|
|
- iCal URL subscription (Nextcloud, Google Calendar, etc.)
|
|
- Compact list of today's + upcoming events
|
|
- Color-coded by calendar source
|
|
- Config: `{ icalUrls: Array<{ url: string, color: string, label: string }>, daysAhead: number }`
|
|
|
|
5. **Markdown Widget** (upgrade from existing Note widget)
|
|
- Full markdown rendering with syntax highlighting (via `shiki` or `highlight.js`)
|
|
- Live preview split-pane edit mode
|
|
- Useful for runbooks, quick-reference docs, IP tables, cheat sheets
|
|
- Config: `{ content: string, syntaxTheme: string }`
|
|
|
|
6. **Metric/Counter Widget**
|
|
- Single big number with label (e.g., "12 containers running", "99.7% uptime")
|
|
- Data source: static value, HTTP JSON endpoint + JSONPath, or Prometheus PromQL query
|
|
- Trend arrow (up/down/flat vs last poll)
|
|
- Config: `{ label: string, source: 'static' | 'http' | 'prometheus', value?: string, url?: string, jsonPath?: string, query?: string, unit?: string, refreshInterval: number }`
|
|
|
|
7. **Link Group Widget**
|
|
- Compact list of related URLs (lighter than full app cards)
|
|
- Example: "Documentation" group with links to wikis, API docs, Swagger pages
|
|
- Collapsible, optional numbering and icons per link
|
|
- Config: `{ links: Array<{ label: string, url: string, icon?: string }>, collapsible: boolean }`
|
|
|
|
8. **Camera/Stream Widget**
|
|
- MJPEG or HLS stream thumbnail from security cameras or media servers
|
|
- Click to open fullscreen stream in modal or new tab
|
|
- Auto-refresh snapshot at configurable interval
|
|
- Config: `{ streamUrl: string, type: 'mjpeg' | 'hls' | 'snapshot', refreshInterval: number, aspectRatio: string }`
|
|
|
|
### Phase 5 — Visual & Styling Enhancements
|
|
|
|
Polish the visual experience with advanced theming and card styles:
|
|
|
|
1. **Glassmorphism Card Style**
|
|
- Frosted glass effect on cards (`backdrop-filter: blur(12px)` + semi-transparent bg)
|
|
- Ambient background effect bleeds through cards
|
|
- Toggle between `solid` / `glass` / `outline` card styles in theme settings
|
|
- Applies globally or per-board
|
|
|
|
2. **Board-Level Themes**
|
|
- Each board gets its own color accent (hue/saturation) + background effect
|
|
- Example: "Work" = blue + mesh gradient, "Media" = purple + aurora, "Infra" = green + particles
|
|
- Board theme overrides global theme when viewing that board
|
|
- Smooth transition when switching boards
|
|
- Schema: add `themeHue`, `themeSaturation`, `backgroundType` to Board model
|
|
|
|
3. **Animated Status Ring**
|
|
- Replace the static status dot with an SVG ring around the app icon
|
|
- Online = animated green fill sweep, Offline = pulsing red ring, Degraded = partial yellow arc, Unknown = gray dashed
|
|
- More visually striking, scales well with different card sizes
|
|
|
|
4. **Card Size Options**
|
|
- Three sizes: `compact` (icon + name), `medium` (current), `large` (icon + name + description + sparkline + tags)
|
|
- Configurable per-section or per-board
|
|
- Responsive: auto-downsizes on mobile
|
|
- Schema: add `cardSize` to Section and Board models
|
|
|
|
5. **Custom CSS Injection**
|
|
- Admin-level custom CSS textarea in system settings
|
|
- Per-board CSS overrides field
|
|
- Sanitized (strip `<script>`, limit selectors to app scope)
|
|
- Power users can tweak anything without touching source code
|
|
|
|
6. **Wallpaper Backgrounds**
|
|
- Upload custom image as board background (in addition to procedural effects)
|
|
- Options: blur amount, overlay opacity, parallax scroll, fixed/scroll position
|
|
- Optional Unsplash integration: random daily wallpaper from a user-defined collection (requires free API key)
|
|
- Schema: add `wallpaperUrl`, `wallpaperBlur`, `wallpaperOverlay` to Board backgroundConfig
|
|
|
|
### Phase 6 — Functional Features
|
|
|
|
New capabilities that improve daily usage and operational value:
|
|
|
|
1. **Pinned / Favorites Bar**
|
|
- Users pin most-used apps to a persistent "Favorites" bar at the top of every board
|
|
- Per-user, survives board navigation
|
|
- Drag-and-drop reordering within the favorites bar
|
|
- Schema: new `UserFavorite` model (userId, appId, order)
|
|
|
|
2. **Recent Apps**
|
|
- Track which apps each user clicks (last 10)
|
|
- Auto-generated "Recently Used" section at top of default board
|
|
- Privacy toggle: users can disable click tracking in their settings
|
|
- Schema: new `AppClick` model (userId, appId, clickedAt)
|
|
|
|
3. **Uptime Dashboard Page**
|
|
- Dedicated `/status` public page showing all app statuses
|
|
- Time range selector: 24h / 7d / 30d uptime percentages
|
|
- Incident timeline: when apps went down, how long, recovery time
|
|
- Can be shared as a team/family status page (guest-accessible, separate from boards)
|
|
- Larger sparkline charts with hover tooltips showing exact timestamps
|
|
|
|
4. **Notifications System**
|
|
- In-app toast notifications when a monitored app goes offline/online
|
|
- Webhook integrations: Discord, Slack, Telegram, generic HTTP POST
|
|
- Notification preferences per user: which apps to watch, which channels to use
|
|
- Notification history page with read/unread state
|
|
- Schema: new `NotificationChannel` model (userId, type, config), `Notification` model (userId, appId, event, sentAt, readAt)
|
|
|
|
5. **App Tags + Filtering**
|
|
- Tag apps with labels: `media`, `infra`, `dev`, `monitoring`, etc.
|
|
- Filter bar within boards to show/hide apps by tag
|
|
- Tag-based auto-sections: dynamically group apps by tag
|
|
- Tag management in admin panel (CRUD, color per tag)
|
|
- Schema: new `Tag` model, `AppTag` many-to-many
|
|
|
|
6. **Multi-URL Apps**
|
|
- Some services have multiple entry points (Grafana dashboards, Portainer envs, Proxmox nodes)
|
|
- App card expands on hover/click to reveal sub-links
|
|
- Primary URL (main click) + secondary URLs list with labels
|
|
- Schema: new `AppLink` model (appId, label, url, order)
|
|
|
|
7. **~~Two-Factor Authentication (TOTP)~~** _(DEFERRED — not in current scope)_
|
|
- Optional 2FA for local accounts using TOTP (Google Authenticator / Authy compatible)
|
|
- QR code setup flow in user settings with `otpauth://` URI
|
|
- Backup codes (one-time use, 10 generated at setup)
|
|
- Enforced 2FA option for admin accounts
|
|
- Schema: add `totpSecret`, `totpEnabled`, `backupCodes` to User model
|
|
|
|
8. **Personal API Tokens**
|
|
- Generate API tokens in user settings for programmatic access
|
|
- Scoped permissions: `read`, `write`, `admin`
|
|
- Token listing with last-used timestamp, revocation
|
|
- Used by external scripts, CLI tools, or future mobile apps
|
|
- Schema: new `ApiToken` model (userId, name, tokenHash, scope, lastUsedAt, expiresAt)
|
|
|
|
9. **Audit Log**
|
|
- Track admin actions: user created/deleted, board modified, settings changed, apps imported
|
|
- Viewable in admin panel with date range + action type filters
|
|
- Retention policy configurable: 30/60/90 days (auto-pruned by cron job)
|
|
- Schema: new `AuditLog` model (userId, action, entityType, entityId, details JSON, createdAt)
|
|
|
|
### Phase 7 — Quality of Life
|
|
|
|
Onboarding, discoverability, and power-user conveniences:
|
|
|
|
1. **Onboarding Wizard**
|
|
- Triggered on first launch (no users exist in DB)
|
|
- Steps: create admin account → configure auth mode → pick theme + background → add first apps (manual or auto-discover) → create first board
|
|
- Progress indicator, skippable steps for advanced users
|
|
- Stores completion flag in SystemSettings
|
|
|
|
2. **App URL Health Preview**
|
|
- When adding/editing an app, fetch the URL server-side and show:
|
|
- HTTP status code + response time
|
|
- Auto-detected favicon (if no icon chosen)
|
|
- Page title extraction for auto-filling app name
|
|
- "Test Connection" button in the app form
|
|
- Reuse existing healthcheckService logic
|
|
|
|
3. **Board Templates**
|
|
- Pre-built board layouts shipped with the app:
|
|
- "Home Server" (sections: Media, Networking, Storage, Monitoring)
|
|
- "Media Stack" (sections: Streaming, Downloads, Management)
|
|
- "Dev Tools" (sections: Git, CI/CD, Databases, Docs)
|
|
- "Monitoring" (sections: Metrics, Logs, Alerts, Status)
|
|
- User picks a template when creating a new board → creates board + empty sections
|
|
- Community-shareable: export a board as template JSON, import others' templates
|
|
|
|
4. **Keyboard Shortcut Overlay**
|
|
- Press `?` anywhere to show all available shortcuts in a modal
|
|
- Context-aware: shows different shortcuts on board view vs admin vs search
|
|
- Shortcuts include:
|
|
- `Cmd/Ctrl+K` — search
|
|
- `j/k` — navigate between apps
|
|
- `Enter` — open selected app
|
|
- `e` — edit mode
|
|
- `1-9` — switch to board by number
|
|
- `f` — toggle favorites bar
|
|
- `?` — show this overlay
|
|
- Discoverable: small `?` hint icon in footer
|
|
|
|
---
|
|
|
|
## Constraints & Preferences
|
|
|
|
- **Immutable data patterns** — never mutate objects in place; return new copies
|
|
- **Small files** — one component/service per file, 200-400 lines typical, 800 max
|
|
- **Comprehensive error handling** — every API call, every user action
|
|
- **Input validation** — Zod schemas shared between client (Superforms) and server
|
|
- **No hardcoded secrets** — all sensitive config via environment variables
|
|
- **Conventional commits** — `feat:`, `fix:`, `refactor:`, etc.
|
|
- **80%+ test coverage target** — unit tests with Vitest, integration tests for API routes, E2E with Playwright
|
|
- **Progressive enhancement** — SvelteKit form actions work without JavaScript where possible
|