277 lines
9.9 KiB
TypeScript
277 lines
9.9 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import AdminShell from "../../ui/AdminShell";
|
|
import { authFetch, clearToken, getApiBase, getToken } from "../../lib/auth";
|
|
|
|
type FlowStage = {
|
|
title: string;
|
|
input: string;
|
|
action: string;
|
|
output: string;
|
|
};
|
|
|
|
const REQUEST_FLOW: FlowStage[] = [
|
|
{
|
|
title: "Identity + access",
|
|
input: "Jellyfin/local login",
|
|
action: "Magent validates credentials and role",
|
|
output: "JWT token + user scope",
|
|
},
|
|
{
|
|
title: "Request intake",
|
|
input: "Seerr request ID",
|
|
action: "Magent snapshots request + media metadata",
|
|
output: "Unified request state",
|
|
},
|
|
{
|
|
title: "Queue orchestration",
|
|
input: "Approved request",
|
|
action: "Sonarr/Radarr add/search operations",
|
|
output: "Grab decision",
|
|
},
|
|
{
|
|
title: "Download execution",
|
|
input: "Selected release",
|
|
action: "qBittorrent downloads + reports progress",
|
|
output: "Import-ready payload",
|
|
},
|
|
{
|
|
title: "Library import",
|
|
input: "Completed download",
|
|
action: "Sonarr/Radarr import and finalize",
|
|
output: "Available media object",
|
|
},
|
|
{
|
|
title: "Playback availability",
|
|
input: "Imported media",
|
|
action: "Jellyfin refresh + link resolution",
|
|
output: "Ready-to-watch state",
|
|
},
|
|
];
|
|
|
|
export default function AdminSystemGuidePage() {
|
|
const router = useRouter();
|
|
const [loading, setLoading] = useState(true);
|
|
const [authorized, setAuthorized] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let active = true;
|
|
const load = async () => {
|
|
if (!getToken()) {
|
|
router.push("/login");
|
|
return;
|
|
}
|
|
try {
|
|
const baseUrl = getApiBase();
|
|
const response = await authFetch(`${baseUrl}/auth/me`);
|
|
if (!response.ok) {
|
|
if (response.status === 401) {
|
|
clearToken();
|
|
router.push("/login");
|
|
return;
|
|
}
|
|
router.push("/");
|
|
return;
|
|
}
|
|
const me = await response.json();
|
|
if (!active) return;
|
|
if (me?.role !== "admin") {
|
|
router.push("/");
|
|
return;
|
|
}
|
|
setAuthorized(true);
|
|
} catch (error) {
|
|
console.error(error);
|
|
router.push("/");
|
|
} finally {
|
|
if (active) setLoading(false);
|
|
}
|
|
};
|
|
void load();
|
|
return () => {
|
|
active = false;
|
|
};
|
|
}, [router]);
|
|
|
|
if (loading) {
|
|
return <main className="card">Loading system guide...</main>;
|
|
}
|
|
|
|
if (!authorized) {
|
|
return null;
|
|
}
|
|
|
|
const rail = (
|
|
<div className="admin-rail-stack">
|
|
<div className="admin-rail-card">
|
|
<span className="admin-rail-eyebrow">How it works</span>
|
|
<h2>Admin flow map</h2>
|
|
<p>Identity → Request intake → Queue orchestration → Download → Import → Playback.</p>
|
|
<span className="small-pill">Admin only</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<AdminShell title="System guide" subtitle="Service connections, controls, and recovery paths." rail={rail}>
|
|
<section className="admin-section system-guide">
|
|
<div className="admin-panel">
|
|
<h2>End-to-end system flow</h2>
|
|
<p className="lede">
|
|
This is the runtime path the platform follows from authentication through to playback availability.
|
|
</p>
|
|
<div className="system-flow-track">
|
|
{REQUEST_FLOW.map((stage, index) => (
|
|
<div key={stage.title} className="system-flow-segment">
|
|
<article className="system-flow-card">
|
|
<div className="system-flow-card-title">
|
|
{index + 1}. {stage.title}
|
|
</div>
|
|
<div className="system-flow-card-row">
|
|
<span>Input</span>
|
|
<strong>{stage.input}</strong>
|
|
</div>
|
|
<div className="system-flow-card-row">
|
|
<span>Action</span>
|
|
<strong>{stage.action}</strong>
|
|
</div>
|
|
<div className="system-flow-card-row">
|
|
<span>Output</span>
|
|
<strong>{stage.output}</strong>
|
|
</div>
|
|
</article>
|
|
{index < REQUEST_FLOW.length - 1 && (
|
|
<div className="system-flow-arrow" aria-hidden="true">
|
|
→
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="admin-panel">
|
|
<h2>What each service is responsible for</h2>
|
|
<div className="system-guide-grid">
|
|
<article className="system-guide-card">
|
|
<h3>Magent</h3>
|
|
<p>
|
|
Handles authentication, request pages, live event updates, invite workflows, diagnostics, notifications,
|
|
and admin operations.
|
|
</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Seerr</h3>
|
|
<p>
|
|
Stores the request itself and remains the request-state source for approval and media request metadata.
|
|
</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Jellyfin</h3>
|
|
<p>Provides user sign-in identity and the final playback destination once content is available.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Sonarr / Radarr</h3>
|
|
<p>Control queue placement, quality-profile decisions, import handling, and release monitoring.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Prowlarr</h3>
|
|
<p>Provides search/indexer coverage for Arr-side release searches.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>qBittorrent</h3>
|
|
<p>Executes the download and exposes live progress, paused states, and queue visibility.</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="admin-panel">
|
|
<h2>Operational controls by area</h2>
|
|
<div className="system-guide-grid">
|
|
<article className="system-guide-card">
|
|
<h3>General</h3>
|
|
<p>Application URL, API URL, ports, bind host, proxy base URL, and manual SSL settings.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Notifications</h3>
|
|
<p>Email, Discord, Telegram, push/mobile, and generic webhook delivery channels.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Users</h3>
|
|
<p>Role/profile/expiry, auto-search access, invite access, and cross-system ban/remove actions.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Invite management</h3>
|
|
<p>Master template, profile assignment, invite access policy, invite emails, and trace map lineage.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Requests + cache</h3>
|
|
<p>All-requests view, sync controls, cached request records, and maintenance operations.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Maintenance + diagnostics</h3>
|
|
<p>
|
|
Connectivity checks, live diagnostics, database repair, cleanup, log review, and nuclear flush/resync
|
|
operations.
|
|
</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="admin-panel">
|
|
<h2>User and invite model</h2>
|
|
<ol className="system-decision-list">
|
|
<li>Jellyfin is used for sign-in identity and user presence across the platform.</li>
|
|
<li>Seerr provides request ownership and request-state data for Magent request pages.</li>
|
|
<li>Invite links, invite profiles, blanket rules, and invite-access controls are managed inside Magent.</li>
|
|
<li>If invite tracing is enabled, the lineage view shows who invited whom and how the chain branches.</li>
|
|
<li>Cross-system removal and ban flows are initiated from Magent admin controls.</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div className="admin-panel">
|
|
<h2>Stall recovery path (decision flow)</h2>
|
|
<ol className="system-decision-list">
|
|
<li>
|
|
Request approved but not in Arr queue <span>→</span> run <strong>Re-add to Arr</strong>.
|
|
</li>
|
|
<li>
|
|
In queue but no release found <span>→</span> run <strong>Search releases</strong> and inspect options.
|
|
</li>
|
|
<li>
|
|
Release exists and user should not pick manually <span>→</span> run{" "}
|
|
<strong>Search + auto-download</strong>.
|
|
</li>
|
|
<li>
|
|
Download paused/stalled in qBittorrent <span>→</span> run <strong>Resume download</strong>.
|
|
</li>
|
|
<li>
|
|
Imported but not visible to user <span>→</span> validate Jellyfin visibility/link from request page.
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div className="admin-panel">
|
|
<h2>Live update surfaces</h2>
|
|
<div className="system-guide-grid">
|
|
<article className="system-guide-card">
|
|
<h3>Landing page</h3>
|
|
<p>Recent request activity refreshes live for signed-in users.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Request pages</h3>
|
|
<p>Timeline state, queue activity, and torrent progress are pushed live without refresh.</p>
|
|
</article>
|
|
<article className="system-guide-card">
|
|
<h3>Admin views</h3>
|
|
<p>Fleet health, diagnostics, logs, sync state, and maintenance data stay in admin-only settings.</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</AdminShell>
|
|
);
|
|
}
|