Files
xtremflow/Theme/login_xtremflow_redesign/code.html
T
MichaelandClaude Fable 5 60d3f42901 feat: security hardening, streaming overhaul, design polish, tests
Security:
- Replace unsalted SHA-256 password hashing with bcrypt (lazy rehash on login)
- Add authenticated /api/xtream-api gateway: Xtream credentials are injected
  server-side and never sent to the frontend; /api/playlists no longer
  returns passwords
- Redact credentials from all logs (login body, proxy/FFmpeg/scheduler URLs)
- Add auth to recordings, EPG, season-passes and streaming routes
  (HttpOnly session cookie for hls.js; loopback bypass for local FFmpeg)
- Lock player postMessage to same-origin in both directions
- Vendor and pin hls.js 1.6.7 / mpegts.js 1.7.3 (drop CDN @latest)
- Fix rate limiter (client IP was never resolved), add login rate limit,
  restrict CORS, add CSP Report-Only, block private-IP SSRF targets,
  fix path traversal in recording log retrieval, chmod 777 -> 770
- Remove dead HiveService (seeded admin/admin into IndexedDB with SHA-256)
- Fix authMiddleware not populating 'user' context (getPlaylist ignored the
  logged-in user; admin purge always returned 403)

Streaming:
- New FfmpegSessionManager: process registry, idle reaper (4 min live /
  15 min VOD), orphan cleanup at startup, clean SIGTERM shutdown,
  fast-fail with stderr instead of 30 s timeout
- Quality selection (source/high/medium/low) for live and VOD; source mode
  streams with -c:v copy (zero transcoding); selector wired into the player
- Concurrent recordings (MAX_CONCURRENT_RECORDINGS, default 2); conflicts
  retry on the next tick instead of silently failing
- Lower live latency (HLS window 20 -> 10 segments, liveSync 10 -> 3)
- Fix recording log lookup (.mp4 vs .mkv mismatch)

Design:
- Replace hardcoded colors with AppColors tokens (12 files)
- web/theme.css syncs HTML players with the Flutter palette
- DPAD/keyboard navigation (arrow-key focus, player shortcuts)
- Tooltips on player icon buttons, Semantics on content cards
- Remove 7 dead widgets broken since the Stitch merge

Quality:
- bin/test/: 21 unit tests (bcrypt, redaction, traversal, SSRF, recording
  conflicts) plus a quality-selector widget test
- GitHub Actions CI (analyze + test + build web)
- Archive stale status docs into docs/archive/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:07:18 +02:00

177 lines
10 KiB
HTML

