feat: 完善 MVP-2 演示和进度体验
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
from shutil import copy2
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sqlalchemy import Engine, create_engine, event
|
||||
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
||||
@@ -30,6 +32,16 @@ def create_sqlite_engine(database_url: str):
|
||||
return engine
|
||||
|
||||
|
||||
def sqlite_file_path(target_engine: Engine = None) -> Path | None:
|
||||
target_engine = target_engine or engine
|
||||
if target_engine.url.get_backend_name() != "sqlite":
|
||||
return None
|
||||
database = target_engine.url.database
|
||||
if not database or database == ":memory:":
|
||||
return None
|
||||
return Path(database)
|
||||
|
||||
|
||||
engine = create_sqlite_engine(get_settings().database_url)
|
||||
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
|
||||
@@ -63,10 +75,39 @@ def checkpoint_sqlite_wal(target_engine: Engine = engine) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def ensure_sqlite_schema_compat(target_engine: Engine = engine) -> None:
|
||||
if target_engine.url.get_backend_name() != "sqlite":
|
||||
return
|
||||
with target_engine.begin() as connection:
|
||||
task_columns = {row[1] for row in connection.exec_driver_sql("PRAGMA table_info(tasks)").all()}
|
||||
if "current_stage" not in task_columns:
|
||||
connection.exec_driver_sql("ALTER TABLE tasks ADD COLUMN current_stage VARCHAR(64)")
|
||||
if "last_progress_at" not in task_columns:
|
||||
connection.exec_driver_sql("ALTER TABLE tasks ADD COLUMN last_progress_at DATETIME")
|
||||
|
||||
|
||||
def backup_sqlite_files(target_engine: Engine = engine) -> list[Path]:
|
||||
db_path = sqlite_file_path(target_engine)
|
||||
if db_path is None:
|
||||
return []
|
||||
backup_dir = db_path.parent / "corrupt-backups"
|
||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||
timestamp = datetime.now(UTC).strftime("%Y%m%d-%H%M%S")
|
||||
copied: list[Path] = []
|
||||
for source in [db_path, db_path.with_name(f"{db_path.name}-wal"), db_path.with_name(f"{db_path.name}-shm")]:
|
||||
if not source.exists():
|
||||
continue
|
||||
target = backup_dir / f"{source.name}.{timestamp}.bak"
|
||||
copy2(source, target)
|
||||
copied.append(target)
|
||||
return copied
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
import app.models # noqa: F401
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
ensure_sqlite_schema_compat(engine)
|
||||
check_database_integrity(engine)
|
||||
checkpoint_sqlite_wal(engine)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user