From c21a13d2a30dba503a24c9f92ce9cd2d3918e801 Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:02:25 +0300 Subject: [PATCH] rebase --- CHANGELOG.md | 11 ++++++++ README.md | 4 +-- app/main.py | 25 ++++++++++++++++++- ...itial_schema.py => _001_initial_schema.py} | 0 4 files changed, 37 insertions(+), 3 deletions(-) rename app/migrations/versions/{001_initial_schema.py => _001_initial_schema.py} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba0a24d..6ae7086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index 1fc3a80..2b39fbc 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/main.py b/app/main.py index 629d606..bb04f2c 100644 --- a/app/main.py +++ b/app/main.py @@ -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 diff --git a/app/migrations/versions/001_initial_schema.py b/app/migrations/versions/_001_initial_schema.py similarity index 100% rename from app/migrations/versions/001_initial_schema.py rename to app/migrations/versions/_001_initial_schema.py