212 lines
6.8 KiB
TypeScript
212 lines
6.8 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: 'Jellyseerr 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">Guide map</span>
|
|
<h2>Quick path</h2>
|
|
<p>Identity → Intake → Queue → Download → Import → Playback.</p>
|
|
<span className="small-pill">Admin only</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<AdminShell
|
|
title="System guide"
|
|
subtitle="Admin-only architecture and operational flow for Magent."
|
|
rail={rail}
|
|
actions={
|
|
<button type="button" onClick={() => router.push('/admin')}>
|
|
Back to settings
|
|
</button>
|
|
}
|
|
>
|
|
<section className="admin-section system-guide">
|
|
<div className="admin-panel">
|
|
<h2>End-to-end system flow</h2>
|
|
<p className="lede">
|
|
This is the exact runtime path for request processing and availability in the current build.
|
|
</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>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, and invite 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>Live request page</h3>
|
|
<p>Event-stream updates for state, action history, and torrent progress without page refresh.</p>
|
|
</article>
|
|
</div>
|
|
</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>
|
|
</section>
|
|
</AdminShell>
|
|
)
|
|
}
|