mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
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>
177 lines
10 KiB
HTML
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&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&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&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> |