refactor(docs): restructure navigation as state machine
Replaced linear flow diagram with explicit state definitions and transition table. Removes MIGRATION_GUIDE.md (merged into main docs).
This commit is contained in:
@@ -1,111 +0,0 @@
|
|||||||
# Migration Guide: File-Based to Database Storage
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This guide covers the transition from file-based series metadata storage to the new database-backed system introduced in v2.0.
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
**Before v2.0**: Series metadata stored in `key` and `data` files alongside anime folders.
|
|
||||||
|
|
||||||
**After v2.0**: All metadata stored in SQLite database (`aniworld.db`). Files are deprecated but still supported for backward compatibility during migration.
|
|
||||||
|
|
||||||
## Automated Migration
|
|
||||||
|
|
||||||
The application automatically migrates on first startup:
|
|
||||||
|
|
||||||
1. Scans anime directory for `key` and `data` files
|
|
||||||
2. Parses legacy files into `AnimeSeries` and `Episode` records
|
|
||||||
3. Loads series into in-memory cache
|
|
||||||
4. Logs migration results
|
|
||||||
|
|
||||||
**No manual action required.**
|
|
||||||
|
|
||||||
## Manual Verification
|
|
||||||
|
|
||||||
After first startup with the new version:
|
|
||||||
|
|
||||||
1. **Check logs** for: `"Migrated X series from files to DB"`
|
|
||||||
2. **Verify series count**: UI shows same number of series as before
|
|
||||||
3. **Confirm episodes**: Episode counts match expected totals
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check migration log
|
|
||||||
grep "Migrated" logs/app.log
|
|
||||||
|
|
||||||
# Verify series via API
|
|
||||||
curl http://localhost:8000/api/anime | jq '.total'
|
|
||||||
```
|
|
||||||
|
|
||||||
## After Migration
|
|
||||||
|
|
||||||
### Safe to Delete
|
|
||||||
|
|
||||||
Once verified, these files can be removed:
|
|
||||||
|
|
||||||
```
|
|
||||||
<anime_folder>/
|
|
||||||
├── Attack on Titan (2013)/
|
|
||||||
│ ├── key # ❌ Can delete
|
|
||||||
│ ├── data # ❌ Can delete
|
|
||||||
│ └── Season 1/
|
|
||||||
│ └── ...
|
|
||||||
```
|
|
||||||
|
|
||||||
**Deleting these files does not affect the database.** The metadata now lives in `aniworld.db`.
|
|
||||||
|
|
||||||
### Backup (Recommended)
|
|
||||||
|
|
||||||
Before deleting, backup the files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create backup directory
|
|
||||||
mkdir -p backup/legacy_series_files
|
|
||||||
|
|
||||||
# Copy all key and data files
|
|
||||||
find /path/to/anime -name "key" -o -name "data" | while read f; do
|
|
||||||
cp "$f" "backup/legacy_series_files/"
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reverting (Not Recommended)
|
|
||||||
|
|
||||||
If you must revert to file-based storage:
|
|
||||||
|
|
||||||
1. **Restore from database backup** (if available)
|
|
||||||
2. **Export manually** (no export script exists)
|
|
||||||
|
|
||||||
**Warning**: File-based storage is deprecated and will be removed in v3.0.0.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Series Not Appearing After Migration
|
|
||||||
|
|
||||||
1. Check logs for migration errors: `grep -i error logs/app.log`
|
|
||||||
2. Verify `key` and `data` files exist and are readable
|
|
||||||
3. Manually trigger rescan: `POST /api/scheduler/trigger-rescan`
|
|
||||||
|
|
||||||
### Duplicate Series
|
|
||||||
|
|
||||||
1. Check for duplicate `key` files (same series in multiple folders)
|
|
||||||
2. Verify series key uniqueness in database:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sqlite3 aniworld.db "SELECT key, COUNT(*) FROM anime_series GROUP BY key HAVING COUNT(*) > 1;"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Missing Episodes
|
|
||||||
|
|
||||||
1. Trigger targeted scan for affected series
|
|
||||||
2. Check episode sync logs
|
|
||||||
3. Verify file permissions on anime directory
|
|
||||||
|
|
||||||
## Deprecation Timeline
|
|
||||||
|
|
||||||
| Version | Status |
|
|
||||||
|---------|--------|
|
|
||||||
| v2.0.x | Legacy files supported, migration automated |
|
|
||||||
| v2.1.x | Legacy files still supported, warnings in logs |
|
|
||||||
| v3.0.0 | **Legacy files removed** - database only |
|
|
||||||
|
|
||||||
Upgrade to v3.0.0 before legacy file support ends.
|
|
||||||
@@ -4,54 +4,56 @@ This document describes the setup flow navigation, covering how users progress f
|
|||||||
|
|
||||||
## Overview
|
## 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.
|
The application uses a middleware-based redirect system to enforce a strict state machine. Users must complete each phase before accessing the next. Attempting to bypass the current phase redirects to the appropriate page.
|
||||||
|
|
||||||
## Setup Flow
|
## State Machine
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
│ SETUP FLOW │
|
│ NAVIGATION STATES │
|
||||||
├─────────────────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────────────────────┤
|
||||||
│ │
|
│ │
|
||||||
│ /setup ──► /loading ──► /setup/unresolved ──► /loading ──► /login │
|
│ NO_SETUP ──────────► SETUP_COMPLETE ──────────► UNRESOLVED_PENDING │
|
||||||
│ │ │ │ │ │
|
│ │ │ │ │
|
||||||
│ │ │ │ │ │
|
│ │ │ │ │
|
||||||
│ ▼ ▼ ▼ ▼ │
|
│ ▼ ▼ ▼ │
|
||||||
│ (first (Series Scan + (has folders) (all resolved) │
|
│ /setup /loading /setup/unresolved │
|
||||||
│ time) NFO Scan) │ │
|
│ (series scan) (resolve folders) │
|
||||||
│ │ │ │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ▼ │
|
|
||||||
│ │ [Done button] ──► marks complete │
|
|
||||||
│ │ │ │
|
|
||||||
│ │ ▼ │
|
|
||||||
│ │ /loading (NFO phase runs again) │
|
|
||||||
│ │ │ │
|
|
||||||
│ └────────┴─────────────────────────────────────┘
|
|
||||||
│ │
|
│ │
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
│ UNRESOLVED_DONE ───────┐
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ NFO_SCAN_PENDING │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ /loading │
|
||||||
|
│ (NFO scan) │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ COMPLETE │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ /login │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
**New Navigation Order:**
|
## State Definitions
|
||||||
1. `/setup` → Initial configuration
|
|
||||||
2. `/loading` → Series scan + NFO scan
|
|
||||||
3. `/setup/unresolved` → Resolve folders (if any)
|
|
||||||
4. `/loading` → NFO scan runs again
|
|
||||||
5. `/login` → Authentication
|
|
||||||
|
|
||||||
**Key Changes:**
|
| State | Condition | Target Page |
|
||||||
- After `/setup/unresolved`, the "Done" button marks the phase as complete
|
|-------|-----------|-------------|
|
||||||
- Revisiting `/setup/unresolved` after completion → redirects to `/loading`
|
| `NO_SETUP` | No master password configured | `/setup` |
|
||||||
- `/loading` always goes to `/setup/unresolved` if unresolved folders exist
|
| `SETUP_COMPLETE` | Initial config passed, loading not started | `/loading` |
|
||||||
- NFO scan runs as a separate phase after series sync during initialization
|
| `UNRESOLVED_PENDING` | Setup done, unresolved exist, not marked done | `/setup/unresolved` |
|
||||||
|
| `UNRESOLVED_DONE` | Unresolved phase marked complete, NFO scan pending | `/loading` |
|
||||||
|
| `NFO_SCAN_PENDING` | Unresolved done, NFO scan incomplete | `/loading` |
|
||||||
|
| `COMPLETE` | All phases finished | `/login` |
|
||||||
|
|
||||||
## Middleware: SetupRedirectMiddleware
|
## Middleware: SetupRedirectMiddleware
|
||||||
|
|
||||||
**File:** `src/server/middleware/setup_redirect.py`
|
**File:** `src/server/middleware/setup_redirect.py`
|
||||||
|
|
||||||
The middleware intercepts all requests and redirects to `/setup` if:
|
The middleware intercepts all requests and enforces the state machine.
|
||||||
- No master password is configured
|
|
||||||
- Configuration file is missing or invalid
|
|
||||||
|
|
||||||
### Exempt Paths (always accessible)
|
### Exempt Paths (always accessible)
|
||||||
|
|
||||||
@@ -68,13 +70,47 @@ The middleware intercepts all requests and redirects to `/setup` if:
|
|||||||
|
|
||||||
### Middleware Logic
|
### Middleware Logic
|
||||||
|
|
||||||
1. **Setup incomplete** → Redirect to `/setup`
|
The middleware checks the current state and redirects accordingly:
|
||||||
2. **Setup complete, accessing `/setup`** → Redirect to `/login`
|
|
||||||
3. **Setup complete, accessing `/loading`** → Allow access (page handles its own redirect)
|
```
|
||||||
4. **Setup complete, accessing `/setup/unresolved`**:
|
1. NO_SETUP state:
|
||||||
- If `unresolved_completed` flag is set → Redirect to `/loading`
|
→ Redirect ALL requests to /setup
|
||||||
- Otherwise → Allow access
|
→ Exception: /setup itself is accessible
|
||||||
5. **API requests during setup** → Return 503 with `setup_url`
|
|
||||||
|
2. SETUP_COMPLETE state:
|
||||||
|
→ Redirect /setup to /loading
|
||||||
|
→ Redirect any other page to /loading
|
||||||
|
|
||||||
|
3. UNRESOLVED_PENDING state (unresolved folders exist, not marked done):
|
||||||
|
→ Redirect /setup to /setup/unresolved
|
||||||
|
→ Redirect /loading to /setup/unresolved
|
||||||
|
→ Allow access to /setup/unresolved
|
||||||
|
→ Redirect any other page to /setup/unresolved
|
||||||
|
|
||||||
|
4. UNRESOLVED_DONE state (unresolved marked done, NFO scan pending):
|
||||||
|
→ Redirect /setup to /loading
|
||||||
|
→ Redirect /setup/unresolved to /loading
|
||||||
|
→ Redirect any other page to /loading
|
||||||
|
|
||||||
|
5. NFO_SCAN_PENDING state:
|
||||||
|
→ Redirect /setup to /loading
|
||||||
|
→ Redirect /setup/unresolved to /loading
|
||||||
|
→ Allow access to /loading (NFO phase runs)
|
||||||
|
→ Redirect any other page to /loading
|
||||||
|
|
||||||
|
6. COMPLETE state (loading finished):
|
||||||
|
→ Redirect /setup, /loading, /setup/unresolved to /login
|
||||||
|
→ Allow access to /login and main app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase Tracking Flags
|
||||||
|
|
||||||
|
| Flag | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `setup_complete` | Initial configuration was saved |
|
||||||
|
| `unresolved_completed` | User clicked "Done" on unresolved page |
|
||||||
|
| `loading_complete` | Series scan + initial loading finished |
|
||||||
|
| `nfo_scan_complete` | Final NFO scan finished |
|
||||||
|
|
||||||
## Pages
|
## Pages
|
||||||
|
|
||||||
@@ -87,8 +123,11 @@ Handles initial configuration:
|
|||||||
- Anime directory selection
|
- Anime directory selection
|
||||||
- Database initialization
|
- Database initialization
|
||||||
|
|
||||||
**Post-completion flow:**
|
**Allowed in states:** `NO_SETUP`
|
||||||
- Redirects to `/loading` to begin initialization
|
|
||||||
|
**Post-completion:**
|
||||||
|
- Sets `setup_complete` flag
|
||||||
|
- Redirects to `/loading`
|
||||||
|
|
||||||
### 2. Loading Page (`/loading`)
|
### 2. Loading Page (`/loading`)
|
||||||
|
|
||||||
@@ -99,25 +138,28 @@ Shows initialization progress via WebSocket:
|
|||||||
- Database population
|
- Database population
|
||||||
- Logo/image loading
|
- Logo/image loading
|
||||||
|
|
||||||
**Post-initialization flow:**
|
**Allowed in states:** `SETUP_COMPLETE`, `UNRESOLVED_DONE`, `NFO_SCAN_PENDING`
|
||||||
|
|
||||||
|
**Post-initialization (series scan complete):**
|
||||||
```javascript
|
```javascript
|
||||||
async function checkUnresolvedAndProceed() {
|
async function checkUnresolvedAndProceed() {
|
||||||
// Fetch unresolved folders via API
|
|
||||||
const res = await fetch('/api/setup/unresolved', {
|
const res = await fetch('/api/setup/unresolved', {
|
||||||
headers: { 'Authorization': `Bearer ${token}` }
|
headers: { 'Authorization': `Bearer ${token}` }
|
||||||
});
|
});
|
||||||
const folders = await res.json();
|
const folders = await res.json();
|
||||||
|
|
||||||
if (folders.length > 0) {
|
if (folders.length > 0) {
|
||||||
// Has unresolved folders → go to resolution page
|
|
||||||
window.location.href = '/setup/unresolved';
|
window.location.href = '/setup/unresolved';
|
||||||
} else {
|
} else {
|
||||||
// No unresolved folders → go to login
|
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Post-NFO scan:**
|
||||||
|
- Sets `nfo_scan_complete` flag
|
||||||
|
- Redirects to `/login`
|
||||||
|
|
||||||
### 3. Unresolved Folders Page (`/setup/unresolved`)
|
### 3. Unresolved Folders Page (`/setup/unresolved`)
|
||||||
|
|
||||||
**File:** `src/server/web/templates/unresolved.html`
|
**File:** `src/server/web/templates/unresolved.html`
|
||||||
@@ -127,24 +169,16 @@ Allows manual resolution of folders that couldn't be auto-matched:
|
|||||||
- Provides search suggestions
|
- Provides search suggestions
|
||||||
- Input field for entering provider key
|
- Input field for entering provider key
|
||||||
- Resolve/delete actions
|
- Resolve/delete actions
|
||||||
- **Done button** at top to complete the phase without resolving all folders
|
- **Done button** to complete the phase without resolving all folders
|
||||||
|
|
||||||
**Post-resolution flow:**
|
**Allowed in states:** `UNRESOLVED_PENDING`
|
||||||
```javascript
|
|
||||||
// After clicking "Done" button
|
|
||||||
async function handleDone() {
|
|
||||||
// Call API to mark phase as complete
|
|
||||||
await fetch('/api/setup/unresolved/done', { method: 'POST' });
|
|
||||||
// Redirect to loading for final NFO scan
|
|
||||||
window.location.href = '/loading';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Done button behavior:**
|
**Done button behavior:**
|
||||||
- Marks all remaining folders as handled
|
- Sets `unresolved_completed` flag
|
||||||
- Sets `unresolved_completed` flag in config
|
- Redirects to `/loading` for final NFO scan
|
||||||
- Redirects to `/loading` to run final NFO scan
|
|
||||||
- After completion, `/setup/unresolved` becomes inaccessible (redirects to `/loading`)
|
**After completion:**
|
||||||
|
- Any access redirects to `/loading`
|
||||||
|
|
||||||
### 4. Login Page (`/login`)
|
### 4. Login Page (`/login`)
|
||||||
|
|
||||||
@@ -152,6 +186,8 @@ async function handleDone() {
|
|||||||
|
|
||||||
Authentication page. After successful login → redirect to `/` (main app).
|
Authentication page. After successful login → redirect to `/` (main app).
|
||||||
|
|
||||||
|
**Allowed in states:** `COMPLETE`
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
### Unresolved Folders API
|
### Unresolved Folders API
|
||||||
@@ -177,7 +213,7 @@ Authentication page. After successful login → redirect to `/` (main app).
|
|||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `src/server/middleware/setup_redirect.py` | Redirect middleware |
|
| `src/server/middleware/setup_redirect.py` | Redirect middleware (state machine) |
|
||||||
| `src/server/controllers/page_controller.py` | Page route handlers |
|
| `src/server/controllers/page_controller.py` | Page route handlers |
|
||||||
| `src/server/web/templates/setup.html` | Setup template |
|
| `src/server/web/templates/setup.html` | Setup template |
|
||||||
| `src/server/web/templates/loading.html` | Loading template |
|
| `src/server/web/templates/loading.html` | Loading template |
|
||||||
@@ -185,22 +221,13 @@ Authentication page. After successful login → redirect to `/` (main app).
|
|||||||
| `src/server/api/setup_endpoints.py` | Unresolved folders API |
|
| `src/server/api/setup_endpoints.py` | Unresolved folders API |
|
||||||
| `src/server/database/service.py` | UnresolvedFolderService |
|
| `src/server/database/service.py` | UnresolvedFolderService |
|
||||||
|
|
||||||
## Common Issues
|
## Navigation Summary
|
||||||
|
|
||||||
### Redirect Loop
|
| Current State | Access `/setup` | Access `/loading` | Access `/setup/unresolved` |
|
||||||
|
|--------------|-----------------|-------------------|---------------------------|
|
||||||
**Symptom:** Browser keeps redirecting between pages.
|
| NO_SETUP | ✅ Allowed | ❌ → `/setup` | ❌ → `/setup` |
|
||||||
|
| SETUP_COMPLETE | ❌ → `/loading` | ✅ Allowed | ❌ → `/loading` |
|
||||||
**Causes:**
|
| UNRESOLVED_PENDING | ❌ → `/setup/unresolved` | ❌ → `/setup/unresolved` | ✅ Allowed |
|
||||||
1. `loading.html` always redirected to `/setup/unresolved` without checking if any exist
|
| UNRESOLVED_DONE | ❌ → `/loading` | ✅ Allowed (NFO phase) | ❌ → `/loading` |
|
||||||
2. `unresolved.html` redirected to `/` which middleware redirected back to `/login`
|
| NFO_SCAN_PENDING | ❌ → `/loading` | ✅ Allowed (NFO phase) | ❌ → `/loading` |
|
||||||
|
| COMPLETE | ❌ → `/login` | ❌ → `/login` | ❌ → `/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.
|
|
||||||
Reference in New Issue
Block a user