ppt-tool/backend/migrations/env.py
Vadym Samoilenko d3d1667a79 Phase 2: Admin panel, analytics, storage, template pipeline, multi-provider LLM
- Fix admin sidebar: remove duplicate Teams, add Storage nav item
- Analytics: client-scoped queries, super_admin sees all (including NULL client_id)
- Storage management: list/download/delete presentations with file metadata
- Settings page with brand config router
- AI usage tracking: new AIUsageModel, ai_usage_service, analytics endpoint
- Master deck → template bridge: _register_as_template creates TemplateModel
  + PresentationLayoutCodeModel so parsed layouts appear in template picker
- Multi-provider LLM vision in parser: Anthropic/Google/OpenAI with asyncio.to_thread
- Fix PPTX upload 400: accept by .pptx extension (browser sends octet-stream)
- Fix reparse FK violation: presentation_id=None for parse_master_deck jobs
- Worker job_timeout increased to 1800s for LLM-heavy master deck parsing
- PYTHONUNBUFFERED=1 in docker-compose worker for real-time log output
- Auth: clientId in /me response, dev-login cookie improvements
- Frontend: auth slice clientId, master-deck thumbnails, storage page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:39:34 +00:00

76 lines
2.7 KiB
Python

import asyncio
from logging.config import fileConfig
from sqlalchemy import pool
from sqlalchemy.ext.asyncio import async_engine_from_config
from alembic import context
from sqlmodel import SQLModel
# Import ALL models so they register with SQLModel.metadata
# Existing models
from models.sql.presentation import PresentationModel # noqa: F401
from models.sql.slide import SlideModel # noqa: F401
from models.sql.key_value import KeyValueSqlModel # noqa: F401
from models.sql.image_asset import ImageAsset # noqa: F401
from models.sql.presentation_layout_code import PresentationLayoutCodeModel # noqa: F401
from models.sql.template import TemplateModel # noqa: F401
from models.sql.webhook_subscription import WebhookSubscription # noqa: F401
from models.sql.async_presentation_generation_status import AsyncPresentationGenerationTaskModel # noqa: F401
from models.sql.ollama_pull_status import OllamaPullStatus # noqa: F401
# New models
from models.sql.user import UserModel # noqa: F401
from models.sql.client import ClientModel # noqa: F401
from models.sql.team import TeamModel # noqa: F401
from models.sql.team_membership import TeamMembershipModel # noqa: F401
from models.sql.brand_config import BrandConfigModel # noqa: F401
from models.sql.master_deck import MasterDeckModel # noqa: F401
from models.sql.audit_log import AuditLogModel # noqa: F401
from models.sql.job import JobModel # noqa: F401
from models.sql.ai_usage import AIUsageModel # noqa: F401
from utils.db_utils import get_database_url_and_connect_args
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = SQLModel.metadata
def run_migrations_offline() -> None:
url, _ = get_database_url_and_connect_args()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def do_run_migrations(connection) -> None:
context.configure(connection=connection, target_metadata=target_metadata)
with context.begin_transaction():
context.run_migrations()
async def run_migrations_online() -> None:
url, connect_args = get_database_url_and_connect_args()
connectable = async_engine_from_config(
{"sqlalchemy.url": url},
prefix="sqlalchemy.",
poolclass=pool.NullPool,
connect_args=connect_args,
)
async with connectable.connect() as connection:
await connection.run_sync(do_run_migrations)
await connectable.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
asyncio.run(run_migrations_online())