Files
ERP/pm/backend/app/main.py
T
MMD 2747f9d4bb chore(pm): vendor the Aramesh/PM platform under pm/ (squash of Aramesh/PM @ 91f3271)
The PM panel (PmPanelPage -> iframe /pm/) needs the project-management
platform served at /pm/ on the ERP domain. Source: org repo Aramesh/PM,
branch feature/project-management-platform. Update via:
git subtree pull --prefix=pm aramesh-pm feature/project-management-platform --squash
2026-10-07 15:14:20 +03:30

80 lines
2.7 KiB
Python

"""PM — Project Management operational layer. FastAPI entrypoint."""
import asyncio
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
from sqlalchemy import select
from app.config import settings
from app.db import SessionFactory, engine
from app.dev_accounts import ensure_demo_accounts, ensure_demo_assignments
from app.errors import install_error_handlers
from app.models import Base, PMUser
from app.routers import auth, dashboard, files, projects, qc, tasks, users
from app.security import hash_password
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s — %(message)s")
logging.getLogger("httpx").setLevel(logging.WARNING)
async def _ensure_admin() -> None:
"""Seed the local admin user (role: project_manager) from env on first boot."""
async with SessionFactory() as db:
existing = await db.scalar(select(PMUser).where(PMUser.username == settings.ADMIN_USER))
if existing is None:
db.add(
PMUser(
username=settings.ADMIN_USER,
password_hash=hash_password(settings.ADMIN_PASSWORD),
display_name="مدیر سیستم",
role="project_manager",
)
)
await db.commit()
logging.getLogger("pm.boot").info("seeded admin user %s", settings.ADMIN_USER)
# dev role switcher: demo accounts for the dashboard picker
await ensure_demo_accounts(db)
@asynccontextmanager
async def lifespan(_: FastAPI):
if settings.DEV_ROLE_SWITCH:
logging.getLogger("pm.boot").warning(
"PM_DEV_ROLE_SWITCH is ON — /auth/dev-login issues tokens without a password. "
"Set PM_DEV_ROLE_SWITCH=false for production."
)
async with engine.begin() as conn:
from app.schema_sync import drop_legacy, ensure_schema
await drop_legacy(conn)
await conn.run_sync(Base.metadata.create_all)
await ensure_schema(conn)
await _ensure_admin()
asyncio.create_task(_init_demo_assignments())
yield
async def _init_demo_assignments() -> None:
await asyncio.sleep(1)
try:
async with SessionFactory() as db:
await ensure_demo_assignments(db)
except Exception: # noqa: BLE001
logging.getLogger("pm.boot").exception("demo assignment failed")
app = FastAPI(title="PM — Construction Project Management Platform", version="1.0.0", lifespan=lifespan)
API_V1_PREFIX = "/api/v1"
for module in (auth, users, dashboard, projects, tasks, qc, files):
app.include_router(module.router, prefix=API_V1_PREFIX)
@app.get("/health")
async def health():
return {"status": "ok", "service": "pm-api"}
install_error_handlers(app)