feat: Update CORS origins to allow Chrome extensions and improve logging in migration tool
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
44
migrate.py
44
migrate.py
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user