diff --git a/docs/NAVIGATION.md b/docs/NAVIGATION.md new file mode 100644 index 0000000..cf49aee --- /dev/null +++ b/docs/NAVIGATION.md @@ -0,0 +1,174 @@ +# Navigation & Redirect Logic + +This document describes the setup flow navigation, covering how users progress from initial setup through to the main application. + +## Overview + +The application uses a middleware-based redirect system to ensure users complete setup before accessing the main app. The flow involves multiple pages handling setup completion, unresolved folder detection, and initialization. + +## Setup Flow + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ SETUP FLOW │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ /setup ──► /loading ──┬──► /setup/unresolved ──► /loading │ +│ │ │ │ │ │ +│ │ │ │ │ │ +│ ▼ ▼ ▼ ▼ │ +│ (first time) (WebSocket) (has folders) (all resolved) │ +│ │ │ │ +│ ▼ │ │ +│ /login ◄───────────────────┴──────────────────────┤ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +## Middleware: SetupRedirectMiddleware + +**File:** `src/server/middleware/setup_redirect.py` + +The middleware intercepts all requests and redirects to `/setup` if: +- No master password is configured +- Configuration file is missing or invalid + +### Exempt Paths (always accessible) + +| Path | Purpose | +|------|---------| +| `/setup` | Initial setup page | +| `/setup/unresolved` | Unresolved folder resolution | +| `/loading` | Initialization progress page | +| `/login` | Authentication | +| `/api/auth/*` | Auth endpoints | +| `/api/config/*` | Config API | +| `/api/health` | Health check | +| `/static/*` | Static assets | + +### Middleware Logic + +1. **Setup incomplete** → Redirect to `/setup` +2. **Setup complete, accessing `/setup`** → Redirect to `/loading` +3. **Setup complete, accessing `/loading`** → Allow access (page handles its own redirect) +4. **API requests during setup** → Return 503 with `setup_url` + +## Pages + +### 1. Setup Page (`/setup`) + +**File:** `src/server/web/templates/setup.html` + +Handles initial configuration: +- Master password creation +- Anime directory selection +- Database initialization + +**Post-completion flow:** +- Redirects to `/loading` to begin initialization + +### 2. Loading Page (`/loading`) + +**File:** `src/server/web/templates/loading.html` + +Shows initialization progress via WebSocket: +- Series scanning +- Database population +- Logo/image loading + +**Post-initialization flow:** +```javascript +async function checkUnresolvedAndProceed() { + // Fetch unresolved folders via API + const res = await fetch('/api/setup/unresolved', { + headers: { 'Authorization': `Bearer ${token}` } + }); + const folders = await res.json(); + + if (folders.length > 0) { + // Has unresolved folders → go to resolution page + window.location.href = '/setup/unresolved'; + } else { + // No unresolved folders → go to login + window.location.href = '/login'; + } +} +``` + +### 3. Unresolved Folders Page (`/setup/unresolved`) + +**File:** `src/server/web/templates/unresolved.html` + +Allows manual resolution of folders that couldn't be auto-matched: +- Shows list of unresolved folders +- Provides search suggestions +- Input field for entering provider key +- Resolve/delete actions + +**Post-resolution flow:** +```javascript +function checkEmptyList() { + if (listEl.children.length === 0) { + // All folders resolved → return to loading + setTimeout(() => { window.location.href = '/loading'; }, 2000); + } +} +``` + +### 4. Login Page (`/login`) + +**File:** `src/server/web/templates/login.html` + +Authentication page. After successful login → redirect to `/` (main app). + +## API Endpoints + +### Unresolved Folders API + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `GET` | `/api/setup/unresolved` | List all unresolved folders | +| `GET` | `/api/setup/unresolved/{folder_name}` | Get specific folder details | +| `POST` | `/api/setup/unresolved/{folder_name}/resolve` | Resolve with provider key | +| `POST` | `/api/setup/unresolved/{folder_name}/search` | Re-search for matches | +| `DELETE` | `/api/setup/unresolved/{folder_name}` | Remove folder from tracking | + +### Auth API + +| Method | Endpoint | Description | +|--------|----------|-------------| +| `POST` | `/api/auth/setup` | Create master password | +| `POST` | `/api/auth/login` | Authenticate | +| `POST` | `/api/auth/logout` | End session | + +## Key Files + +| File | Purpose | +|------|---------| +| `src/server/middleware/setup_redirect.py` | Redirect middleware | +| `src/server/controllers/page_controller.py` | Page route handlers | +| `src/server/web/templates/setup.html` | Setup template | +| `src/server/web/templates/loading.html` | Loading template | +| `src/server/web/templates/unresolved.html` | Unresolved folders template | +| `src/server/api/setup_endpoints.py` | Unresolved folders API | +| `src/server/database/service.py` | UnresolvedFolderService | + +## Common Issues + +### Redirect Loop + +**Symptom:** Browser keeps redirecting between pages. + +**Causes:** +1. `loading.html` always redirected to `/setup/unresolved` without checking if any exist +2. `unresolved.html` redirected to `/` which middleware redirected back to `/login` + +**Fix:** See the navigation logic updates in loading.html and unresolved.html. + +### Can't Access Unresolved Page After Setup + +**Symptom:** Middleware redirects to `/login` instead of allowing access to `/setup/unresolved`. + +**Cause:** `/setup/unresolved` is in the exempt paths but the request may not be reaching it due to completion check timing. + +**Fix:** The middleware allows access to `/loading` which handles the redirect to `/setup/unresolved` after initialization. \ No newline at end of file diff --git a/src/server/web/templates/loading.html b/src/server/web/templates/loading.html index 6acaae3..73a02e6 100644 --- a/src/server/web/templates/loading.html +++ b/src/server/web/templates/loading.html @@ -475,9 +475,26 @@ } async function checkUnresolvedAndProceed() { - // Always redirect to /setup/unresolved after initialization - // so users can manually enter unresolved animes - window.location.href = '/setup/unresolved'; + // Fetch unresolved folders and only redirect if there are any + // Otherwise go directly to login + try { + const token = localStorage.getItem('auth_token'); + const res = await fetch('/api/setup/unresolved', { + headers: { 'Authorization': `Bearer ${token}` } + }); + if (res.ok) { + const folders = await res.json(); + if (folders && folders.length > 0) { + // Has unresolved folders - go to resolution page + window.location.href = '/setup/unresolved'; + return; + } + } + } catch (err) { + console.error('Failed to check unresolved folders:', err); + } + // No unresolved folders or error - go to login + window.location.href = '/login'; } function showError(message) { diff --git a/src/server/web/templates/unresolved.html b/src/server/web/templates/unresolved.html index 714b556..dfd7280 100644 --- a/src/server/web/templates/unresolved.html +++ b/src/server/web/templates/unresolved.html @@ -552,7 +552,7 @@ listEl.style.display = 'none'; emptyEl.style.display = 'block'; document.getElementById('skip-link').style.display = 'block'; - setTimeout(() => { window.location.href = '/'; }, 2000); + setTimeout(() => { window.location.href = '/loading'; }, 2000); } else { listEl.style.display = 'flex'; emptyEl.style.display = 'none'; @@ -690,7 +690,7 @@ emptyEl.style.display = 'block'; skipLink.style.display = 'block'; showToast('All series configured!', 'success'); - setTimeout(() => { window.location.href = '/'; }, 2000); + setTimeout(() => { window.location.href = '/loading'; }, 2000); } }