Remove migration code and alembic dependency
This commit is contained in:
parent
842f9c88eb
commit
ee317b29f1
@ -17,7 +17,7 @@
|
|||||||
"keep_days": 30
|
"keep_days": 30
|
||||||
},
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"master_password_hash": "$pbkdf2-sha256$29000$DgFgDIHwfk/p/X.PEULIGQ$baPkp2MQxqv8yolTjZ5Ks0fIl9g/Eer3YBE1jjR6qjc",
|
"master_password_hash": "$pbkdf2-sha256$29000$tRZCyFnr/d87x/i/19p7Lw$BoD8EF67N97SRs7kIX8SREbotRwvFntS.WCH9ZwTxHY",
|
||||||
"anime_directory": "/home/lukas/Volume/serien/"
|
"anime_directory": "/home/lukas/Volume/serien/"
|
||||||
},
|
},
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
|
|||||||
24
data/config_backups/config_backup_20251213_085947.json
Normal file
24
data/config_backups/config_backup_20251213_085947.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "Aniworld",
|
||||||
|
"data_dir": "data",
|
||||||
|
"scheduler": {
|
||||||
|
"enabled": true,
|
||||||
|
"interval_minutes": 60
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"level": "INFO",
|
||||||
|
"file": null,
|
||||||
|
"max_bytes": null,
|
||||||
|
"backup_count": 3
|
||||||
|
},
|
||||||
|
"backup": {
|
||||||
|
"enabled": false,
|
||||||
|
"path": "data/backups",
|
||||||
|
"keep_days": 30
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"master_password_hash": "$pbkdf2-sha256$29000$nbNWSkkJIeTce48xxrh3bg$QXT6A63JqmSLimtTeI04HzC4eKfQS26xFW7UL9Ry5co",
|
||||||
|
"anime_directory": "/home/lukas/Volume/serien/"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
24
data/config_backups/config_backup_20251213_090130.json
Normal file
24
data/config_backups/config_backup_20251213_090130.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "Aniworld",
|
||||||
|
"data_dir": "data",
|
||||||
|
"scheduler": {
|
||||||
|
"enabled": true,
|
||||||
|
"interval_minutes": 60
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"level": "INFO",
|
||||||
|
"file": null,
|
||||||
|
"max_bytes": null,
|
||||||
|
"backup_count": 3
|
||||||
|
},
|
||||||
|
"backup": {
|
||||||
|
"enabled": false,
|
||||||
|
"path": "data/backups",
|
||||||
|
"keep_days": 30
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"master_password_hash": "$pbkdf2-sha256$29000$j5HSWuu9V.rdm9Pa2zunNA$gjQqL753WLBMZtHVOhziVn.vW3Bkq8mGtCzSkbBjSHo",
|
||||||
|
"anime_directory": "/home/lukas/Volume/serien/"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
@ -178,10 +178,6 @@ grep -rn "data-key\|data-folder\|data-series" src/server/web/templates/ --includ
|
|||||||
- [ ] All CRUD operations use `key` for identification
|
- [ ] All CRUD operations use `key` for identification
|
||||||
- [ ] Logging uses `key` in messages
|
- [ ] Logging uses `key` in messages
|
||||||
|
|
||||||
3. **`src/server/database/migrations/`**
|
|
||||||
- [ ] Migration files maintain `key` as unique, indexed column
|
|
||||||
- [ ] No migrations that use `folder` as identifier
|
|
||||||
|
|
||||||
**Validation Commands:**
|
**Validation Commands:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -60,10 +60,9 @@ Throughout the codebase, three identifiers are used for anime series:
|
|||||||
**Valid examples**: `"attack-on-titan"`, `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
**Valid examples**: `"attack-on-titan"`, `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||||
**Invalid examples**: `"Attack On Titan"`, `"attack_on_titan"`, `"attack on titan"`
|
**Invalid examples**: `"Attack On Titan"`, `"attack_on_titan"`, `"attack on titan"`
|
||||||
|
|
||||||
### Migration Notes
|
### Notes
|
||||||
|
|
||||||
- **Backward Compatibility**: API endpoints accepting `anime_id` will check `key` first, then fall back to `folder` lookup
|
- **Backward Compatibility**: API endpoints accepting `anime_id` will check `key` first, then fall back to `folder` lookup
|
||||||
- **Deprecation**: Folder-based lookups are deprecated and will be removed in a future version
|
|
||||||
- **New Code**: Always use `key` for identification; `folder` is metadata only
|
- **New Code**: Always use `key` for identification; `folder` is metadata only
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|||||||
@ -14,5 +14,4 @@ pytest==7.4.3
|
|||||||
pytest-asyncio==0.21.1
|
pytest-asyncio==0.21.1
|
||||||
httpx==0.25.2
|
httpx==0.25.2
|
||||||
sqlalchemy>=2.0.35
|
sqlalchemy>=2.0.35
|
||||||
alembic==1.13.0
|
|
||||||
aiosqlite>=0.19.0
|
aiosqlite>=0.19.0
|
||||||
@ -7,7 +7,7 @@
|
|||||||
# installs dependencies, sets up the database, and starts the application.
|
# installs dependencies, sets up the database, and starts the application.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./start.sh [development|production] [--no-install] [--no-migrate]
|
# ./start.sh [development|production] [--no-install]
|
||||||
#
|
#
|
||||||
# Environment Variables:
|
# Environment Variables:
|
||||||
# ENVIRONMENT: 'development' or 'production' (default: development)
|
# ENVIRONMENT: 'development' or 'production' (default: development)
|
||||||
@ -28,7 +28,6 @@ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|||||||
CONDA_ENV="${CONDA_ENV:-AniWorld}"
|
CONDA_ENV="${CONDA_ENV:-AniWorld}"
|
||||||
ENVIRONMENT="${1:-development}"
|
ENVIRONMENT="${1:-development}"
|
||||||
INSTALL_DEPS="${INSTALL_DEPS:-true}"
|
INSTALL_DEPS="${INSTALL_DEPS:-true}"
|
||||||
RUN_MIGRATIONS="${RUN_MIGRATIONS:-true}"
|
|
||||||
PORT="${PORT:-8000}"
|
PORT="${PORT:-8000}"
|
||||||
HOST="${HOST:-127.0.0.1}"
|
HOST="${HOST:-127.0.0.1}"
|
||||||
|
|
||||||
@ -104,20 +103,6 @@ install_dependencies() {
|
|||||||
log_success "Dependencies installed."
|
log_success "Dependencies installed."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run database migrations
|
|
||||||
run_migrations() {
|
|
||||||
if [[ "$RUN_MIGRATIONS" != "true" ]]; then
|
|
||||||
log_warning "Skipping database migrations."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Running database migrations..."
|
|
||||||
cd "$PROJECT_ROOT"
|
|
||||||
conda run -n "$CONDA_ENV" \
|
|
||||||
python -m alembic upgrade head 2>/dev/null || log_warning "No migrations to run."
|
|
||||||
log_success "Database migrations completed."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Initialize database
|
# Initialize database
|
||||||
init_database() {
|
init_database() {
|
||||||
log_info "Initializing database..."
|
log_info "Initializing database..."
|
||||||
@ -220,10 +205,6 @@ main() {
|
|||||||
INSTALL_DEPS="false"
|
INSTALL_DEPS="false"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
--no-migrate)
|
|
||||||
RUN_MIGRATIONS="false"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
ENVIRONMENT="$1"
|
ENVIRONMENT="$1"
|
||||||
shift
|
shift
|
||||||
@ -237,7 +218,6 @@ main() {
|
|||||||
create_env_file
|
create_env_file
|
||||||
install_dependencies
|
install_dependencies
|
||||||
init_database
|
init_database
|
||||||
run_migrations
|
|
||||||
start_application
|
start_application
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,6 @@ async def setup_auth(req: SetupRequest):
|
|||||||
|
|
||||||
This endpoint also initializes the configuration with default values
|
This endpoint also initializes the configuration with default values
|
||||||
and saves the anime directory and master password hash.
|
and saves the anime directory and master password hash.
|
||||||
If anime_directory is provided, runs migration for existing data files.
|
|
||||||
"""
|
"""
|
||||||
if auth_service.is_configured():
|
if auth_service.is_configured():
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@ -214,14 +214,14 @@ def update_advanced_config(
|
|||||||
async def update_directory(
|
async def update_directory(
|
||||||
directory_config: Dict[str, str], auth: dict = Depends(require_auth)
|
directory_config: Dict[str, str], auth: dict = Depends(require_auth)
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Update anime directory configuration and run migration.
|
"""Update anime directory configuration.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
directory_config: Dictionary with 'directory' key
|
directory_config: Dictionary with 'directory' key
|
||||||
auth: Authentication token (required)
|
auth: Authentication token (required)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Success message with optional migration results
|
Success message
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
directory = directory_config.get("directory")
|
directory = directory_config.get("directory")
|
||||||
|
|||||||
@ -13,7 +13,7 @@ This package provides persistent storage for anime series, episodes, download qu
|
|||||||
Install required dependencies:
|
Install required dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install sqlalchemy alembic aiosqlite
|
pip install sqlalchemy aiosqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use the project requirements:
|
Or use the project requirements:
|
||||||
@ -163,24 +163,6 @@ from src.config.settings import settings
|
|||||||
settings.database_url = "sqlite:///./data/aniworld.db"
|
settings.database_url = "sqlite:///./data/aniworld.db"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Migrations (Future)
|
|
||||||
|
|
||||||
Alembic is installed for database migrations:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Initialize Alembic
|
|
||||||
alembic init alembic
|
|
||||||
|
|
||||||
# Generate migration
|
|
||||||
alembic revision --autogenerate -m "Description"
|
|
||||||
|
|
||||||
# Apply migrations
|
|
||||||
alembic upgrade head
|
|
||||||
|
|
||||||
# Rollback
|
|
||||||
alembic downgrade -1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Run database tests:
|
Run database tests:
|
||||||
@ -196,7 +178,6 @@ The test suite uses an in-memory SQLite database for isolation and speed.
|
|||||||
- **base.py**: Base declarative class and mixins
|
- **base.py**: Base declarative class and mixins
|
||||||
- **models.py**: SQLAlchemy ORM models (4 models)
|
- **models.py**: SQLAlchemy ORM models (4 models)
|
||||||
- **connection.py**: Engine, session factory, dependency injection
|
- **connection.py**: Engine, session factory, dependency injection
|
||||||
- **migrations.py**: Alembic migration placeholder
|
|
||||||
- ****init**.py**: Package exports
|
- ****init**.py**: Package exports
|
||||||
- **service.py**: Service layer with CRUD operations
|
- **service.py**: Service layer with CRUD operations
|
||||||
|
|
||||||
@ -432,5 +413,4 @@ Solution: Ensure referenced records exist before creating relationships.
|
|||||||
## Further Reading
|
## Further Reading
|
||||||
|
|
||||||
- [SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/)
|
- [SQLAlchemy 2.0 Documentation](https://docs.sqlalchemy.org/en/20/)
|
||||||
- [Alembic Tutorial](https://alembic.sqlalchemy.org/en/latest/tutorial.html)
|
|
||||||
- [FastAPI with Databases](https://fastapi.tiangolo.com/tutorial/sql-databases/)
|
- [FastAPI with Databases](https://fastapi.tiangolo.com/tutorial/sql-databases/)
|
||||||
|
|||||||
@ -313,7 +313,6 @@ async def get_schema_version(engine: Optional[AsyncEngine] = None) -> str:
|
|||||||
"""Get current database schema version.
|
"""Get current database schema version.
|
||||||
|
|
||||||
Returns version string based on existing tables and structure.
|
Returns version string based on existing tables and structure.
|
||||||
For production, consider using Alembic versioning.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
engine: Optional database engine (uses default if not provided)
|
engine: Optional database engine (uses default if not provided)
|
||||||
|
|||||||
@ -51,7 +51,7 @@ async def lifespan(app: FastAPI):
|
|||||||
try:
|
try:
|
||||||
logger.info("Starting FastAPI application...")
|
logger.info("Starting FastAPI application...")
|
||||||
|
|
||||||
# Initialize database first (required for migration and other services)
|
# Initialize database first (required for other services)
|
||||||
try:
|
try:
|
||||||
from src.server.database.connection import init_db
|
from src.server.database.connection import init_db
|
||||||
await init_db()
|
await init_db()
|
||||||
|
|||||||
@ -4,7 +4,7 @@ This service handles:
|
|||||||
- Loading and saving configuration to JSON files
|
- Loading and saving configuration to JSON files
|
||||||
- Configuration validation
|
- Configuration validation
|
||||||
- Backup and restore functionality
|
- Backup and restore functionality
|
||||||
- Configuration migration for version updates
|
- Configuration version management
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@ -35,8 +35,8 @@ class ConfigBackupError(ConfigServiceError):
|
|||||||
class ConfigService:
|
class ConfigService:
|
||||||
"""Service for managing application configuration persistence.
|
"""Service for managing application configuration persistence.
|
||||||
|
|
||||||
Handles loading, saving, validation, backup, and migration of
|
Handles loading, saving, validation, backup, and version management
|
||||||
configuration files. Uses JSON format for human-readable and
|
of configuration files. Uses JSON format for human-readable and
|
||||||
version-control friendly storage.
|
version-control friendly storage.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -84,11 +84,6 @@ class ConfigService:
|
|||||||
with open(self.config_path, "r", encoding="utf-8") as f:
|
with open(self.config_path, "r", encoding="utf-8") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
|
||||||
# Check if migration is needed
|
|
||||||
file_version = data.get("version", "1.0.0")
|
|
||||||
if file_version != self.CONFIG_VERSION:
|
|
||||||
data = self._migrate_config(data, file_version)
|
|
||||||
|
|
||||||
# Remove version key before constructing AppConfig
|
# Remove version key before constructing AppConfig
|
||||||
data.pop("version", None)
|
data.pop("version", None)
|
||||||
|
|
||||||
@ -329,26 +324,6 @@ class ConfigService:
|
|||||||
# Ignore errors during cleanup
|
# Ignore errors during cleanup
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def _migrate_config(
|
|
||||||
self, data: Dict, from_version: str # noqa: ARG002
|
|
||||||
) -> Dict:
|
|
||||||
"""Migrate configuration from old version to current.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: Configuration data to migrate
|
|
||||||
from_version: Version to migrate from (reserved for future use)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict: Migrated configuration data
|
|
||||||
"""
|
|
||||||
# Currently only one version exists
|
|
||||||
# Future migrations would go here
|
|
||||||
# Example:
|
|
||||||
# if from_version == "1.0.0" and self.CONFIG_VERSION == "2.0.0":
|
|
||||||
# data = self._migrate_1_0_to_2_0(data)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
# Singleton instance
|
# Singleton instance
|
||||||
_config_service: Optional[ConfigService] = None
|
_config_service: Optional[ConfigService] = None
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user