This commit is contained in:
Mikan
2026-06-21 02:41:48 +03:00
parent c21a13d2a3
commit bd85e186dc
31 changed files with 1372 additions and 319 deletions

View File

@@ -27,6 +27,25 @@ from app.models import Setting, User
_logger = get_logger(__name__)
async def _apply_schema_fixups(engine) -> None:
"""Apply idempotent ALTER statements for schema fixes that create_all
cannot handle (e.g. changing NOT NULL → NULL on existing tables).
These run on every startup. Each statement is wrapped in try/except so
it silently succeeds if the fix was already applied.
"""
fixups = [
# world_presets.owner_id: was NOT NULL, now nullable (for system presets)
"ALTER TABLE world_presets ALTER COLUMN owner_id DROP NOT NULL",
]
async with engine.begin() as conn:
for sql in fixups:
try:
await conn.execute(text(sql))
except Exception: # noqa: BLE001
pass # already applied, or table doesn't exist yet
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
"""Application startup / shutdown lifecycle."""
@@ -44,10 +63,10 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
try:
engine = get_engine()
await create_all_tables(engine)
await _apply_schema_fixups(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:
@@ -56,7 +75,9 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
await seed_default_settings(session)
# 2) ensure admin setup token
token = await get_admin_setup_token(session)
# 3) check if any admin exists
# 3) check if any admin exists — ALWAYS print the setup URL
# (user requested: even if admin exists, show the token for
# reference / debugging)
from sqlalchemy import func
admins_count = (
@@ -65,16 +86,18 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
)
).scalar_one()
if admins_count == 0:
_logger.warning(
"no_admin_yet",
setup_url=f"/register/admin?token={token}",
)
print(f"\n=== AI-RPG Admin Setup ===")
print(f"No admin user yet. Open this URL in your browser:")
print(f" /register/admin?token={token}")
print(f"===========================\n")
_logger.warning("no_admin_yet", setup_url=f"/register/admin?token={token}")
print(f"\n{'=' * 60}")
print(f" AI-RPG — No admin user yet.")
print(f" Open this URL to create the first admin:")
print(f" http://localhost:8080/register/admin?token={token}")
print(f"{'=' * 60}\n")
else:
_logger.info("admins_present", count=admins_count)
# Still print the token URL for reference
print(f"\n Admin already exists. Admin register URL (for reference):")
print(f" /register/admin?token={token}")
print(f" (This URL is blocked since an admin already exists.)\n")
# 4) seed builtin presets (idempotent)
from app.migrations.seed import seed_builtin_presets
@@ -94,7 +117,6 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
_logger.warning("qdrant_init_failed", error=str(e))
except Exception as e: # noqa: BLE001
_logger.error("startup_failed", error=str(e))
# Don't crash — let /api/health reflect the broken state
pass
yield