Files
filam3d/backend/app/main.py
2026-03-22 14:26:45 +03:00

112 lines
3.9 KiB
Python

import logging
import sys
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import select
from app.config import settings
from app.database import async_session, engine, Base
from app.models import Material, AdminUser
from app.seed.materials import MATERIALS
from app.services.auth import hash_password
from app.routers import calculate, materials, orders, ai_advisor, admin, clients, track
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s | %(levelname)-8s | %(name)-30s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
stream=sys.stdout,
)
logger = logging.getLogger("app.main")
# Reduce noise from third-party libs
logging.getLogger("uvicorn.access").setLevel(logging.INFO)
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.INFO)
logging.getLogger("httpcore").setLevel(logging.INFO)
logging.getLogger("trimesh").setLevel(logging.WARNING)
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("=== Application startup ===")
logger.info("Creating database tables...")
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
logger.info("Database tables created successfully")
logger.info("Checking seed data...")
async with async_session() as session:
result = await session.execute(select(Material).limit(1))
if result.scalar_one_or_none() is None:
logger.info("No materials found, seeding %d materials...", len(MATERIALS))
for mat_data in MATERIALS:
session.add(Material(**mat_data))
logger.debug(" Seeded material: %s", mat_data["name"])
await session.commit()
logger.info("Materials seeded successfully")
else:
logger.info("Materials already exist, skipping seed")
# Seed default admin user
logger.info("Checking admin user...")
async with async_session() as session:
result = await session.execute(select(AdminUser).limit(1))
if result.scalar_one_or_none() is None:
admin_user = AdminUser(
email=settings.ADMIN_DEFAULT_EMAIL,
password_hash=hash_password(settings.ADMIN_DEFAULT_PASSWORD),
name="Admin",
)
session.add(admin_user)
await session.commit()
logger.info("Default admin created: %s", settings.ADMIN_DEFAULT_EMAIL)
else:
logger.info("Admin user already exists, skipping")
logger.info("=== Application ready ===")
yield
logger.info("=== Application shutdown ===")
app = FastAPI(title="3D Print Calculator", version="1.0.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger.info("--> %s %s (client: %s)", request.method, request.url.path, request.client.host if request.client else "unknown")
logger.debug(" Headers: %s", dict(request.headers))
logger.debug(" Query params: %s", dict(request.query_params))
response = await call_next(request)
logger.info("<-- %s %s -> %d", request.method, request.url.path, response.status_code)
return response
app.include_router(calculate.router, prefix="/api")
app.include_router(materials.router, prefix="/api")
app.include_router(orders.router, prefix="/api")
app.include_router(ai_advisor.router, prefix="/api")
app.include_router(track.router, prefix="/api")
app.include_router(clients.router, prefix="/api/client")
app.include_router(admin.router, prefix="/api/admin")
@app.get("/api/health")
async def health():
logger.debug("Health check requested")
return {"status": "ok"}