zephyrdark 1dae2945c3
All checks were successful
Deploy to Production / deploy (push) Successful in 1m31s
feat: client-side password hashing and admin user auto-seeding
- Hash passwords with SHA-256 on frontend before transmission to prevent
  raw password exposure in network traffic and server logs
- Switch login endpoint from OAuth2 form-data to JSON body
- Auto-create admin user on startup from ADMIN_USERNAME/ADMIN_PASSWORD
  env vars, solving login failure after registration was disabled
- Update auth tests to match new SHA-256 + JSON login flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:21:36 +09:00

119 lines
3.2 KiB
Python

"""
Galaxis-Po Backend API
"""
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api import (
auth_router, admin_router, portfolio_router, strategy_router,
market_router, backtest_router, snapshot_router,
)
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
def _seed_admin_user() -> None:
"""Create admin user from env vars if not exists."""
from app.core.config import get_settings
from app.core.database import SessionLocal
from app.core.security import get_password_hash
from app.models.user import User
settings = get_settings()
if not settings.admin_username or not settings.admin_password:
logger.info("ADMIN_USERNAME/ADMIN_PASSWORD not set, skipping admin seed")
return
db = SessionLocal()
try:
existing = db.query(User).filter(User.username == settings.admin_username).first()
if existing:
logger.info(f"Admin user '{settings.admin_username}' already exists")
return
user = User(
username=settings.admin_username,
email=settings.admin_email or f"{settings.admin_username}@local",
hashed_password=get_password_hash(settings.admin_password),
)
db.add(user)
db.commit()
logger.info(f"Admin user '{settings.admin_username}' created")
except Exception as e:
db.rollback()
logger.error(f"Failed to seed admin user: {e}")
finally:
db.close()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager."""
# Startup
logger.info("Starting Galaxis-Po API...")
# Seed admin user
_seed_admin_user()
# Start scheduler (import here to avoid circular imports)
try:
from jobs.scheduler import start_scheduler
start_scheduler()
logger.info("Scheduler started")
except Exception as e:
logger.warning(f"Failed to start scheduler: {e}")
yield
# Shutdown
logger.info("Shutting down Galaxis-Po API...")
try:
from jobs.scheduler import stop_scheduler
stop_scheduler()
logger.info("Scheduler stopped")
except Exception as e:
logger.warning(f"Failed to stop scheduler: {e}")
app = FastAPI(
title="Galaxis-Po API",
description="Quant Portfolio Management API",
version="0.1.0",
lifespan=lifespan,
)
from app.core.config import get_settings
_settings = get_settings()
app.add_middleware(
CORSMiddleware,
allow_origins=[origin.strip() for origin in _settings.cors_origins.split(",") if origin.strip()],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["*"],
)
# Include routers
app.include_router(auth_router)
app.include_router(admin_router)
app.include_router(portfolio_router)
app.include_router(strategy_router)
app.include_router(market_router)
app.include_router(backtest_router)
app.include_router(snapshot_router)
@app.get("/health")
async def health_check():
return {"status": "healthy"}