<!DOCTYPE html>
<html class="dark" lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>XtremFlow - Login</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com" rel="preconnect"/>
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&amp;family=Space+Grotesk:wght@500;600;700&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"surface-variant": "#343439",
"primary-fixed": "#d8e2ff",
"surface-bright": "#38393d",
"on-tertiary-container": "#282a2a",
"error-container": "#93000a",
"on-secondary": "#2f3037",
"on-primary-fixed-variant": "#004493",
"tertiary-container": "#909191",
"surface-container-lowest": "#0d0e12",
"inverse-on-surface": "#2f3035",
"surface-container-highest": "#343439",
"surface": "#121317",
"secondary-fixed-dim": "#c6c5cf",
"on-tertiary": "#2f3131",
"surface-dim": "#121317",
"on-secondary-fixed": "#1a1b22",
"on-primary": "#002e69",
"primary": "#adc6ff",
"primary-fixed-dim": "#adc6ff",
"outline": "#8b90a0",
"secondary": "#c6c5cf",
"surface-tint": "#adc6ff",
"surface-container-high": "#292a2e",
"on-tertiary-fixed-variant": "#454747",
"error": "#ffb4ab",
"on-surface": "#e3e2e7",
"secondary-container": "#4a4b53",
"on-primary-fixed": "#001a41",
"tertiary-fixed": "#e2e2e2",
"on-secondary-container": "#bcbbc4",
"on-secondary-fixed-variant": "#46464e",
"inverse-primary": "#005bc1",
"primary-container": "#4b8eff",
"tertiary": "#c6c6c7",
"inverse-surface": "#e3e2e7",
"on-error-container": "#ffdad6",
"surface-container": "#1f1f24",
"on-tertiary-fixed": "#1a1c1c",
"surface-container-low": "#1a1b20",
"on-error": "#690005",
"tertiary-fixed-dim": "#c6c6c7",
"secondary-fixed": "#e3e1eb",
"on-surface-variant": "#c1c6d7",
"on-background": "#e3e2e7",
"outline-variant": "#414755",
"background": "#121317",
"on-primary-container": "#00285c"
},
"borderRadius": {
"DEFAULT": "0.25rem",
"lg": "0.5rem",
"xl": "0.75rem",
"full": "9999px"
},
"spacing": {
"base": "8px",
"xl": "80px",
"lg": "48px",
"md": "24px",
"sm": "12px",
"xs": "4px",
"margin": "32px",
"gutter": "24px"
},
"fontFamily": {
"body-md": ["Inter"],
"headline-lg": ["Space Grotesk"],
"label-lg": ["Inter"],
"headline-xl": ["Space Grotesk"],
"label-sm": ["Inter"],
"body-lg": ["Inter"],
"headline-md": ["Space Grotesk"]
},
"fontSize": {
"body-md": ["16px", {"lineHeight": "1.5", "fontWeight": "400"}],
"headline-lg": ["32px", {"lineHeight": "1.2", "letterSpacing": "-0.01em", "fontWeight": "600"}],
"label-lg": ["14px", {"lineHeight": "1.2", "letterSpacing": "0.05em", "fontWeight": "600"}],
"headline-xl": ["48px", {"lineHeight": "1.1", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"label-sm": ["12px", {"lineHeight": "1.2", "letterSpacing": "0.02em", "fontWeight": "500"}],
"body-lg": ["18px", {"lineHeight": "1.6", "fontWeight": "400"}],
"headline-md": ["24px", {"lineHeight": "1.3", "fontWeight": "500"}]
}
}
}
}
</script>
<style>
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.fill-icon {
font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
</style>
</head>
<body class="bg-background text-on-background min-h-screen flex items-center justify-center relative overflow-hidden font-body-md text-body-md antialiased">
<!-- Subtle Tech Gradient Background Elements -->
<div class="absolute inset-0 z-0">
<div class="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary-container/10 rounded-full blur-[120px]"></div>
<div class="absolute bottom-[-20%] right-[-10%] w-[60%] h-[60%] bg-inverse-primary/10 rounded-full blur-[150px]"></div>
<!-- Grid overlay for tech feel -->
<div class="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.02)_1px,transparent_1px)] bg-[size:40px_40px] opacity-20 pointer-events-none"></div>
</div>
<!-- Login Card Canvas -->
<main class="relative z-10 w-full max-w-[420px] mx-margin">
<!-- Glassmorphism Container -->
<div class="bg-surface-container/40 backdrop-blur-2xl border border-white/10 rounded-xl shadow-[0_0_40px_rgba(0,0,0,0.8)] p-lg overflow-hidden relative">
<!-- Subtle Inner Glow -->
<div class="absolute top-0 left-0 w-full h-full border border-primary-container/20 rounded-xl pointer-events-none" style="clip-path: polygon(0 0, 100% 0, 100% 1px, 0 1px);"></div>
<div class="absolute top-0 left-0 w-full h-full border border-primary-container/20 rounded-xl pointer-events-none" style="clip-path: polygon(0 0, 1px 0, 1px 100%, 0 100%);"></div>
<!-- Brand Header -->
<div class="flex flex-col items-center mb-lg">
<div class="w-12 h-12 bg-primary-container text-on-primary-container rounded-lg flex items-center justify-center mb-sm shadow-[0_0_15px_rgba(75,142,255,0.3)]">
<span class="material-symbols-outlined fill-icon text-[28px]" data-icon="hub">hub</span>
</div>
<h1 class="font-headline-lg text-headline-lg text-on-background tracking-tighter">XtremFlow</h1>
<p class="font-label-sm text-label-sm text-on-surface-variant mt-xs tracking-widest uppercase">System Access</p>
</div>
<!-- Login Form -->
<form class="flex flex-col gap-md">
<!-- Username / Email Field -->
<div class="flex flex-col gap-xs relative">
<label class="font-label-sm text-label-sm text-on-surface-variant ml-1" for="username">Username or Email</label>
<div class="relative flex items-center">
<span class="material-symbols-outlined absolute left-sm text-outline-variant pointer-events-none" data-icon="person">person</span>
<input class="w-full bg-surface-container-highest border border-outline-variant/30 text-on-surface font-body-md text-body-md rounded-lg py-sm pl-[44px] pr-sm focus:border-primary-container focus:ring-1 focus:ring-primary-container transition-all placeholder:text-outline-variant/50 outline-none" id="username" placeholder="Enter your credentials" type="text"/>
</div>
</div>
<!-- Password Field -->
<div class="flex flex-col gap-xs relative">
<div class="flex justify-between items-center ml-1">
<label class="font-label-sm text-label-sm text-on-surface-variant" for="password">Password</label>
<a class="font-label-sm text-label-sm text-primary-fixed hover:text-primary transition-colors" href="#">Forgot?</a>
</div>
<div class="relative flex items-center">
<span class="material-symbols-outlined absolute left-sm text-outline-variant pointer-events-none" data-icon="lock">lock</span>
<input class="w-full bg-surface-container-highest border border-outline-variant/30 text-on-surface font-body-md text-body-md rounded-lg py-sm pl-[44px] pr-[44px] focus:border-primary-container focus:ring-1 focus:ring-primary-container transition-all placeholder:text-outline-variant/50 outline-none" id="password" placeholder="••••••••" type="password"/>
<button class="absolute right-sm text-outline-variant hover:text-on-surface transition-colors flex items-center justify-center" type="button">
<span class="material-symbols-outlined" data-icon="visibility">visibility</span>
</button>
</div>
</div>
<!-- Actions -->
<div class="mt-xs">
<button class="w-full bg-primary-container text-on-primary-container font-label-lg text-label-lg py-sm rounded-lg hover:bg-primary-fixed transition-all active:scale-[0.98] shadow-[0_4px_14px_rgba(75,142,255,0.2)] hover:shadow-[0_6px_20px_rgba(75,142,255,0.3)] flex justify-center items-center gap-2" type="submit">
Sign In
<span class="material-symbols-outlined text-[18px]" data-icon="login">login</span>
</button>
</div>
</form>
<!-- Footer -->
<div class="mt-lg text-center">
<p class="font-body-sm text-[14px] text-on-surface-variant">
Require access? <a class="text-primary-fixed hover:text-primary font-medium transition-colors" href="#">Contact Administrator</a>
</p>
</div>
</div>
</main>
</body></html>