232 lines
7.1 KiB
Python
232 lines
7.1 KiB
Python
import os
|
|
|
|
import smbclient
|
|
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.staticfiles import StaticFiles
|
|
from datetime import datetime, timezone
|
|
|
|
from src.core.PDFWorker.PDFTempPoller import PDFPollWorker
|
|
from .core.config import get_settings
|
|
# from .api.router import files_router
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from src.core.nats_callback import build_file_handler
|
|
from fastapi.exceptions import HTTPException, StarletteHTTPException
|
|
from src.api.router import le_router, files_router, cg_router, rf_office_router, osv_router, fsl_router
|
|
from miniopy_async import Minio
|
|
from src.core.config import settings
|
|
from src.core.clients import S3Storage, PollWorker
|
|
from src.core.async_cache import AsyncInMemoryCache
|
|
# from src.core.logger import setup_logger
|
|
from nats.aio.client import Client as NATS
|
|
import base64
|
|
import nkeys
|
|
from loguru import logger
|
|
|
|
import os
|
|
|
|
from fastapi.staticfiles import StaticFiles
|
|
from starlette.middleware.gzip import GZipMiddleware
|
|
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
|
|
|
from src.core.exception_handlers import register_exception_handlers
|
|
|
|
settings = get_settings()
|
|
|
|
VERSION = "1.0.0"
|
|
UP_TIME = datetime.now(timezone.utc)
|
|
|
|
|
|
async def sub_cb(msg):
|
|
return None
|
|
|
|
|
|
protected_middleware_available = False
|
|
if not settings.DEBUG:
|
|
try:
|
|
from raisa_fastapi_protected_api import ( # pyright: ignore[reportMissingImports]
|
|
AuthorizationMiddleware,
|
|
OpenEndpoint,
|
|
ProtectedOAuthSettings,
|
|
ProtectedRolesSettings,
|
|
ProtectedSettings,
|
|
SearchType,
|
|
)
|
|
|
|
protected_middleware_available = True
|
|
except ImportError:
|
|
protected_middleware_available = False
|
|
|
|
|
|
def _normalize_root_path(value: str) -> str:
|
|
if not value:
|
|
return ""
|
|
value = value.strip()
|
|
if value == "/":
|
|
return ""
|
|
return "/" + value.strip("/")
|
|
|
|
|
|
_APP_ROOT_PATH = _normalize_root_path(settings.ROOT_PATH)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app):
|
|
app.state.nats_subs = []
|
|
app.state.storage = None
|
|
app.state.nats = None
|
|
app.state.poller = None
|
|
app.state.pdf_poller = None
|
|
s3_client = None
|
|
smb_registered = False
|
|
try:
|
|
minio = settings.OPENBAO.MINIO
|
|
if minio is not None:
|
|
s3_client = Minio(
|
|
minio.MINIO_URL,
|
|
access_key=minio.MINIO_ADMIN_LOGIN,
|
|
secret_key=minio.MINIO_ADMIN_PASSWORD,
|
|
secure=minio.MINIO_SECURE,
|
|
)
|
|
if not await s3_client.bucket_exists(minio.MINIO_BUCKET):
|
|
raise RuntimeError(f"Бакет MinIO {minio.MINIO_BUCKET!r} не создан")
|
|
app.state.storage = S3Storage(s3_client, minio.MINIO_BUCKET)
|
|
logger.info("MinIO подключён, бакет: {}", minio.MINIO_BUCKET)
|
|
|
|
smb = settings.OPENBAO.SMB
|
|
if smb is not None:
|
|
smbclient.register_session(smb.SMB_HOST, smb.SMB_LOGIN, smb.SMB_PASSWORD,
|
|
port=smb.SMB_PORT)
|
|
smb_registered = True
|
|
logger.info("SMB подключён")
|
|
|
|
nats_settings = settings.OPENBAO.NATS
|
|
if nats_settings is not None:
|
|
nc = NATS()
|
|
app.state.nats = nc
|
|
if not settings.DEBUG:
|
|
keypair = nkeys.from_seed(nats_settings.NATS_SEED.encode("utf-8"))
|
|
|
|
def user_jwt_cb():
|
|
return nats_settings.NATS_JWT.encode("utf-8")
|
|
|
|
def signature_cb(nonce):
|
|
return base64.b64encode(keypair.sign(nonce.encode("utf-8")))
|
|
|
|
await nc.connect(nats_settings.NATS_URL, user_jwt_cb=user_jwt_cb,
|
|
signature_cb=signature_cb, max_reconnect_attempts=2)
|
|
logger.info("NATS подключён")
|
|
|
|
missing = [name for name in ("MINIO", "NATS", "SMB")
|
|
if getattr(settings.OPENBAO, name) is None]
|
|
if missing:
|
|
logger.info("Распознавание файлов отключено: не настроены {}", ", ".join(missing))
|
|
else:
|
|
cache = AsyncInMemoryCache()
|
|
app.state.poller = PollWorker(nc, cache, app.state.storage)
|
|
await app.state.poller.start()
|
|
app.state.pdf_poller = PDFPollWorker(cache, app.state.storage)
|
|
await app.state.pdf_poller.start()
|
|
|
|
logger.info("Приложение запущено")
|
|
yield
|
|
finally:
|
|
for worker in (app.state.poller, app.state.pdf_poller):
|
|
if worker is not None:
|
|
await worker.stop()
|
|
if not settings.DEBUG:
|
|
for sub in app.state.nats_subs:
|
|
await sub.unsubscribe()
|
|
if app.state.nats is not None:
|
|
await app.state.nats.close()
|
|
if s3_client is not None:
|
|
await s3_client.close_session()
|
|
if smb_registered:
|
|
smbclient.reset_connection_cache()
|
|
logger.info("Приложение закрывается")
|
|
|
|
app = FastAPI(
|
|
title="Fast API",
|
|
version=VERSION,
|
|
root_path=settings.ROOT_PATH,
|
|
lifespan=lifespan
|
|
)
|
|
|
|
@app.get(
|
|
"/healthcheck",
|
|
status_code=200
|
|
)
|
|
async def healthcheck():
|
|
return {
|
|
"uptime": UP_TIME,
|
|
"version": VERSION
|
|
}
|
|
|
|
|
|
app.include_router(le_router)
|
|
app.include_router(files_router)
|
|
app.include_router(cg_router)
|
|
app.include_router(rf_office_router)
|
|
app.include_router(osv_router)
|
|
app.include_router(fsl_router)
|
|
|
|
|
|
register_exception_handlers(app)
|
|
|
|
|
|
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=1)
|
|
|
|
|
|
|
|
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"])
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
|
|
#Создание каталога для web приложения
|
|
os.makedirs(os.path.join('web'), exist_ok=True)
|
|
|
|
#Подключаем все ендпоинты и роуты до маунта статических файлов
|
|
# app.include_router(files_router)
|
|
|
|
#Подключение собранного web приложения
|
|
|
|
|
|
def sep_static(path, stat_dir):
|
|
sep_path = path.split("/")
|
|
index = sep_path.index(stat_dir)
|
|
return "/".join(sep_path[index:])
|
|
|
|
|
|
class SPAStaticFiles(StaticFiles):
|
|
async def get_response(self, path: str, scope):
|
|
try:
|
|
return await super().get_response(path, scope)
|
|
except (HTTPException, StarletteHTTPException) as ex:
|
|
if ex.status_code == 404:
|
|
if "images" in path:
|
|
return await super().get_response(sep_static(path, "images"), scope)
|
|
if "static" in path:
|
|
return await super().get_response(sep_static(path, "static"), scope)
|
|
return await super().get_response("index.html", scope)
|
|
else:
|
|
raise ex
|
|
|
|
|
|
|
|
app.mount("/", SPAStaticFiles(directory="web", html=True), name="web")
|
|
|
|
app.mount("/", StaticFiles(directory="web", html=True), name="web")
|