diff --git a/backend/alembic.ini b/backend/alembic.ini new file mode 100644 index 0000000..47488b0 --- /dev/null +++ b/backend/alembic.ini @@ -0,0 +1,42 @@ +# Alembic configuration file. +# Run: alembic upgrade head (to apply migrations) +# Run: alembic revision --autogenerate -m "description" (to generate migration) + +[alembic] +script_location = alembic +prepend_sys_path = . +sqlalchemy.url = postgresql+asyncpg://agp_user:agp_password@localhost:5432/agp_db + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/backend/alembic/env.py b/backend/alembic/env.py new file mode 100644 index 0000000..d9411ca --- /dev/null +++ b/backend/alembic/env.py @@ -0,0 +1,65 @@ +"""Alembic environment configuration.""" + +import asyncio +from logging.config import fileConfig + +from alembic import context +from sqlalchemy.ext.asyncio import create_async_engine + +from app.config import settings +from app.database import Base +from app.models import user, session, world, asset # noqa: F401 + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +target_metadata = Base.metadata + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + url = settings.DATABASE_URL + 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): + context.configure(connection=connection, target_metadata=target_metadata) + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations(): + """Run migrations in 'online' mode with async engine.""" + connectable = create_async_engine(settings.DATABASE_URL, echo=False) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/backend/app/routers/users.py b/backend/app/routers/users.py index df7805c..6dc6a9d 100644 --- a/backend/app/routers/users.py +++ b/backend/app/routers/users.py @@ -13,7 +13,7 @@ from app.models.user import User router = APIRouter() -# ── Pydantic schemas ────────────────────────────────────────────────────────── +# ── Pydantic schemas ────────────────────────────────────────────────────── class UserCreate(BaseModel): username: str @@ -35,7 +35,7 @@ class UserUpdate(BaseModel): is_active: bool | None = None -# ── Endpoints ───────────────────────────────────────────────────────────────── +# ── Endpoints ───────────────────────────────────────────────────────────── @router.post( "/", @@ -57,9 +57,9 @@ async def create_user( if existing.scalar_one_or_none(): raise HTTPException(status_code=400, detail="Email already registered") - from app.routers.auth import _get_password_hash + from app.routers.auth import hash_password - hashed = _get_password_hash(user_in.password) + hashed = hash_password(user_in.password) user = User( username=user_in.username,