feat: Update CORS origins to allow Chrome extensions and improve logging in migration tool
Some checks failed
Backend CI / lint (push) Failing after 10s
Backend CI / test (push) Failing after 1m37s

This commit is contained in:
JSC
2025-09-19 16:41:11 +02:00
parent 1bef694f38
commit bccfcafe0e
9 changed files with 72 additions and 32 deletions

View File

@@ -23,7 +23,10 @@ class Settings(BaseSettings):
BACKEND_URL: str = "http://localhost:8000" # Backend base URL
# CORS Configuration
CORS_ORIGINS: list[str] = ["http://localhost:8001"] # Allowed origins for CORS
CORS_ORIGINS: list[str] = [
"http://localhost:8001", # Frontend development
"chrome-extension://*", # Chrome extensions
]
# Database Configuration
DATABASE_URL: str = "sqlite+aiosqlite:///data/soundboard.db"
@@ -37,7 +40,9 @@ class Settings(BaseSettings):
LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# JWT Configuration
JWT_SECRET_KEY: str = "your-secret-key-change-in-production" # noqa: S105 default value if none set in .env
JWT_SECRET_KEY: str = (
"your-secret-key-change-in-production" # noqa: S105 default value if none set in .env
)
JWT_ALGORITHM: str = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
JWT_REFRESH_TOKEN_EXPIRE_DAYS: int = 7

View File

@@ -1,11 +1,12 @@
from collections.abc import AsyncGenerator, Callable
from alembic.config import Config
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
# Import all models to ensure SQLModel metadata discovery
import app.models # noqa: F401
from alembic import command
from app.core.config import settings
from app.core.logging import get_logger
@@ -44,8 +45,6 @@ async def init_db() -> None:
try:
logger.info("Running database migrations")
# Run Alembic migrations programmatically
from alembic import command
from alembic.config import Config
# Get the alembic config
alembic_cfg = Config("alembic.ini")

View File

@@ -598,7 +598,9 @@ class CreditService:
current_credits = user.credits
plan_credits = user.plan.credits
max_credits = user.plan.max_credits
target_credits = min(current_credits + plan_credits, max_credits)
target_credits = min(
current_credits + plan_credits, max_credits,
)
credits_added = target_credits - current_credits
stats["total_credits_added"] += credits_added
else:

View File

@@ -348,8 +348,12 @@ class SchedulerService:
# Check if task is still active and pending
if not task.is_active or task.status != TaskStatus.PENDING:
logger.warning(
"Task %s execution skipped - is_active: %s, status: %s (should be %s)",
task_id, task.is_active, task.status, TaskStatus.PENDING,
"Task %s execution skipped - is_active: %s, status: %s "
"(should be %s)",
task_id,
task.is_active,
task.status,
TaskStatus.PENDING,
)
return
@@ -364,7 +368,9 @@ class SchedulerService:
# Mark task as running
logger.info(
"Task %s starting execution (type: %s)", task_id, task.recurrence_type,
"Task %s starting execution (type: %s)",
task_id,
task.recurrence_type,
)
await repo.mark_as_running(task)
@@ -383,7 +389,8 @@ class SchedulerService:
# For CRON tasks, update execution metadata but keep PENDING
# APScheduler handles the recurring schedule automatically
logger.info(
"Task %s (CRON) executed successfully, updating metadata", task_id,
"Task %s (CRON) executed successfully, updating metadata",
task_id,
)
task.last_executed_at = datetime.now(tz=UTC)
task.executions_count += 1
@@ -392,8 +399,11 @@ class SchedulerService:
session.add(task)
await session.commit()
logger.info(
"Task %s (CRON) metadata updated, status: %s, executions: %s",
task_id, task.status, task.executions_count,
"Task %s (CRON) metadata updated, status: %s, "
"executions: %s",
task_id,
task.status,
task.executions_count,
)
else:
# For non-CRON recurring tasks, calculate next execution

View File

@@ -80,11 +80,19 @@ class TaskHandlerRegistry:
msg = f"Invalid user_id format: {user_id}"
raise TaskExecutionError(msg) from e
transaction = await self.credit_service.recharge_user_credits_auto(user_id_int)
transaction = await self.credit_service.recharge_user_credits_auto(
user_id_int,
)
if transaction:
logger.info("Recharged credits for user %s: %s credits added", user_id, transaction.amount)
logger.info(
"Recharged credits for user %s: %s credits added",
user_id,
transaction.amount,
)
else:
logger.info("No credits added for user %s (already at maximum)", user_id)
logger.info(
"No credits added for user %s (already at maximum)", user_id,
)
else:
# Recharge all users (system task)
stats = await self.credit_service.recharge_all_users_credits()

