from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from . import models # noqa: F401 — ثبت مدل‌ها پیش از ساخت جداول from .api import admin, forms, public from .config import settings from .database import Base, engine from .seo_pages import router as seo_router # ساخت جداول پایه — تغییرات بعدی اسکیما فقط با revisionهای Alembic اعمال می‌شوند # (مigrations/versions). اجرای اینجا برای اجرای مستقیم uvicorn در محیط توسعه است؛ # در کانتینر، CMD همین کار را قبل از alembic upgrade head انجام می‌دهد. Base.metadata.create_all(bind=engine) app = FastAPI( title="Aramesh Building Group API", description=( "بک‌اند سایت گروه ساختمانی آرامش — API محتوای سایت، فرم تماس و خبرنامه، " "و پنل مدیریت با احراز هویت JWT." ), version="1.0.0", docs_url="/docs", redoc_url="/redoc", ) app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=False, allow_methods=["*"], allow_headers=["*"], ) # پاسخ‌های JSON /api — فشرده‌سازی وقتی بدنه قابل‌توجه است app.add_middleware(GZipMiddleware, minimum_size=1024) # پیش‌نمایش خصوصی — لایه دوم جلوگیری از ایندکس (لایه اول، گیت ورود Traefik است). # روی همه پاسخ‌ها هدر noindex می‌نشیند تا حتی در صورت حذف گیت، گوگل چیزی ذخیره نکند. # همیشه ثبت می‌شود و پرچم در زمان هر درخواست خوانده می‌شود (خاموش‌کردن انتشار = فقط تغییر .env). @app.middleware("http") async def _preview_noindex(request, call_next): response = await call_next(request) if settings.preview_mode: response.headers["X-Robots-Tag"] = "noindex, nofollow, noarchive" return response # پاسخ‌های API هرگز کش مرورگر/CDN نشوند — دادهٔ پنل بعد از هر ذخیره فوراً fresh بماند @app.middleware("http") async def _api_no_store(request, call_next): response = await call_next(request) if request.url.path.startswith("/api"): response.headers["Cache-Control"] = "no-store" return response app.include_router(public.router, prefix="/api", tags=["عمومی"]) app.include_router(forms.router, prefix="/api", tags=["فرم‌ها"]) app.include_router(admin.auth_router, prefix="/api", tags=["احراز هویت"]) app.include_router(admin.router, prefix="/api/admin", tags=["پنل مدیریت"]) # robots.txt، sitemap.xml و HTML صفحات وبلاگ/پروژه با متای سئو — در ریشه دامنه app.include_router(seo_router, tags=["سئو"]) # متریک‌های Prometheus — فقط Prometheus در شبکه داخلی اسکرپ می‌کند # (Traefik مسیر /metrics را نمی‌شناسد، پس از بیرون در دسترس نیست) from prometheus_fastapi_instrumentator import Instrumentator # noqa: E402 Instrumentator().instrument(app).expose(app, endpoint="/metrics", include_in_schema=False) @app.get("/") def root(): return { "app": "Aramesh Building Group API", "docs": "/docs", "health": "/api/health", }