feat: add auth system, boss pokemon details, moves/abilities API, and run ownership
Some checks failed
CI / backend-tests (push) Failing after 1m16s
CI / frontend-tests (push) Successful in 57s

Add user authentication with login/signup/protected routes, boss pokemon
detail fields and result team tracking, moves and abilities selector
components and API, run ownership and visibility controls, and various
UI improvements across encounters, run list, and journal pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 21:41:38 +01:00
parent a6cb309b8b
commit 0a519e356e
69 changed files with 3574 additions and 693 deletions

View File

@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Assign existing unowned runs to a user.
Usage:
cd backend && uv run python scripts/assign_unowned_runs.py <user_uuid>
This script assigns all runs without an owner to the specified user.
Useful for migrating existing data after implementing user ownership.
"""
import asyncio
import sys
from uuid import UUID
from sqlalchemy import select, update
sys.path.insert(0, "src")
from app.core.database import async_session # noqa: E402
from app.models.nuzlocke_run import NuzlockeRun # noqa: E402
from app.models.user import User # noqa: E402
async def main(user_uuid: str) -> None:
try:
user_id = UUID(user_uuid)
except ValueError:
print(f"Error: Invalid UUID format: {user_uuid}")
sys.exit(1)
async with async_session() as session:
# Verify user exists
user_result = await session.execute(select(User).where(User.id == user_id))
user = user_result.scalar_one_or_none()
if user is None:
print(f"Error: User {user_id} not found")
sys.exit(1)
print(f"Found user: {user.email} (display_name: {user.display_name})")
# Count unowned runs
count_result = await session.execute(
select(NuzlockeRun.id, NuzlockeRun.name).where(
NuzlockeRun.owner_id.is_(None)
)
)
unowned_runs = count_result.all()
if not unowned_runs:
print("No unowned runs found.")
return
print(f"\nFound {len(unowned_runs)} unowned run(s):")
for run_id, run_name in unowned_runs:
print(f" - [{run_id}] {run_name}")
# Confirm action
confirm = input(f"\nAssign all {len(unowned_runs)} runs to this user? [y/N] ")
if confirm.lower() != "y":
print("Aborted.")
return
# Perform the update
await session.execute(
update(NuzlockeRun)
.where(NuzlockeRun.owner_id.is_(None))
.values(owner_id=user_id)
)
await session.commit()
print(f"\nAssigned {len(unowned_runs)} run(s) to user {user.email}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python scripts/assign_unowned_runs.py <user_uuid>")
print("\nExample:")
print(" uv run python scripts/assign_unowned_runs.py 550e8400-e29b-41d4-a716-446655440000")
sys.exit(1)
asyncio.run(main(sys.argv[1]))