View File

@@ -2,25 +2,33 @@
"""Database migration CLI tool."""
import argparse
import logging
import sys
from pathlib import Path
from alembic import command
from alembic.config import Config
from alembic import command
# Set up logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format="%(message)s")
def main() -> None:
"""Main CLI function for database migrations."""
"""Run database migration CLI tool."""
parser = argparse.ArgumentParser(description="Database migration tool")
subparsers = parser.add_subparsers(dest="command", help="Migration commands")
# Upgrade command
upgrade_parser = subparsers.add_parser("upgrade", help="Upgrade database to latest revision")
upgrade_parser = subparsers.add_parser(
"upgrade", help="Upgrade database to latest revision",
)
upgrade_parser.add_argument(
"revision",
nargs="?",
default="head",
help="Target revision (default: head)"
help="Target revision (default: head)",
)
# Downgrade command
@@ -35,8 +43,12 @@ def main() -> None:
# Generate migration command
revision_parser = subparsers.add_parser("revision", help="Create new migration")
revision_parser.add_argument("-m", "--message", required=True, help="Migration message")
revision_parser.add_argument("--autogenerate", action="store_true", help="Auto-generate migration")
revision_parser.add_argument(
"-m", "--message", required=True, help="Migration message",
)
revision_parser.add_argument(
"--autogenerate", action="store_true", help="Auto-generate migration",
)
args = parser.parse_args()
@@ -47,7 +59,7 @@ def main() -> None:
# Get the alembic config
config_path = Path("alembic.ini")
if not config_path.exists():
print("Error: alembic.ini not found. Run from the backend directory.")
logger.error("Error: alembic.ini not found. Run from the backend directory.")
sys.exit(1)
alembic_cfg = Config(str(config_path))
@@ -55,11 +67,15 @@ def main() -> None:
try:
if args.command == "upgrade":
command.upgrade(alembic_cfg, args.revision)
print(f"Successfully upgraded database to revision: {args.revision}")
logger.info(
"Successfully upgraded database to revision: %s", args.revision,
)
elif args.command == "downgrade":
command.downgrade(alembic_cfg, args.revision)
print(f"Successfully downgraded database to revision: {args.revision}")
logger.info(
"Successfully downgraded database to revision: %s", args.revision,
)
elif args.command == "current":
command.current(alembic_cfg)
@@ -70,15 +86,15 @@ def main() -> None:
elif args.command == "revision":
if args.autogenerate:
command.revision(alembic_cfg, message=args.message, autogenerate=True)
print(f"Created new auto-generated migration: {args.message}")
logger.info("Created new auto-generated migration: %s", args.message)
else:
command.revision(alembic_cfg, message=args.message)
print(f"Created new empty migration: {args.message}")
logger.info("Created new empty migration: %s", args.message)
except Exception as e:
print(f"Error: {e}")
except (OSError, RuntimeError):
logger.exception("Error occurred during migration")
sys.exit(1)
if __name__ == "__main__":
main()
main()

View File

@@ -196,7 +196,7 @@ class TestApiTokenDependencies:
) -> None:
"""Test flexible authentication falls back to JWT when no API token."""
# Mock the get_current_user function (normally imported)
with pytest.raises(Exception, match="Database error|Could not validate"):
with pytest.raises(Exception, match=r"Database error|Could not validate"):
# This will fail because we can't easily mock the get_current_user import
# In a real test, you'd mock the import or use dependency injection
await get_current_user_flexible(mock_auth_service, "jwt_token", None)

View File

@@ -110,7 +110,7 @@ class TestUserRepository:
test_session: AsyncSession,
) -> None:
"""Test creating a new user."""
free_plan, pro_plan = ensure_plans
free_plan, _pro_plan = ensure_plans
plan_id = free_plan.id
plan_credits = free_plan.credits

View File

@@ -42,7 +42,7 @@ class TestAuthService:
assert response.user.role == "admin" # First user gets admin role
assert response.user.is_active is True
# First user gets pro plan
free_plan, pro_plan = ensure_plans
_free_plan, pro_plan = ensure_plans
assert response.user.credits == pro_plan.credits
assert response.user.plan["code"] == pro_plan.code