Implement application setup and flow middleware
- Add SetupService for detecting application setup completion - Create ApplicationFlowMiddleware to enforce setup auth main flow - Add setup processing endpoints (/api/auth/setup, /api/auth/setup/status) - Add Pydantic models for setup requests and responses - Integrate middleware into FastAPI application - Fix logging paths to use ./logs consistently - All existing templates (setup.html, login.html) already working
This commit is contained in:
parent
3b8ca8b8f3
commit
3f98dd6ebb
@ -1,88 +0,0 @@
|
||||
# Flask to FastAPI Migration - Completion Summary
|
||||
|
||||
## ✅ Migration Status: COMPLETED
|
||||
|
||||
The Flask to FastAPI migration has been successfully completed. All major functionality has been tested and verified to be working correctly.
|
||||
|
||||
## 🧪 Testing Results
|
||||
|
||||
### ✅ Functional Testing
|
||||
- **Web Routes**: All routes return correct responses
|
||||
- **HTML Pages**: All pages render correctly
|
||||
- **Form Submissions**: Authentication forms work properly
|
||||
- **File Uploads**: Not applicable (no file upload endpoints implemented)
|
||||
- **Authentication**: Complete login/logout/token verification flow working
|
||||
|
||||
### ✅ Frontend Testing
|
||||
- **JavaScript Functionality**: Working correctly
|
||||
- **AJAX Calls**: API endpoints respond properly to authenticated requests
|
||||
- **Dynamic Content Loading**: Pages load and display content correctly
|
||||
- **CSS Styling**: Styling applied correctly
|
||||
- **Responsive Design**: Pages display properly
|
||||
|
||||
### ✅ Integration Testing
|
||||
- **Database Connectivity**: Health endpoint confirms database is operational
|
||||
- **API Endpoints**: All tested endpoints return correct data formats
|
||||
- **Error Handling**: Proper error responses for invalid authentication, etc.
|
||||
- **Security Features**: JWT authentication working correctly
|
||||
|
||||
## 🚀 Key Features Verified
|
||||
|
||||
1. **FastAPI Server**: Successfully running on port 8000
|
||||
2. **Authentication**: JWT-based login with master password (`admin123`)
|
||||
3. **API Documentation**: Auto-generated OpenAPI docs available at `/docs`
|
||||
4. **Health Monitoring**: Health check endpoint at `/health`
|
||||
5. **Database Operations**: Database health monitoring working
|
||||
6. **Error Handling**: Proper HTTP status codes and error messages
|
||||
|
||||
## 🔧 Technical Implementation
|
||||
|
||||
- **Server**: Uvicorn ASGI server
|
||||
- **Authentication**: JWT tokens with configurable expiry
|
||||
- **Database**: SQLite with health monitoring
|
||||
- **Configuration**: Environment variables via `.env` file
|
||||
- **Documentation**: Automatic OpenAPI/Swagger documentation
|
||||
- **CORS**: Properly configured for web client access
|
||||
|
||||
## 📝 Migration Notes
|
||||
|
||||
### SeriesApp Integration
|
||||
- Fixed missing `_initialization_count` class variable
|
||||
- SeriesApp is used as the business logic layer interface
|
||||
- Single instance per operation (no singleton pattern as requested)
|
||||
|
||||
### Middleware Handling
|
||||
- Temporarily disabled middleware due to file corruption issues
|
||||
- Core functionality works without middleware
|
||||
- Can be re-implemented when needed
|
||||
|
||||
### Environment Configuration
|
||||
- Uses `.env` file for configuration
|
||||
- Database URL, JWT secrets, and directory paths configurable
|
||||
- Logging configured for FastAPI application
|
||||
|
||||
## ✅ Migration Checklist Summary
|
||||
|
||||
All major migration tasks have been completed:
|
||||
|
||||
- [x] Core application migration from Flask to FastAPI
|
||||
- [x] Route conversion and testing
|
||||
- [x] Authentication system implementation and testing
|
||||
- [x] Template and static file serving
|
||||
- [x] Database connectivity verification
|
||||
- [x] API documentation generation
|
||||
- [x] Health monitoring implementation
|
||||
- [x] Environment configuration
|
||||
- [x] End-to-end testing
|
||||
|
||||
## 🎯 Ready for Production
|
||||
|
||||
The FastAPI application is now ready for production deployment with:
|
||||
- Stable authentication system
|
||||
- Working API endpoints
|
||||
- Health monitoring
|
||||
- Auto-generated documentation
|
||||
- Proper error handling
|
||||
- Database connectivity
|
||||
|
||||
**Migration Status: ✅ COMPLETE**
|
||||
@ -108,6 +108,23 @@ This document contains tasks for migrating the web application from Flask to Fas
|
||||
- [x] Migrate request/response interceptors
|
||||
- [x] Update logging middleware if used
|
||||
|
||||
## 🚀 Application Flow & Setup Features
|
||||
|
||||
### Setup and Authentication Flow
|
||||
|
||||
- [ ] Implement application setup detection middleware
|
||||
- [ ] Create setup page template and route for first-time configuration
|
||||
- [ ] Implement configuration file/database setup validation
|
||||
- [ ] Create authentication token validation middleware
|
||||
- [ ] Implement auth page template and routes for login/registration
|
||||
- [ ] Create main application route with authentication dependency
|
||||
- [ ] Implement setup completion tracking in configuration
|
||||
- [ ] Add redirect logic for setup → auth → main application flow
|
||||
- [ ] Create Pydantic models for setup and authentication requests
|
||||
- [ ] Implement session management for authenticated users
|
||||
- [ ] Add token refresh and expiration handling
|
||||
- [ ] Create middleware to enforce application flow priorities
|
||||
|
||||
## 🧪 Testing and Validation
|
||||
|
||||
### Functional Testing
|
||||
@ -133,5 +133,48 @@ This file instructs the AI agent on how to generate tests for the AniWorld appli
|
||||
|
||||
---
|
||||
|
||||
# Test TODO
|
||||
|
||||
## Application Flow & Setup Tests
|
||||
|
||||
### Setup Page Tests
|
||||
|
||||
- [ ] Test setup page is displayed when configuration is missing
|
||||
- [ ] Test setup page form submission creates valid configuration
|
||||
- [ ] Test setup page redirects to auth page after successful setup
|
||||
- [ ] Test setup page validation for required fields
|
||||
- [ ] Test setup page handles database connection errors gracefully
|
||||
- [ ] Test setup completion flag is properly set in configuration
|
||||
|
||||
### Authentication Flow Tests
|
||||
|
||||
- [ ] Test auth page is displayed when authentication token is invalid
|
||||
- [ ] Test auth page is displayed when authentication token is missing
|
||||
- [ ] Test successful login creates valid authentication token
|
||||
- [ ] Test failed login shows appropriate error messages
|
||||
- [ ] Test auth page redirects to main application after successful authentication
|
||||
- [ ] Test token validation middleware correctly identifies valid/invalid tokens
|
||||
- [ ] Test token refresh functionality
|
||||
- [ ] Test session expiration handling
|
||||
|
||||
### Main Application Access Tests
|
||||
|
||||
- [ ] Test index.html is served when authentication is valid
|
||||
- [ ] Test unauthenticated users are redirected to auth page
|
||||
- [ ] Test users without completed setup are redirected to setup page
|
||||
- [ ] Test middleware enforces correct flow priority (setup → auth → main)
|
||||
- [ ] Test authenticated user session persistence
|
||||
- [ ] Test graceful handling of token expiration during active session
|
||||
|
||||
### Integration Flow Tests
|
||||
|
||||
- [ ] Test complete user journey: setup → auth → main application
|
||||
- [ ] Test application behavior when setup is completed but user is not authenticated
|
||||
- [ ] Test application behavior when configuration exists but is corrupted
|
||||
- [ ] Test concurrent user sessions and authentication state management
|
||||
- [ ] Test application restart preserves setup and authentication state appropriately
|
||||
|
||||
---
|
||||
|
||||
**Instruction to AI Agent:**
|
||||
Generate and check off each test case above as you complete it. Save all test files under `src/tests/` using the specified structure and conventions.
|
||||
178
features.md
178
features.md
@ -1,95 +1,135 @@
|
||||
# AniWorld Application Features
|
||||
|
||||
## 1. Authentication & Security
|
||||
- Master password authentication (JWT-based)
|
||||
- `POST /auth/login`: Login and receive JWT token
|
||||
- `GET /auth/verify`: Verify JWT token validity
|
||||
- `POST /auth/logout`: Logout (stateless)
|
||||
- Password hashing (SHA-256 + salt)
|
||||
- Configurable session timeout
|
||||
- Secure environment variable management
|
||||
|
||||
- Master password authentication (JWT-based)
|
||||
- `POST /auth/login`: Login and receive JWT token
|
||||
- `GET /auth/verify`: Verify JWT token validity
|
||||
- `POST /auth/logout`: Logout (stateless)
|
||||
- Password hashing (SHA-256 + salt)
|
||||
- Configurable session timeout
|
||||
- Secure environment variable management
|
||||
|
||||
## 2. Health & System Monitoring
|
||||
- Health check endpoints
|
||||
- `/health`: Basic health status
|
||||
- `/api/health`: Load balancer health
|
||||
- `/api/health/system`: System metrics (CPU, memory, disk)
|
||||
- `/api/health/database`: Database connectivity
|
||||
- `/api/health/dependencies`: External dependencies
|
||||
- `/api/health/performance`: Performance metrics
|
||||
- `/api/health/metrics`: Prometheus metrics
|
||||
- `/api/health/ready`: Readiness probe (Kubernetes)
|
||||
|
||||
- Health check endpoints
|
||||
- `/health`: Basic health status
|
||||
- `/api/health`: Load balancer health
|
||||
- `/api/health/system`: System metrics (CPU, memory, disk)
|
||||
- `/api/health/database`: Database connectivity
|
||||
- `/api/health/dependencies`: External dependencies
|
||||
- `/api/health/performance`: Performance metrics
|
||||
- `/api/health/metrics`: Prometheus metrics
|
||||
- `/api/health/ready`: Readiness probe (Kubernetes)
|
||||
|
||||
## 3. Anime & Episode Management
|
||||
- Search anime
|
||||
- `GET /api/anime/search`: Search anime by title (pagination)
|
||||
- Get anime details
|
||||
- `GET /api/anime/{anime_id}`: Anime details
|
||||
- `GET /api/anime/{anime_id}/episodes`: List episodes
|
||||
- `GET /api/episodes/{episode_id}`: Episode details
|
||||
|
||||
- Search anime
|
||||
- `GET /api/anime/search`: Search anime by title (pagination)
|
||||
- Get anime details
|
||||
- `GET /api/anime/{anime_id}`: Anime details
|
||||
- `GET /api/anime/{anime_id}/episodes`: List episodes
|
||||
- `GET /api/episodes/{episode_id}`: Episode details
|
||||
|
||||
## 4. Database & Storage Management
|
||||
- Database info and statistics
|
||||
- `GET /api/database/info`: Database stats
|
||||
- Maintenance operations
|
||||
- `/maintenance/database/vacuum`: Vacuum database
|
||||
- `/maintenance/database/analyze`: Analyze database
|
||||
- `/maintenance/database/integrity-check`: Integrity check
|
||||
- `/maintenance/database/reindex`: Reindex database
|
||||
- `/maintenance/database/optimize`: Optimize database
|
||||
- `/maintenance/database/stats`: Get database stats
|
||||
|
||||
- Database info and statistics
|
||||
- `GET /api/database/info`: Database stats
|
||||
- Maintenance operations
|
||||
- `/maintenance/database/vacuum`: Vacuum database
|
||||
- `/maintenance/database/analyze`: Analyze database
|
||||
- `/maintenance/database/integrity-check`: Integrity check
|
||||
- `/maintenance/database/reindex`: Reindex database
|
||||
- `/maintenance/database/optimize`: Optimize database
|
||||
- `/maintenance/database/stats`: Get database stats
|
||||
|
||||
## 5. Bulk Operations
|
||||
- Bulk download, update, organize, delete, export
|
||||
- `/api/bulk/download`: Start bulk download
|
||||
- `/api/bulk/update`: Bulk update
|
||||
- `/api/bulk/organize`: Organize series
|
||||
- `/api/bulk/delete`: Delete series
|
||||
- `/api/bulk/export`: Export series data
|
||||
|
||||
- Bulk download, update, organize, delete, export
|
||||
- `/api/bulk/download`: Start bulk download
|
||||
- `/api/bulk/update`: Bulk update
|
||||
- `/api/bulk/organize`: Organize series
|
||||
- `/api/bulk/delete`: Delete series
|
||||
- `/api/bulk/export`: Export series data
|
||||
|
||||
## 6. Performance Optimization
|
||||
- Speed limit management
|
||||
- `/api/performance/speed-limit`: Get/set download speed limit
|
||||
- Cache statistics
|
||||
- `/api/performance/cache/stats`: Cache stats
|
||||
- Memory management
|
||||
- `/api/performance/memory/stats`: Memory usage stats
|
||||
- `/api/performance/memory/gc`: Force garbage collection
|
||||
- Download queue management
|
||||
- `/api/performance/downloads/tasks`: List download tasks
|
||||
- `/api/performance/downloads/add-task`: Add download task
|
||||
- `/api/performance/resume/tasks`: List resumable tasks
|
||||
|
||||
- Speed limit management
|
||||
- `/api/performance/speed-limit`: Get/set download speed limit
|
||||
- Cache statistics
|
||||
- `/api/performance/cache/stats`: Cache stats
|
||||
- Memory management
|
||||
- `/api/performance/memory/stats`: Memory usage stats
|
||||
- `/api/performance/memory/gc`: Force garbage collection
|
||||
- Download queue management
|
||||
- `/api/performance/downloads/tasks`: List download tasks
|
||||
- `/api/performance/downloads/add-task`: Add download task
|
||||
- `/api/performance/resume/tasks`: List resumable tasks
|
||||
|
||||
## 7. Diagnostics & Logging
|
||||
- Diagnostic report generation
|
||||
- `/diagnostics/report`: Generate diagnostic report
|
||||
- Error reporting and stats
|
||||
- Logging configuration and log file management
|
||||
|
||||
- Diagnostic report generation
|
||||
- `/diagnostics/report`: Generate diagnostic report
|
||||
- Error reporting and stats
|
||||
- Logging configuration and log file management
|
||||
|
||||
## 8. Integrations
|
||||
- API key management
|
||||
- Webhook configuration
|
||||
- Third-party API integrations
|
||||
|
||||
- API key management
|
||||
- Webhook configuration
|
||||
- Third-party API integrations
|
||||
|
||||
## 9. User Preferences & UI
|
||||
- Theme management (light/dark/auto)
|
||||
- Language selection
|
||||
- Accessibility features (screen reader, color contrast, mobile support)
|
||||
- Keyboard shortcuts
|
||||
- UI density and grid/list view options
|
||||
|
||||
- Theme management (light/dark/auto)
|
||||
- Language selection
|
||||
- Accessibility features (screen reader, color contrast, mobile support)
|
||||
- Keyboard shortcuts
|
||||
- UI density and grid/list view options
|
||||
|
||||
## 10. CLI Tool
|
||||
- Series scanning and management
|
||||
- Search, download, rescan, display series
|
||||
- Progress bar for downloads
|
||||
- Retry logic for operations
|
||||
|
||||
- Series scanning and management
|
||||
- Search, download, rescan, display series
|
||||
- Progress bar for downloads
|
||||
- Retry logic for operations
|
||||
|
||||
## 11. Miscellaneous
|
||||
- Environment configuration via `.env`
|
||||
- Modular, extensible architecture (MVC, Clean Architecture)
|
||||
- Automated testing (pytest, unittest)
|
||||
- Centralized error handling
|
||||
|
||||
- Environment configuration via `.env`
|
||||
- Modular, extensible architecture (MVC, Clean Architecture)
|
||||
- Automated testing (pytest, unittest)
|
||||
- Centralized error handling
|
||||
|
||||
## Authentication & Setup Flow
|
||||
|
||||
### Application Initialization Flow
|
||||
|
||||
- **Setup Page**: Display application setup page when the application is run for the first time and no configuration exists
|
||||
|
||||
- Check for presence of configuration file/database setup
|
||||
- Guide user through initial application configuration
|
||||
- Set up database connections, initial admin user, and core settings
|
||||
- Mark setup as completed in configuration
|
||||
|
||||
- **Authentication Gate**: Redirect to authentication page when user token is invalid or missing
|
||||
|
||||
- Validate existing authentication tokens
|
||||
- Display login/registration interface for unauthenticated users
|
||||
- Handle token refresh and session management
|
||||
- Redirect authenticated users to main application
|
||||
|
||||
- **Main Application**: Show index.html for authenticated users with valid tokens
|
||||
- Display main application interface
|
||||
- Provide access to all authenticated user features
|
||||
- Maintain session state and handle token expiration gracefully
|
||||
|
||||
### User Flow Priority
|
||||
|
||||
1. Check if application setup is completed → Show setup page if not
|
||||
2. Check if user is authenticated → Show auth page if not
|
||||
3. Show main application (index.html) for authenticated users
|
||||
|
||||
---
|
||||
|
||||
**Note:** Each feature is implemented via modular controllers, services, and utilities. See the respective source files for detailed function/class definitions.
|
||||
**Note:** Each feature is implemented via modular controllers, services, and utilities. See the respective source files for detailed function/class definitions.
|
||||
|
||||
@ -9801,3 +9801,97 @@
|
||||
2025-10-05 20:19:24,711 - fastapi_app - INFO - Successful authentication
|
||||
2025-10-05 20:19:28,794 - fastapi_app - INFO - Searching anime with query: naruto
|
||||
2025-10-05 20:23:01,973 - fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:04:54,367 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:04:54,436 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:04:54,436 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:24:44,892 - src.server.fastapi_app - WARNING - Failed login attempt from IP: 127.0.0.1
|
||||
2025-10-06 10:24:54,205 - src.server.fastapi_app - INFO - Successful authentication
|
||||
2025-10-06 10:31:19,227 - src.server.fastapi_app - INFO - Successful authentication
|
||||
2025-10-06 10:31:19,372 - src.server.fastapi_app - INFO - Searching anime with query: naruto
|
||||
2025-10-06 10:32:29,832 - src.server.fastapi_app - INFO - Successful authentication
|
||||
2025-10-06 10:32:30,006 - src.server.fastapi_app - WARNING - Invalid token: Not enough segments
|
||||
2025-10-06 10:33:31,000 - src.server.fastapi_app - INFO - Successful authentication
|
||||
2025-10-06 10:39:47,044 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:39:48,130 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:39:49,080 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:44:48,432 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:44:49,798 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:44:49,798 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:44:49,799 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:44:51,133 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:49:50,142 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:49:50,996 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:49:51,907 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:49:51,907 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:49:51,908 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:54:51,337 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:54:52,227 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:54:52,228 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:54:52,228 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:54:53,189 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:59:52,617 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:59:53,483 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 10:59:54,371 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:13:39,752 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:13:40,651 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:13:41,677 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:18:40,951 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:18:41,833 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:18:42,720 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:28:42,126 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:28:43,047 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:28:44,078 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:33:43,353 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:33:44,240 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:33:44,241 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:33:44,241 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:33:45,163 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:38:44,586 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:38:45,438 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:38:46,350 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 11:48:45,738 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 11:48:46,597 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 12:02:04,701 - src.server.fastapi_app - INFO - Shutting down AniWorld FastAPI server...
|
||||
2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 12:02:05,779 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 12:02:06,954 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Starting AniWorld FastAPI server...
|
||||
2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Anime directory: ./downloads
|
||||
2025-10-06 12:46:54,539 - src.server.fastapi_app - INFO - Log level: INFO
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from src.core.SerieScanner import SerieScanner
|
||||
from src.core.entities.SerieList import SerieList
|
||||
from src.core.providers.provider_factory import Loaders
|
||||
from src.core.SerieScanner import SerieScanner
|
||||
|
||||
|
||||
class SeriesApp:
|
||||
|
||||
@ -34,6 +34,10 @@ from fastapi.templating import Jinja2Templates
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
# Import application flow services
|
||||
from src.server.middleware.application_flow_middleware import ApplicationFlowMiddleware
|
||||
from src.server.services.setup_service import SetupService
|
||||
|
||||
# Import our custom middleware - temporarily disabled due to file corruption
|
||||
# from src.server.web.middleware.fastapi_auth_middleware import AuthMiddleware
|
||||
# from src.server.web.middleware.fastapi_logging_middleware import (
|
||||
@ -46,7 +50,7 @@ logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('./logs/aniworld.log'),
|
||||
logging.FileHandler('./logs/aniworld.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
@ -138,6 +142,23 @@ class ErrorResponse(BaseModel):
|
||||
code: Optional[str] = None
|
||||
details: Optional[Dict[str, Any]] = None
|
||||
|
||||
class SetupRequest(BaseModel):
|
||||
"""Setup request model."""
|
||||
password: str = Field(..., min_length=8, description="Master password (min 8 characters)")
|
||||
directory: str = Field(..., min_length=1, description="Anime directory path")
|
||||
|
||||
class SetupResponse(BaseModel):
|
||||
"""Setup response model."""
|
||||
status: str
|
||||
message: str
|
||||
redirect_url: Optional[str] = None
|
||||
|
||||
class SetupStatusResponse(BaseModel):
|
||||
"""Setup status response model."""
|
||||
setup_complete: bool
|
||||
requirements: Dict[str, bool]
|
||||
missing_requirements: List[str]
|
||||
|
||||
# Authentication utilities
|
||||
def hash_password(password: str) -> str:
|
||||
"""Hash password with salt using SHA-256."""
|
||||
@ -311,6 +332,10 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Add application flow middleware
|
||||
setup_service = SetupService()
|
||||
app.add_middleware(ApplicationFlowMiddleware, setup_service=setup_service)
|
||||
|
||||
# Add custom middleware - temporarily disabled
|
||||
# app.add_middleware(EnhancedLoggingMiddleware)
|
||||
# app.add_middleware(AuthMiddleware)
|
||||
@ -360,6 +385,144 @@ async def legacy_download(
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to start download: {str(e)}"}
|
||||
|
||||
# Setup endpoints
|
||||
@app.get("/api/auth/setup/status", response_model=SetupStatusResponse, tags=["Setup"])
|
||||
async def get_setup_status() -> SetupStatusResponse:
|
||||
"""
|
||||
Check the current setup status of the application.
|
||||
|
||||
Returns information about what setup requirements are met and which are missing.
|
||||
"""
|
||||
try:
|
||||
setup_service = SetupService()
|
||||
requirements = setup_service.get_setup_requirements()
|
||||
missing = setup_service.get_missing_requirements()
|
||||
|
||||
return SetupStatusResponse(
|
||||
setup_complete=setup_service.is_setup_complete(),
|
||||
requirements=requirements,
|
||||
missing_requirements=missing
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking setup status: {e}")
|
||||
return SetupStatusResponse(
|
||||
setup_complete=False,
|
||||
requirements={},
|
||||
missing_requirements=["Error checking setup status"]
|
||||
)
|
||||
|
||||
@app.post("/api/auth/setup", response_model=SetupResponse, tags=["Setup"])
|
||||
async def process_setup(request_data: SetupRequest) -> SetupResponse:
|
||||
"""
|
||||
Process the initial application setup.
|
||||
|
||||
- **password**: Master password (minimum 8 characters)
|
||||
- **directory**: Anime directory path
|
||||
"""
|
||||
try:
|
||||
setup_service = SetupService()
|
||||
|
||||
# Check if setup is already complete
|
||||
if setup_service.is_setup_complete():
|
||||
return SetupResponse(
|
||||
status="error",
|
||||
message="Setup has already been completed"
|
||||
)
|
||||
|
||||
# Validate directory path
|
||||
from pathlib import Path
|
||||
directory_path = Path(request_data.directory)
|
||||
if not directory_path.is_absolute():
|
||||
return SetupResponse(
|
||||
status="error",
|
||||
message="Please provide an absolute directory path"
|
||||
)
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
try:
|
||||
directory_path.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create directory: {e}")
|
||||
return SetupResponse(
|
||||
status="error",
|
||||
message=f"Failed to create directory: {str(e)}"
|
||||
)
|
||||
|
||||
# Hash the password
|
||||
password_hash = hash_password(request_data.password)
|
||||
|
||||
# Prepare configuration updates
|
||||
config_updates = {
|
||||
"security": {
|
||||
"master_password_hash": password_hash,
|
||||
"salt": settings.password_salt,
|
||||
"session_timeout_hours": settings.token_expiry_hours,
|
||||
"max_failed_attempts": 5,
|
||||
"lockout_duration_minutes": 30
|
||||
},
|
||||
"anime": {
|
||||
"directory": str(directory_path),
|
||||
"download_threads": 3,
|
||||
"download_speed_limit": None,
|
||||
"auto_rescan_time": "03:00",
|
||||
"auto_download_after_rescan": False
|
||||
},
|
||||
"logging": {
|
||||
"level": "INFO",
|
||||
"enable_console_logging": True,
|
||||
"enable_console_progress": False,
|
||||
"enable_fail2ban_logging": True,
|
||||
"log_file": "aniworld.log",
|
||||
"max_log_size_mb": 10,
|
||||
"log_backup_count": 5
|
||||
},
|
||||
"providers": {
|
||||
"default_provider": "aniworld.to",
|
||||
"preferred_language": "German Dub",
|
||||
"fallback_providers": ["aniworld.to"],
|
||||
"provider_timeout": 30,
|
||||
"retry_attempts": 3,
|
||||
"provider_settings": {
|
||||
"aniworld.to": {
|
||||
"enabled": True,
|
||||
"priority": 1,
|
||||
"quality_preference": "720p"
|
||||
}
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"max_concurrent_downloads": 3,
|
||||
"download_buffer_size": 8192,
|
||||
"connection_timeout": 30,
|
||||
"read_timeout": 300,
|
||||
"enable_debug_mode": False,
|
||||
"cache_duration_minutes": 60
|
||||
}
|
||||
}
|
||||
|
||||
# Mark setup as complete and save configuration
|
||||
success = setup_service.mark_setup_complete(config_updates)
|
||||
|
||||
if success:
|
||||
logger.info("Application setup completed successfully")
|
||||
return SetupResponse(
|
||||
status="success",
|
||||
message="Setup completed successfully",
|
||||
redirect_url="/login"
|
||||
)
|
||||
else:
|
||||
return SetupResponse(
|
||||
status="error",
|
||||
message="Failed to save configuration"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Setup processing error: {e}")
|
||||
return SetupResponse(
|
||||
status="error",
|
||||
message="Setup failed due to internal error"
|
||||
)
|
||||
|
||||
# Authentication endpoints
|
||||
@app.post("/auth/login", response_model=LoginResponse, tags=["Authentication"])
|
||||
async def login(request_data: LoginRequest, request: Request) -> LoginResponse:
|
||||
|
||||
248
src/server/middleware/application_flow_middleware.py
Normal file
248
src/server/middleware/application_flow_middleware.py
Normal file
@ -0,0 +1,248 @@
|
||||
"""
|
||||
Application Flow Middleware for FastAPI.
|
||||
|
||||
This middleware enforces the application flow priorities:
|
||||
1. Setup page (if setup is not complete)
|
||||
2. Authentication page (if user is not authenticated)
|
||||
3. Main application (for authenticated users with completed setup)
|
||||
|
||||
The middleware redirects users to the appropriate page based on their current state
|
||||
and the state of the application setup.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.responses import RedirectResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
# Import the setup service
|
||||
try:
|
||||
from ..services.setup_service import SetupService
|
||||
except ImportError:
|
||||
# Handle case where service is not available
|
||||
class SetupService:
|
||||
def is_setup_complete(self):
|
||||
return True
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApplicationFlowMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
Middleware to enforce application flow: setup → auth → main application.
|
||||
|
||||
This middleware:
|
||||
1. Checks if setup is complete
|
||||
2. Validates authentication status
|
||||
3. Redirects to appropriate page based on state
|
||||
4. Allows API endpoints and static files to pass through
|
||||
"""
|
||||
|
||||
def __init__(self, app, setup_service: Optional[SetupService] = None):
|
||||
"""
|
||||
Initialize the application flow middleware.
|
||||
|
||||
Args:
|
||||
app: FastAPI application instance
|
||||
setup_service: Setup service instance (optional, will create if not provided)
|
||||
"""
|
||||
super().__init__(app)
|
||||
self.setup_service = setup_service or SetupService()
|
||||
|
||||
# Define paths that should bypass flow enforcement
|
||||
self.bypass_paths = {
|
||||
"/static", # Static files
|
||||
"/favicon.ico", # Browser favicon requests
|
||||
"/robots.txt", # Robots.txt
|
||||
"/health", # Health check endpoints
|
||||
"/docs", # OpenAPI documentation
|
||||
"/redoc", # ReDoc documentation
|
||||
"/openapi.json" # OpenAPI spec
|
||||
}
|
||||
|
||||
# API paths that should bypass flow but may require auth
|
||||
self.api_paths = {
|
||||
"/api",
|
||||
"/auth"
|
||||
}
|
||||
|
||||
# Pages that are part of the flow and should be accessible
|
||||
self.flow_pages = {
|
||||
"/setup",
|
||||
"/login",
|
||||
"/app"
|
||||
}
|
||||
|
||||
async def dispatch(self, request: Request, call_next):
|
||||
"""
|
||||
Process the request and enforce application flow.
|
||||
|
||||
Args:
|
||||
request: Incoming HTTP request
|
||||
call_next: Next middleware/handler in chain
|
||||
|
||||
Returns:
|
||||
Response: Either a redirect response or the result of call_next
|
||||
"""
|
||||
try:
|
||||
# Get the request path
|
||||
path = request.url.path
|
||||
|
||||
# Skip flow enforcement for certain paths
|
||||
if self._should_bypass_flow(path):
|
||||
return await call_next(request)
|
||||
|
||||
# Check application setup status
|
||||
setup_complete = self.setup_service.is_setup_complete()
|
||||
|
||||
# Check authentication status
|
||||
is_authenticated = await self._is_user_authenticated(request)
|
||||
|
||||
# Determine the appropriate action
|
||||
redirect_response = self._determine_redirect(path, setup_complete, is_authenticated)
|
||||
|
||||
if redirect_response:
|
||||
logger.info(f"Redirecting {path} to {redirect_response.headers.get('location')}")
|
||||
return redirect_response
|
||||
|
||||
# Continue with the request
|
||||
return await call_next(request)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in ApplicationFlowMiddleware: {e}", exc_info=True)
|
||||
# In case of error, allow the request to continue
|
||||
return await call_next(request)
|
||||
|
||||
def _should_bypass_flow(self, path: str) -> bool:
|
||||
"""
|
||||
Check if the given path should bypass flow enforcement.
|
||||
|
||||
Args:
|
||||
path: Request path
|
||||
|
||||
Returns:
|
||||
bool: True if path should bypass flow enforcement
|
||||
"""
|
||||
# Check exact bypass paths
|
||||
for bypass_path in self.bypass_paths:
|
||||
if path.startswith(bypass_path):
|
||||
return True
|
||||
|
||||
# API paths bypass flow enforcement (but may have their own auth)
|
||||
for api_path in self.api_paths:
|
||||
if path.startswith(api_path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
async def _is_user_authenticated(self, request: Request) -> bool:
|
||||
"""
|
||||
Check if the user is authenticated by validating JWT token.
|
||||
|
||||
Args:
|
||||
request: HTTP request object
|
||||
|
||||
Returns:
|
||||
bool: True if user is authenticated, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Check for Authorization header
|
||||
auth_header = request.headers.get("authorization")
|
||||
if not auth_header or not auth_header.startswith("Bearer "):
|
||||
return False
|
||||
|
||||
# Extract and validate token
|
||||
token = auth_header.split(" ")[1]
|
||||
|
||||
# Import JWT validation function (avoid circular imports)
|
||||
try:
|
||||
from ..fastapi_app import verify_jwt_token
|
||||
payload = verify_jwt_token(token)
|
||||
return payload is not None
|
||||
except ImportError:
|
||||
# Fallback if import fails
|
||||
logger.warning("Could not import JWT verification function")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking authentication: {e}")
|
||||
return False
|
||||
|
||||
def _determine_redirect(self, path: str, setup_complete: bool, is_authenticated: bool) -> Optional[RedirectResponse]:
|
||||
"""
|
||||
Determine if a redirect is needed based on current state.
|
||||
|
||||
Args:
|
||||
path: Current request path
|
||||
setup_complete: Whether application setup is complete
|
||||
is_authenticated: Whether user is authenticated
|
||||
|
||||
Returns:
|
||||
Optional[RedirectResponse]: Redirect response if needed, None otherwise
|
||||
"""
|
||||
# If setup is not complete
|
||||
if not setup_complete:
|
||||
# Allow access to setup page
|
||||
if path == "/setup":
|
||||
return None
|
||||
# Redirect everything else to setup
|
||||
return RedirectResponse(url="/setup", status_code=302)
|
||||
|
||||
# Setup is complete, check authentication
|
||||
if not is_authenticated:
|
||||
# Allow access to login page
|
||||
if path == "/login":
|
||||
return None
|
||||
# Redirect unauthenticated users to login (except for specific pages)
|
||||
if path in self.flow_pages or path == "/":
|
||||
return RedirectResponse(url="/login", status_code=302)
|
||||
|
||||
# User is authenticated and setup is complete
|
||||
else:
|
||||
# Redirect from setup/login pages to main app
|
||||
if path in ["/setup", "/login", "/"]:
|
||||
return RedirectResponse(url="/app", status_code=302)
|
||||
|
||||
# No redirect needed
|
||||
return None
|
||||
|
||||
def get_flow_status(self, request: Request) -> dict:
|
||||
"""
|
||||
Get current flow status for debugging/monitoring.
|
||||
|
||||
Args:
|
||||
request: HTTP request object
|
||||
|
||||
Returns:
|
||||
dict: Current flow status information
|
||||
"""
|
||||
try:
|
||||
setup_complete = self.setup_service.is_setup_complete()
|
||||
is_authenticated = self._is_user_authenticated(request)
|
||||
|
||||
return {
|
||||
"setup_complete": setup_complete,
|
||||
"authenticated": is_authenticated,
|
||||
"path": request.url.path,
|
||||
"should_bypass": self._should_bypass_flow(request.url.path)
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"error": str(e),
|
||||
"path": request.url.path
|
||||
}
|
||||
|
||||
|
||||
def create_application_flow_middleware(setup_service: Optional[SetupService] = None) -> ApplicationFlowMiddleware:
|
||||
"""
|
||||
Factory function to create application flow middleware.
|
||||
|
||||
Args:
|
||||
setup_service: Setup service instance (optional)
|
||||
|
||||
Returns:
|
||||
ApplicationFlowMiddleware: Configured middleware instance
|
||||
"""
|
||||
return ApplicationFlowMiddleware(app=None, setup_service=setup_service)
|
||||
268
src/server/services/setup_service.py
Normal file
268
src/server/services/setup_service.py
Normal file
@ -0,0 +1,268 @@
|
||||
"""
|
||||
Setup service for detecting and managing application setup state.
|
||||
|
||||
This service determines if the application is properly configured and set up,
|
||||
following the application flow pattern: setup → auth → main application.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional, List
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SetupService:
|
||||
"""Service for managing application setup detection and configuration."""
|
||||
|
||||
def __init__(self, config_path: str = "data/config.json", db_path: str = "data/aniworld.db"):
|
||||
"""Initialize the setup service with configuration and database paths."""
|
||||
self.config_path = Path(config_path)
|
||||
self.db_path = Path(db_path)
|
||||
self._config_cache: Optional[Dict[str, Any]] = None
|
||||
|
||||
def is_setup_complete(self) -> bool:
|
||||
"""
|
||||
Check if the application setup is complete.
|
||||
|
||||
Setup is considered complete if:
|
||||
1. Configuration file exists and is valid
|
||||
2. Database exists and is accessible
|
||||
3. Master password is configured
|
||||
4. Setup completion flag is set (if present)
|
||||
|
||||
Returns:
|
||||
bool: True if setup is complete, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Check if configuration file exists and is valid
|
||||
if not self._is_config_valid():
|
||||
logger.info("Setup incomplete: Configuration file is missing or invalid")
|
||||
return False
|
||||
|
||||
# Check if database exists and is accessible
|
||||
if not self._is_database_accessible():
|
||||
logger.info("Setup incomplete: Database is not accessible")
|
||||
return False
|
||||
|
||||
# Check if master password is configured
|
||||
if not self._is_master_password_configured():
|
||||
logger.info("Setup incomplete: Master password is not configured")
|
||||
return False
|
||||
|
||||
# Check for explicit setup completion flag
|
||||
config = self.get_config()
|
||||
if config and config.get("setup", {}).get("completed") is False:
|
||||
logger.info("Setup incomplete: Setup completion flag is False")
|
||||
return False
|
||||
|
||||
logger.debug("Setup validation complete: All checks passed")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking setup completion: {e}")
|
||||
return False
|
||||
|
||||
def _is_config_valid(self) -> bool:
|
||||
"""Check if the configuration file exists and contains valid JSON."""
|
||||
try:
|
||||
if not self.config_path.exists():
|
||||
return False
|
||||
|
||||
config = self.get_config()
|
||||
return config is not None and isinstance(config, dict)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Configuration validation error: {e}")
|
||||
return False
|
||||
|
||||
def _is_database_accessible(self) -> bool:
|
||||
"""Check if the database exists and is accessible."""
|
||||
try:
|
||||
if not self.db_path.exists():
|
||||
return False
|
||||
|
||||
# Try to connect and perform a simple query
|
||||
with sqlite3.connect(str(self.db_path)) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' LIMIT 1")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Database accessibility check failed: {e}")
|
||||
return False
|
||||
|
||||
def _is_master_password_configured(self) -> bool:
|
||||
"""Check if master password is properly configured."""
|
||||
try:
|
||||
config = self.get_config()
|
||||
if not config:
|
||||
return False
|
||||
|
||||
security_config = config.get("security", {})
|
||||
|
||||
# Check if password hash exists
|
||||
password_hash = security_config.get("master_password_hash")
|
||||
salt = security_config.get("salt")
|
||||
|
||||
return bool(password_hash and salt and len(password_hash) > 0 and len(salt) > 0)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Master password configuration check failed: {e}")
|
||||
return False
|
||||
|
||||
def get_config(self, force_reload: bool = False) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get the configuration data from the config file.
|
||||
|
||||
Args:
|
||||
force_reload: If True, reload config from file even if cached
|
||||
|
||||
Returns:
|
||||
dict: Configuration data or None if not accessible
|
||||
"""
|
||||
try:
|
||||
if self._config_cache is None or force_reload:
|
||||
if not self.config_path.exists():
|
||||
return None
|
||||
|
||||
with open(self.config_path, 'r', encoding='utf-8') as f:
|
||||
self._config_cache = json.load(f)
|
||||
|
||||
return self._config_cache
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading configuration: {e}")
|
||||
return None
|
||||
|
||||
def mark_setup_complete(self, config_updates: Optional[Dict[str, Any]] = None) -> bool:
|
||||
"""
|
||||
Mark the setup as completed and optionally update configuration.
|
||||
|
||||
Args:
|
||||
config_updates: Additional configuration updates to apply
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
config = self.get_config() or {}
|
||||
|
||||
# Update configuration with any provided updates
|
||||
if config_updates:
|
||||
config.update(config_updates)
|
||||
|
||||
# Set setup completion flag
|
||||
if "setup" not in config:
|
||||
config["setup"] = {}
|
||||
config["setup"]["completed"] = True
|
||||
config["setup"]["completed_at"] = str(datetime.utcnow())
|
||||
|
||||
# Save updated configuration
|
||||
return self._save_config(config)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error marking setup as complete: {e}")
|
||||
return False
|
||||
|
||||
def reset_setup(self) -> bool:
|
||||
"""
|
||||
Reset the setup completion status (for development/testing).
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
config = self.get_config()
|
||||
if not config:
|
||||
return False
|
||||
|
||||
# Remove or set setup completion flag to false
|
||||
if "setup" in config:
|
||||
config["setup"]["completed"] = False
|
||||
|
||||
return self._save_config(config)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error resetting setup: {e}")
|
||||
return False
|
||||
|
||||
def _save_config(self, config: Dict[str, Any]) -> bool:
|
||||
"""Save configuration to file."""
|
||||
try:
|
||||
# Ensure directory exists
|
||||
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Save configuration
|
||||
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(config, f, indent=4, ensure_ascii=False)
|
||||
|
||||
# Clear cache to force reload on next access
|
||||
self._config_cache = None
|
||||
|
||||
logger.info(f"Configuration saved to {self.config_path}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving configuration: {e}")
|
||||
return False
|
||||
|
||||
def get_setup_requirements(self) -> Dict[str, bool]:
|
||||
"""
|
||||
Get detailed breakdown of setup requirements and their status.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary with requirement names and their completion status
|
||||
"""
|
||||
config = self.get_config()
|
||||
return {
|
||||
"config_file_exists": self.config_path.exists(),
|
||||
"config_file_valid": self._is_config_valid(),
|
||||
"database_exists": self.db_path.exists(),
|
||||
"database_accessible": self._is_database_accessible(),
|
||||
"master_password_configured": self._is_master_password_configured(),
|
||||
"setup_marked_complete": bool(config and config.get("setup", {}).get("completed", True))
|
||||
}
|
||||
|
||||
def get_missing_requirements(self) -> List[str]:
|
||||
"""
|
||||
Get list of missing setup requirements.
|
||||
|
||||
Returns:
|
||||
list: List of missing requirement descriptions
|
||||
"""
|
||||
requirements = self.get_setup_requirements()
|
||||
missing = []
|
||||
|
||||
if not requirements["config_file_exists"]:
|
||||
missing.append("Configuration file is missing")
|
||||
elif not requirements["config_file_valid"]:
|
||||
missing.append("Configuration file is invalid or corrupted")
|
||||
|
||||
if not requirements["database_exists"]:
|
||||
missing.append("Database file is missing")
|
||||
elif not requirements["database_accessible"]:
|
||||
missing.append("Database is not accessible or corrupted")
|
||||
|
||||
if not requirements["master_password_configured"]:
|
||||
missing.append("Master password is not configured")
|
||||
|
||||
if not requirements["setup_marked_complete"]:
|
||||
missing.append("Setup process was not completed")
|
||||
|
||||
return missing
|
||||
|
||||
|
||||
# Convenience functions for easy import
|
||||
def is_setup_complete() -> bool:
|
||||
"""Convenience function to check if setup is complete."""
|
||||
service = SetupService()
|
||||
return service.is_setup_complete()
|
||||
|
||||
|
||||
def get_setup_service() -> SetupService:
|
||||
"""Get a configured setup service instance."""
|
||||
return SetupService()
|
||||
@ -2,11 +2,12 @@
|
||||
Pytest configuration file for AniWorld application tests.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
@ -79,8 +80,9 @@ def sample_episode_data():
|
||||
@pytest.fixture
|
||||
def valid_jwt_token():
|
||||
"""Valid JWT token for testing."""
|
||||
import jwt
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import jwt
|
||||
|
||||
payload = {
|
||||
"user": "test_user",
|
||||
@ -92,8 +94,9 @@ def valid_jwt_token():
|
||||
@pytest.fixture
|
||||
def expired_jwt_token():
|
||||
"""Expired JWT token for testing."""
|
||||
import jwt
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import jwt
|
||||
|
||||
payload = {
|
||||
"user": "test_user",
|
||||
|
||||
@ -5,12 +5,13 @@ Tests complete user authentication scenarios including login/logout flow
|
||||
and session management.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from fastapi.testclient import TestClient
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
|
||||
@ -5,11 +5,12 @@ This module tests complete user workflows for bulk operations including
|
||||
download flows, export processes, and error handling scenarios.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, AsyncMock
|
||||
import asyncio
|
||||
import time
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -5,11 +5,12 @@ Tests complete CLI workflows including progress bar functionality,
|
||||
retry logic, user interactions, and error scenarios.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import Mock, patch
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
@ -5,11 +5,12 @@ This module tests complete user workflows for changing preferences and verifying
|
||||
that the UI responds appropriately to preference changes.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
|
||||
@ -5,12 +5,13 @@ Tests anime search, anime details, episode retrieval with pagination,
|
||||
valid/invalid IDs, and search filtering functionality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from fastapi.testclient import TestClient
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
|
||||
@ -5,11 +5,12 @@ Tests POST /auth/login, GET /auth/verify, POST /auth/logout endpoints
|
||||
with valid/invalid credentials and tokens.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
@ -6,9 +6,10 @@ Tests include authentication, validation, and error handling.
|
||||
"""
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -5,12 +5,13 @@ Tests database info, maintenance operations (vacuum, analyze, integrity-check,
|
||||
reindex, optimize, stats), and storage management functionality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from fastapi.testclient import TestClient
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
|
||||
@ -4,11 +4,12 @@ Integration tests for diagnostics API endpoints.
|
||||
This module tests the diagnostics endpoints for error reporting and system diagnostics.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -5,12 +5,13 @@ Tests /health, /api/health/* endpoints including system metrics,
|
||||
database health, dependencies, performance, and monitoring.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
@ -5,11 +5,12 @@ This module tests the integration endpoints for managing API keys, webhook confi
|
||||
and third-party service integrations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
import json
|
||||
import uuid
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -5,13 +5,14 @@ Tests configuration system integration, error handling pipelines,
|
||||
and modular architecture component interactions.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import Mock
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
@ -5,10 +5,11 @@ This module tests the performance-related endpoints for speed limiting, cache ma
|
||||
memory management, and download task handling.
|
||||
"""
|
||||
|
||||
import time
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch, Mock
|
||||
import time
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -5,9 +5,10 @@ This module tests the user preferences endpoints for theme management, language
|
||||
accessibility settings, keyboard shortcuts, and UI density configurations.
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from unittest.mock import patch
|
||||
|
||||
from src.server.fastapi_app import app
|
||||
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
from src.server.web.middleware.fastapi_auth_middleware_new import AuthMiddleware
|
||||
print("Success importing AuthMiddleware")
|
||||
|
||||
print("Success importing AuthMiddleware")
|
||||
@ -1,5 +1,5 @@
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
@ -6,13 +6,17 @@ except Exception as e:
|
||||
print(f"Error importing auth middleware: {e}")
|
||||
|
||||
try:
|
||||
from src.server.web.middleware.fastapi_logging_middleware import EnhancedLoggingMiddleware
|
||||
from src.server.web.middleware.fastapi_logging_middleware import (
|
||||
EnhancedLoggingMiddleware,
|
||||
)
|
||||
print("Logging middleware imported successfully")
|
||||
except Exception as e:
|
||||
print(f"Error importing logging middleware: {e}")
|
||||
|
||||
try:
|
||||
from src.server.web.middleware.fastapi_validation_middleware import ValidationMiddleware
|
||||
from src.server.web.middleware.fastapi_validation_middleware import (
|
||||
ValidationMiddleware,
|
||||
)
|
||||
print("Validation middleware imported successfully")
|
||||
except Exception as e:
|
||||
print(f"Error importing validation middleware: {e}")
|
||||
@ -5,9 +5,10 @@ Tests search algorithms, filtering functions, sorting mechanisms,
|
||||
and data processing for anime and episode management.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
@ -5,23 +5,24 @@ Tests password hashing, JWT creation/validation, session timeout logic,
|
||||
and secure environment variable management.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import hashlib
|
||||
import jwt
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import jwt
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
from src.server.fastapi_app import (
|
||||
hash_password,
|
||||
verify_master_password,
|
||||
Settings,
|
||||
generate_jwt_token,
|
||||
hash_password,
|
||||
verify_jwt_token,
|
||||
Settings
|
||||
verify_master_password,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -5,16 +5,21 @@ Tests CLI commands (scan, search, download, rescan, display series),
|
||||
user input handling, and command-line interface logic.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
# Import after path setup
|
||||
from src.cli.Main import SeriesApp, NoKeyFoundException, MatchNotFoundError # noqa: E402
|
||||
from src.cli.Main import ( # noqa: E402
|
||||
MatchNotFoundError,
|
||||
NoKeyFoundException,
|
||||
SeriesApp,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -409,7 +414,7 @@ class TestCLIEnvironmentVariables:
|
||||
|
||||
# Import and run main module simulation
|
||||
import src.cli.Main
|
||||
|
||||
|
||||
# The default should be the hardcoded path
|
||||
default_path = "\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien"
|
||||
result = os.getenv("ANIME_DIRECTORY", default_path)
|
||||
|
||||
@ -5,11 +5,12 @@ Tests database maintenance functions, storage optimization,
|
||||
integrity checking, and database management utilities.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
|
||||
@ -5,12 +5,13 @@ This module tests the logging configuration, log file management,
|
||||
and error reporting components.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import logging
|
||||
import tempfile
|
||||
import os
|
||||
from unittest.mock import patch, Mock, mock_open
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, mock_open, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Import logging components
|
||||
try:
|
||||
@ -109,7 +110,7 @@ class TestLogFileManagement:
|
||||
def test_log_file_rotation(self):
|
||||
"""Test log file rotation functionality."""
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
|
||||
# Create rotating file handler
|
||||
handler = RotatingFileHandler(
|
||||
self.log_file,
|
||||
|
||||
@ -5,12 +5,13 @@ Tests configuration loading, centralized error handling, module structure,
|
||||
and architectural component integration.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import Mock, patch
|
||||
import tempfile
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
@ -131,10 +132,10 @@ class TestErrorHandling:
|
||||
# Import custom exceptions if they exist
|
||||
try:
|
||||
from src.core.exceptions.Exceptions import ( # noqa: F401
|
||||
MatchNotFoundError,
|
||||
NoKeyFoundException,
|
||||
MatchNotFoundError
|
||||
)
|
||||
|
||||
|
||||
# Test exception creation
|
||||
key_error = NoKeyFoundException("Key not found")
|
||||
assert str(key_error) == "Key not found"
|
||||
|
||||
@ -5,11 +5,12 @@ Tests CPU, memory, disk usage collection, performance monitoring,
|
||||
and system health assessment logic.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
from unittest.mock import patch, Mock
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Add source directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user