chore: standardize security and quality foundations
This commit is contained in:
@@ -1,36 +1,89 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { authFetch, clearToken, getApiBase } from '../lib/auth'
|
||||
import '../welcome.css'
|
||||
import { useEffect, useState } from "react";
|
||||
import { authFetch, clearToken, getApiBase } from "../lib/auth";
|
||||
import "../welcome.css";
|
||||
|
||||
export default function WelcomePage() {
|
||||
const [ready, setReady] = useState(false)
|
||||
const [url, setUrl] = useState<string | null>(null)
|
||||
const [error, setError] = useState('')
|
||||
const [ready, setReady] = useState(false);
|
||||
const [url, setUrl] = useState<string | null>(null);
|
||||
const [error, setError] = useState("");
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
const controller = new AbortController();
|
||||
void (async () => {
|
||||
try {
|
||||
const response = await authFetch(`${getApiBase()}/site/info`, { signal: controller.signal })
|
||||
if (response.status === 401) { clearToken(); window.location.replace('/login'); return }
|
||||
if (!response.ok) throw new Error('Unavailable')
|
||||
const data = await response.json()
|
||||
const candidate = data.mediaServerUrl ? new URL(data.mediaServerUrl) : null
|
||||
if (candidate && ['https:', 'http:'].includes(candidate.protocol) && !candidate.username && !candidate.password) setUrl(candidate.href)
|
||||
setReady(true)
|
||||
const response = await authFetch(`${getApiBase()}/site/info`, { signal: controller.signal });
|
||||
if (response.status === 401) {
|
||||
clearToken();
|
||||
window.location.replace("/login");
|
||||
return;
|
||||
}
|
||||
if (!response.ok) throw new Error("Unavailable");
|
||||
const data = await response.json();
|
||||
const candidate = data.mediaServerUrl ? new URL(data.mediaServerUrl) : null;
|
||||
if (candidate && ["https:", "http:"].includes(candidate.protocol) && !candidate.username && !candidate.password)
|
||||
setUrl(candidate.href);
|
||||
setReady(true);
|
||||
} catch {
|
||||
if (!controller.signal.aborted) setError('We couldn’t load your welcome page. Please try again.')
|
||||
if (!controller.signal.aborted) setError("We couldn’t load your welcome page. Please try again.");
|
||||
}
|
||||
})()
|
||||
return () => controller.abort()
|
||||
}, [])
|
||||
return <main className="welcome-page">
|
||||
<header><span className="welcome-kicker">GrizzlyFlix + Magent</span><h1>Make yourself at home.</h1><p>Something to watch, or something to sort out?</p></header>
|
||||
{error ? <div role="alert"><p>{error}</p><button type="button" onClick={() => window.location.reload()}>Try again</button> <a href="/login">Back to sign in</a></div> : !ready ? <p role="status">Getting things ready…</p> : <div className="welcome-choices">
|
||||
{url ? <a className="welcome-choice" href={url}><span className="welcome-icon" aria-hidden="true">▷</span><h2>Go to GrizzlyFlix</h2><p>Find your next favourite. Watch movies and TV shows.</p><strong>Let’s watch <span aria-hidden="true">→</span></strong></a> : <section className="welcome-choice"><span className="welcome-icon" aria-hidden="true">▷</span><h2>Go to GrizzlyFlix</h2><p>The watch link hasn’t been set up yet. Please ask an admin to add the public playback URL.</p></section>}
|
||||
<a className="welcome-choice" href="/"><span className="welcome-icon" aria-hidden="true">☷</span><h2>View stats & requests</h2><p>Check your viewing stats, follow your requests, or make a new one.</p><strong>Open Magent <span aria-hidden="true">→</span></strong></a>
|
||||
</div>}
|
||||
<footer>First time here? <a href="/how-it-works">Here’s how it works</a>.</footer>
|
||||
</main>
|
||||
})();
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
return (
|
||||
<main className="welcome-page">
|
||||
<header>
|
||||
<span className="welcome-kicker">GrizzlyFlix + Magent</span>
|
||||
<h1>Make yourself at home.</h1>
|
||||
<p>Something to watch, or something to sort out?</p>
|
||||
</header>
|
||||
{error ? (
|
||||
<div role="alert">
|
||||
<p>{error}</p>
|
||||
<button type="button" onClick={() => window.location.reload()}>
|
||||
Try again
|
||||
</button>{" "}
|
||||
<a href="/login">Back to sign in</a>
|
||||
</div>
|
||||
) : !ready ? (
|
||||
<p role="status">Getting things ready…</p>
|
||||
) : (
|
||||
<div className="welcome-choices">
|
||||
{url ? (
|
||||
<a className="welcome-choice" href={url}>
|
||||
<span className="welcome-icon" aria-hidden="true">
|
||||
▷
|
||||
</span>
|
||||
<h2>Go to GrizzlyFlix</h2>
|
||||
<p>Find your next favourite. Watch movies and TV shows.</p>
|
||||
<strong>
|
||||
Let’s watch <span aria-hidden="true">→</span>
|
||||
</strong>
|
||||
</a>
|
||||
) : (
|
||||
<section className="welcome-choice">
|
||||
<span className="welcome-icon" aria-hidden="true">
|
||||
▷
|
||||
</span>
|
||||
<h2>Go to GrizzlyFlix</h2>
|
||||
<p>The watch link hasn’t been set up yet. Please ask an admin to add the public playback URL.</p>
|
||||
</section>
|
||||
)}
|
||||
<a className="welcome-choice" href="/">
|
||||
<span className="welcome-icon" aria-hidden="true">
|
||||
☷
|
||||
</span>
|
||||
<h2>View stats & requests</h2>
|
||||
<p>Check your viewing stats, follow your requests, or make a new one.</p>
|
||||
<strong>
|
||||
Open Magent <span aria-hidden="true">→</span>
|
||||
</strong>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<footer>
|
||||
First time here? <a href="/how-it-works">Here’s how it works</a>.
|
||||
</footer>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user