alexei.dolgolyov 5dcadd1c20 feat(ui): migrate entire UI to "Cozy Home" design
Warm, friendly redesign replacing the generic cold-shadcn look. Built as a
swappable token bundle so other presets can be added later; dark mode and the
user-tunable accent hue are retained.

Foundation
- app.css: warm cream (light) + "dusk" (dark) token system; terracotta accent
  (default hue 16); pastel --room-* palette; vivid --status-* (dots/bars) plus
  AA-legible --status-*-ink (text); soft warm shadows; --radius 1rem; font tokens
- Fonts: Fraunces (display) + Figtree (body), self-hosted in static/fonts
  (no Google CDN) so offline/LAN installs work; system-ui fallbacks kept
- h1/h2/h3 render in Fraunces via base layer

Chrome and surfaces
- Sidebar, Header, home, AppCard/BoardCard, BoardHeader, sections, favorites
- 29 widgets + integration renderers: cozy card shells, room-palette charts
- Default background is a static warm "cozy" glow (mesh demoted, rAF gated on
  prefers-reduced-motion)

System-wide
- Status colors tokenized (no raw bg/text-*-500 or status hex); success/warning
  to status tokens, categorical to room palette, errors to destructive
- Inputs rounded-xl; buttons rounded-xl; cards/dialogs rounded-[1.4rem];
  soft-shadow vocabulary only; focus-visible:ring-primary/30
- Forms, admin tables (now cozy cards), dialogs, popovers, auth screens

a11y: reduced-motion guards; darker status "ink" text for AA on cream.
Known tradeoff: terracotta primary + white button text ~2.96:1 (signature color,
user-tunable).

Verified: svelte-check 0/0, build ok, 274 tests pass, eslint 0 errors.
Design refs + system sheet in design-mockups/.
2026-05-27 23:04:47 +03:00
2026-04-10 19:39:27 +03:00
2026-04-10 19:39:27 +03:00

Web App Launcher

A self-hosted dashboard for organizing, monitoring, and launching web applications. Built with SvelteKit, Prisma (SQLite), and Tailwind CSS.

Features

  • App registry — add apps with icons, tags, and categories; automatic healthcheck monitoring with sparkline history
  • Boards & widgets — customizable dashboards with drag-and-drop, resizable widget columns, and inline WYSIWYG editing
  • Service integrations — connect to media services, Planka, and more to display live data in widgets
  • Authentication — local accounts + OAuth/Authentik; per-board access control; API tokens
  • Localization — English and Russian
  • PWA — installable, multi-tab sync, auto-discovery bookmarklet
  • SQLite backup/restore — full database backup from the admin panel

Quick Start

git clone https://git.dolgolyov-family.by/alexei.dolgolyov/web-app-launcher.git
cd web-app-launcher

# Generate two strong secrets
export JWT_SECRET=$(openssl rand -hex 32)
export INTEGRATION_ENCRYPTION_KEY=$(openssl rand -hex 32)

docker compose up -d

The app is available at http://localhost:3000. On first launch, create an admin account at the setup page.

The launcher refuses to start if JWT_SECRET or INTEGRATION_ENCRYPTION_KEY is missing, shorter than 32 characters, or set to a known placeholder. This is intentional — running with the old change-me-… defaults would let anyone mint admin tokens.

Configuration

Environment variables (set in docker-compose.yml or .env):

Variable Default Description
APP_PORT 3000 Port to expose
JWT_SECRET required Strong secret for JWT signing. Generate with openssl rand -hex 32.
INTEGRATION_ENCRYPTION_KEY required Strong secret for encrypting stored integration credentials. Must differ from JWT_SECRET. Generate with openssl rand -hex 32.
ORIGIN http://localhost:$APP_PORT Public URL users visit. When set to https://..., session cookies are issued with the Secure flag. Set this to your public https URL when behind a reverse proxy.
GUEST_MODE true Allow unauthenticated access to guest-flagged boards
HEALTHCHECK_CRON */5 * * * * App healthcheck interval
HEALTHCHECK_TIMEOUT_MS 5000 Healthcheck request timeout
ALLOW_PRIVATE_NETWORK_FETCH false (true in dev) Allow outbound fetches to RFC1918/loopback/link-local. Self-hosted users monitoring LAN services usually want true. Off by default in prod to mitigate SSRF.
RUN_SCHEDULERS true Run background jobs (healthcheck, backup) in this process. Set false on extra horizontal replicas.
OAUTH_CLIENT_ID OAuth provider client ID
OAUTH_CLIENT_SECRET OAuth provider client secret
OAUTH_DISCOVERY_URL OpenID Connect discovery URL
METRICS_TOKEN Optional bearer token for /api/metrics. Unset = open (private-network setups)

Production deployment

Reverse proxy (Traefik / Caddy / Nginx)

The launcher must know its public URL to issue secure cookies. Set ORIGIN=https://launcher.example.com and terminate TLS at the proxy. Example Traefik labels:

services:
  web-app-launcher:
    # remove `ports:` mapping
    networks: [traefik, launcher-net]
    labels:
      - traefik.enable=true
      - traefik.http.routers.launcher.rule=Host(`launcher.example.com`)
      - traefik.http.routers.launcher.entrypoints=websecure
      - traefik.http.routers.launcher.tls.certresolver=letsencrypt
      - traefik.http.services.launcher.loadbalancer.server.port=3000
    environment:
      - ORIGIN=https://launcher.example.com

Volume backup

docker run --rm \
  -v web-app-launcher_launcher-data:/data \
  -v "$PWD":/backup \
  alpine tar czf /backup/launcher-backup.tar.gz -C /data .

Upgrade

docker compose pull && docker compose up -d

Database migrations run automatically on container start via prisma migrate deploy. The previous db push fallback was removed because it can silently drop columns on schema drift.

Breaking changes when upgrading from versions ≤ 0.0.x

The 0.1.0 hardening release is a one-way upgrade with three breaking changes:

  1. INTEGRATION_ENCRYPTION_KEY is required and must differ from JWT_SECRET. The launcher will refuse to start without it. Previously the integration key was derived from JWT_SECRET; all stored integration credentials (Planka, Authentik, Pi-hole, Portainer, Gitea, Immich, etc.) will be undecryptable after the upgrade and must be re-entered through the admin UI.

  2. All users will be logged out and all API tokens / invites will be revoked. The hardening migration drops the Session, Invite, and ApiToken tables to switch from bcrypt-hashed storage to sha256 (so token validation is O(1) instead of O(N) bcrypt comparisons). Users will need to log in once; admins need to reissue API tokens and pending invites.

  3. Uploaded icons / wallpapers move from static/uploads/ to /app/data/uploads/. This makes them persist across container rebuilds. On upgrade, copy any existing files from your previous static/uploads/ mount into the launcher-data volume:

    # if you previously mounted `./static/uploads:/app/static/uploads`
    docker run --rm \
      -v "$PWD/static/uploads:/src:ro" \
      -v web-app-launcher_launcher-data:/dst \
      alpine sh -c "mkdir -p /dst/uploads && cp -r /src/. /dst/uploads/"
    

Take a backup before upgrading.

Development

npm install
npx prisma generate
# strong dev secrets are already in .env (gitignored)
npm run dev

License

MIT

S
Description
No description provided
Readme 2.9 MiB
0.0.1 Latest
2026-04-10 21:23:00 +03:00
Languages
TypeScript 49.3%
Svelte 42%
HTML 7.1%
CSS 1.2%
JavaScript 0.2%
Other 0.2%