This commit is contained in:
Mikan
2026-06-21 01:02:25 +03:00
parent 49ff467d3a
commit c21a13d2a3
4 changed files with 37 additions and 3 deletions

View File

@@ -4,6 +4,17 @@ All notable changes to AI-RPG are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.5] — 2026-06-20
### Fixed
- **app/main.py**: added `create_all_tables(engine)` call at the start of `lifespan`. Without this, the backend started but crashed on every DB query with `relation "settings" does not exist` because tables were never created in PostgreSQL. Tables are now created idempotently on every startup via `Base.metadata.create_all` ( SQLAlchemy skips tables that already exist).
- **app/main.py**: also added `seed_builtin_presets(session)` call so the 2 builtin presets (Classic Fantasy, Deep Space Outpost) are seeded on first startup, not just settings.
- **app/migrations/versions/**: renamed `001_initial_schema.py``_001_initial_schema.py`. Python module names cannot start with a digit, so `from app.migrations.versions.001_initial_schema import create_all_tables` was a SyntaxError. The leading underscore is a conventional marker for "internal" modules.
- **README.md**: updated references to the renamed migration file.
### Why this happened
The lifespan handler was supposed to run DB migrations as its first step, but I forgot to wire it up. The `seed_default_settings()` call immediately tried to `SELECT FROM settings` against a fresh PostgreSQL database with no tables. SQLAlchemy 2.x's `create_all` is idempotent (skips existing tables), so this is safe to call on every startup — equivalent to `alembic upgrade head` for our single-migration MVP.
## [1.0.4] — 2026-06-20
### Fixed (proper fix for CORS env parsing)

View File

@@ -107,7 +107,7 @@ pip install -r requirements.txt
docker compose up -d db qdrant
# Применить миграции (создаёт все таблицы)
python -c "import asyncio; from app.db import get_engine; from app.migrations.versions.001_initial_schema import create_all_tables; asyncio.run(create_all_tables(get_engine()))"
python -c "import asyncio; from app.db import get_engine; from app.migrations.versions._001_initial_schema import create_all_tables; asyncio.run(create_all_tables(get_engine()))"
# Запустить backend
uvicorn app.main:app --reload --port 8000
@@ -187,7 +187,7 @@ ai-rpg/
│ │ ├── init_db.py # init_db(session)
│ │ ├── init_qdrant.py # re-export init_qdrant_collections
│ │ ├── seed.py # builtin presets (fantasy, sci-fi)
│ │ └── versions/001_initial_schema.py
│ │ └── versions/_001_initial_schema.py
│ ├── config.py # Settings (pydantic-settings)
│ ├── db.py # async engine + session factory
│ └── main.py # FastAPI app + lifespan

View File

@@ -35,6 +35,20 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
_logger.info("app_starting", version=__version__, debug=cfg.debug)
# Startup -------------------------------------------------------------
# Step 0: Create all DB tables (idempotent — equivalent to alembic upgrade head).
# This must run BEFORE any settings query, otherwise seed_default_settings
# crashes with "relation 'settings' does not exist".
from app.db import get_engine
from app.migrations.versions._001_initial_schema import create_all_tables
try:
engine = get_engine()
await create_all_tables(engine)
_logger.info("db_tables_ready")
except Exception as e: # noqa: BLE001
_logger.error("db_tables_create_failed", error=str(e))
# Continue anyway — /api/health will reflect the broken state
sm = get_sessionmaker()
try:
async with sm() as session:
@@ -61,12 +75,21 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
print(f"===========================\n")
else:
_logger.info("admins_present", count=admins_count)
# 4) Init Qdrant collections
# 4) seed builtin presets (idempotent)
from app.migrations.seed import seed_builtin_presets
try:
await seed_builtin_presets(session)
_logger.info("presets_seeded")
except Exception as e: # noqa: BLE001
_logger.warning("presets_seed_failed", error=str(e))
# 5) Init Qdrant collections
from app.core.settings_service import get_setting
dim = int(await get_setting(session, "embeddings.dimension") or 256)
try:
await init_qdrant_collections(dim)
_logger.info("qdrant_collections_ready", dimension=dim)
except Exception as e: # noqa: BLE001
_logger.warning("qdrant_init_failed", error=str(e))
except Exception as e: # noqa: BLE001