chore: упрощены app.py, config.py и requirements.txt после выреза шаблона
Убраны SPA, CORS, SMB, РАИСА, SQLAlchemy-обработчики и зависимости БД/SMB; настройки: ENV_FILE, SERVICE_NAME, NATS_ENABLED, WORKER_CONCURRENCY, PUBLISH_RETRIES, DONE_CACHE_SIZE, MINIO_SECURE. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
b428f68e9e
commit
9cdc62b26d
@ -2,13 +2,9 @@ aiohappyeyeballs==2.7.1
|
|||||||
aiohttp==3.14.3
|
aiohttp==3.14.3
|
||||||
aiohttp-retry==2.9.1
|
aiohttp-retry==2.9.1
|
||||||
aiosignal==1.4.0
|
aiosignal==1.4.0
|
||||||
alembic==1.14.0
|
|
||||||
annotated-doc==0.0.4
|
annotated-doc==0.0.4
|
||||||
annotated-types==0.8.0
|
annotated-types==0.8.0
|
||||||
anyio==4.14.2
|
anyio==4.14.2
|
||||||
argon2-cffi==23.1.0
|
|
||||||
argon2-cffi-bindings==25.1.0
|
|
||||||
asyncpg==0.30.0
|
|
||||||
attrs==26.1.0
|
attrs==26.1.0
|
||||||
certifi==2025.11.12
|
certifi==2025.11.12
|
||||||
cffi==2.1.0
|
cffi==2.1.0
|
||||||
@ -20,7 +16,6 @@ fastapi-cli==0.0.24
|
|||||||
fastapi-cloud-cli==0.14.1
|
fastapi-cloud-cli==0.14.1
|
||||||
fastar==0.8.0
|
fastar==0.8.0
|
||||||
frozenlist==1.8.0
|
frozenlist==1.8.0
|
||||||
greenlet==3.4.0
|
|
||||||
h11==0.16.0
|
h11==0.16.0
|
||||||
httpcore==1.0.9
|
httpcore==1.0.9
|
||||||
httptools==0.7.1
|
httptools==0.7.1
|
||||||
@ -28,7 +23,6 @@ httpx==0.28.1
|
|||||||
idna==3.18
|
idna==3.18
|
||||||
Jinja2==3.1.6
|
Jinja2==3.1.6
|
||||||
loguru==0.7.3
|
loguru==0.7.3
|
||||||
Mako==1.3.10
|
|
||||||
markdown-it-py==4.2.0
|
markdown-it-py==4.2.0
|
||||||
MarkupSafe==3.0.3
|
MarkupSafe==3.0.3
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
@ -48,14 +42,11 @@ PyNaCl==1.5.0
|
|||||||
python-dotenv==1.2.2
|
python-dotenv==1.2.2
|
||||||
python-multipart==0.0.32
|
python-multipart==0.0.32
|
||||||
PyYAML==6.0.3
|
PyYAML==6.0.3
|
||||||
raisa_fastapi_protected_api==1.0.0
|
|
||||||
rich==15.0.0
|
rich==15.0.0
|
||||||
rich-toolkit==0.19.7
|
rich-toolkit==0.19.7
|
||||||
rignore==0.7.6
|
rignore==0.7.6
|
||||||
sentry-sdk==2.66.1
|
sentry-sdk==2.66.1
|
||||||
shellingham==1.5.4
|
shellingham==1.5.4
|
||||||
SQLAlchemy==2.0.36
|
|
||||||
smbprotocol==1.15.0
|
|
||||||
starlette==1.3.1
|
starlette==1.3.1
|
||||||
typer==0.27.0
|
typer==0.27.0
|
||||||
typing-inspection==0.4.2
|
typing-inspection==0.4.2
|
||||||
|
|||||||
177
api/src/app.py
177
api/src/app.py
@ -1,21 +1,16 @@
|
|||||||
"""Точка входа FastAPI. Каркас из шаблона команды: инфраструктура без доменного кода.
|
"""Точка входа FastAPI сервиса tabular-parser.
|
||||||
|
|
||||||
Что здесь есть: настройки, подключение S3 (MinIO), NATS (nkeys JWT), SMB (горячие папки ИС Сканирование),
|
Что здесь есть: настройки, подключение S3 (MinIO), NATS (nkeys JWT; пустые seed/jwt — без аутентификации),
|
||||||
middleware (gzip, proxy headers, CORS, РАИСА при DEBUG=False), healthcheck, раздача собранного SPA.
|
middleware (gzip, proxy headers), healthcheck, обработчики исключений. Сервис без БД и фронта (DECISION-4).
|
||||||
Что добавляет Срез 0: роутеры в src/api/router.py, outbox-паблишер и consumer recognition.result
|
Воркер обработки `recognition.request` и поле `nats` в healthcheck добавляются позже (src/core/nats.py).
|
||||||
(docs/context/03-architecture.md), зависимость require(permission).
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import nkeys
|
import nkeys
|
||||||
import smbclient
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.exceptions import HTTPException, StarletteHTTPException
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
|
||||||
from fastapi.staticfiles import StaticFiles
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from miniopy_async import Minio
|
from miniopy_async import Minio
|
||||||
from nats.aio.client import Client as NATS
|
from nats.aio.client import Client as NATS
|
||||||
@ -26,120 +21,86 @@ from src.api.router import router
|
|||||||
from src.core.clients import S3Storage
|
from src.core.clients import S3Storage
|
||||||
from src.core.config import get_settings
|
from src.core.config import get_settings
|
||||||
from src.core.exception_handlers import register_exception_handlers
|
from src.core.exception_handlers import register_exception_handlers
|
||||||
|
from src.core.logger import setup_logger
|
||||||
|
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|
||||||
VERSION = settings.VERSION
|
VERSION = settings.VERSION
|
||||||
UP_TIME = datetime.now(timezone.utc)
|
UP_TIME = datetime.now(UTC)
|
||||||
|
|
||||||
|
setup_logger(level="DEBUG" if settings.DEBUG else "INFO")
|
||||||
|
|
||||||
|
|
||||||
# --- NATS: аутентификация по nkeys/JWT ---------------------------------------------------------
|
async def _connect_nats() -> NATS | None:
|
||||||
kp = nkeys.from_seed(settings.OPENBAO.NATS.NATS_SEED.encode("utf-8"))
|
"""Подключение к NATS (core, без JetStream); при ошибке — ``None``, приложение всё равно стартует."""
|
||||||
|
nats_settings = settings.OPENBAO.NATS
|
||||||
|
options: dict = {"max_reconnect_attempts": 2}
|
||||||
|
if nats_settings.NATS_SEED and nats_settings.NATS_JWT:
|
||||||
|
kp = nkeys.from_seed(nats_settings.NATS_SEED.encode("utf-8"))
|
||||||
|
|
||||||
|
def sig_cb(nonce: bytes | str) -> bytes:
|
||||||
|
if isinstance(nonce, str):
|
||||||
|
nonce = nonce.encode("utf-8")
|
||||||
|
return base64.b64encode(kp.sign(nonce))
|
||||||
|
|
||||||
def jwt_cb():
|
options |= {"user_jwt_cb": lambda: nats_settings.NATS_JWT.encode("utf-8"), "signature_cb": sig_cb}
|
||||||
return settings.OPENBAO.NATS.NATS_JWT.encode("utf-8")
|
nc = NATS()
|
||||||
|
|
||||||
|
|
||||||
def sig_cb(nonce):
|
|
||||||
if isinstance(nonce, str):
|
|
||||||
nonce = nonce.encode("utf-8")
|
|
||||||
return base64.b64encode(kp.sign(nonce))
|
|
||||||
|
|
||||||
|
|
||||||
# --- РАИСА: защищённый API (только вне DEBUG) ---------------------------------------------------
|
|
||||||
protected_middleware_available = False
|
|
||||||
if not settings.DEBUG:
|
|
||||||
try:
|
try:
|
||||||
from raisa_fastapi_protected_api import ( # pyright: ignore[reportMissingImports]
|
await nc.connect(nats_settings.NATS_URL, **options)
|
||||||
AuthorizationMiddleware,
|
except Exception as exc:
|
||||||
OpenEndpoint,
|
logger.exception(
|
||||||
ProtectedOAuthSettings,
|
"Не удалось подключиться к NATS. error_category=nats_error, error_type={error_type}, error={error}",
|
||||||
ProtectedRolesSettings,
|
error_type=type(exc).__name__,
|
||||||
ProtectedSettings,
|
error=repr(exc),
|
||||||
SearchType,
|
|
||||||
)
|
)
|
||||||
|
return None
|
||||||
protected_middleware_available = True
|
logger.info("Подключение к NATS произошло")
|
||||||
except ImportError:
|
return nc
|
||||||
protected_middleware_available = False
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_root_path(value: str) -> str:
|
async def _connect_s3() -> S3Storage | None:
|
||||||
if not value:
|
"""Клиент S3 с проверкой бакета; при недоступности — ``None`` (приложение всё равно стартует)."""
|
||||||
return ""
|
minio = settings.OPENBAO.MINIO
|
||||||
value = value.strip()
|
|
||||||
if value == "/":
|
|
||||||
return ""
|
|
||||||
return "/" + value.strip("/")
|
|
||||||
|
|
||||||
|
|
||||||
_APP_ROOT_PATH = _normalize_root_path(settings.ROOT_PATH)
|
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
|
||||||
async def lifespan(app: FastAPI):
|
|
||||||
app.state.nats_subs = []
|
|
||||||
app.state.storage = None
|
|
||||||
app.state.nats = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
smbclient.register_session(
|
|
||||||
settings.OPENBAO.SMB.SMB_HOST, settings.OPENBAO.SMB.SMB_LOGIN, settings.OPENBAO.SMB.SMB_PASSWORD
|
|
||||||
)
|
|
||||||
except Exception:
|
|
||||||
logger.exception("Ошибка при регистрации сессии SMB")
|
|
||||||
|
|
||||||
s3_client = Minio(
|
s3_client = Minio(
|
||||||
settings.OPENBAO.MINIO.MINIO_URL,
|
minio.MINIO_URL,
|
||||||
access_key=settings.OPENBAO.MINIO.MINIO_ADMIN_LOGIN,
|
access_key=minio.MINIO_ADMIN_LOGIN,
|
||||||
secret_key=settings.OPENBAO.MINIO.MINIO_ADMIN_PASSWORD,
|
secret_key=minio.MINIO_ADMIN_PASSWORD,
|
||||||
|
secure=minio.MINIO_SECURE,
|
||||||
cert_check=False,
|
cert_check=False,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
bucket_exists = await s3_client.bucket_exists(settings.OPENBAO.MINIO.MINIO_BUCKET)
|
bucket_exists = await s3_client.bucket_exists(minio.MINIO_BUCKET)
|
||||||
logger.info("Бакет проверен. bucket_exists={bucket_exists}", bucket_exists=bucket_exists)
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Не удалось проверить наличие бакета. error_category=s3_error, error_type={error_type}, error={error}",
|
"Не удалось проверить наличие бакета. error_category=s3_error, error_type={error_type}, error={error}",
|
||||||
error_type=type(exc).__name__,
|
error_type=type(exc).__name__,
|
||||||
error=repr(exc),
|
error=repr(exc),
|
||||||
)
|
)
|
||||||
return
|
await s3_client.close_session()
|
||||||
app.state.storage = S3Storage(client=s3_client, bucket_name=settings.OPENBAO.MINIO.MINIO_BUCKET)
|
return None
|
||||||
|
logger.info("Бакет проверен. bucket_exists={bucket_exists}", bucket_exists=bucket_exists)
|
||||||
|
return S3Storage(client=s3_client, bucket_name=minio.MINIO_BUCKET)
|
||||||
|
|
||||||
nc = NATS()
|
|
||||||
if not settings.DEBUG:
|
@asynccontextmanager
|
||||||
try:
|
async def lifespan(app: FastAPI):
|
||||||
await nc.connect(
|
app.state.storage = await _connect_s3()
|
||||||
settings.OPENBAO.NATS.NATS_URL, user_jwt_cb=jwt_cb, signature_cb=sig_cb, max_reconnect_attempts=2
|
app.state.nats = await _connect_nats() if settings.NATS_ENABLED else None
|
||||||
)
|
if not settings.NATS_ENABLED:
|
||||||
logger.info("Подключение к NATS произошло")
|
logger.info("NATS выключен (NATS_ENABLED=false)")
|
||||||
except Exception as exc:
|
|
||||||
logger.exception(
|
|
||||||
"Не удалось подключиться к NATS. error_category=nats_error, error_type={error_type}, error={error}",
|
|
||||||
error_type=type(exc).__name__,
|
|
||||||
error=repr(exc),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
app.state.nats = nc
|
|
||||||
# Подписки (recognition.result) и outbox-паблишер — Срез 0, см. docs/context/03-architecture.md
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info("Приложение запущено")
|
logger.info("Приложение запущено")
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
logger.info("Приложение закрывается")
|
logger.info("Приложение закрывается")
|
||||||
smbclient.reset_connection_cache()
|
if app.state.nats:
|
||||||
for sub in app.state.nats_subs:
|
|
||||||
await sub.unsubscribe()
|
|
||||||
if app.state.nats and not settings.DEBUG:
|
|
||||||
await app.state.nats.close()
|
await app.state.nats.close()
|
||||||
if app.state.storage:
|
if app.state.storage:
|
||||||
await app.state.storage.client.close_session()
|
await app.state.storage.client.close_session()
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="АРМ КА", version=VERSION, root_path=settings.ROOT_PATH, lifespan=lifespan)
|
app = FastAPI(title="tabular-parser", version=VERSION, root_path=settings.ROOT_PATH, lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/healthcheck", status_code=200)
|
@app.get("/healthcheck", status_code=200)
|
||||||
@ -153,37 +114,3 @@ register_exception_handlers(app)
|
|||||||
|
|
||||||
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=1)
|
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=1)
|
||||||
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"])
|
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"])
|
||||||
app.add_middleware(
|
|
||||||
CORSMiddleware,
|
|
||||||
allow_origins=["*"],
|
|
||||||
allow_credentials=True,
|
|
||||||
allow_methods=["*"],
|
|
||||||
allow_headers=["*"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# --- Раздача собранного SPA (web/) ---------------------------------------------------------------
|
|
||||||
os.makedirs("web", exist_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def sep_static(path: str, stat_dir: str) -> str:
|
|
||||||
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)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
app.mount("/", SPAStaticFiles(directory="web", html=True), name="web")
|
|
||||||
|
|||||||
@ -1,50 +1,62 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from pydantic import PositiveInt
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
class DBSettings(BaseSettings):
|
|
||||||
DATABASE_URL: str
|
|
||||||
|
|
||||||
class MinioSettings(BaseSettings):
|
class MinioSettings(BaseSettings):
|
||||||
MINIO_URL: str
|
MINIO_URL: str
|
||||||
MINIO_ADMIN_LOGIN: str
|
MINIO_ADMIN_LOGIN: str
|
||||||
MINIO_ADMIN_PASSWORD: str
|
MINIO_ADMIN_PASSWORD: str
|
||||||
MINIO_BUCKET: str
|
MINIO_BUCKET: str
|
||||||
|
MINIO_SECURE: bool = True # https (платформа); false — http (MinIO из docker compose)
|
||||||
|
|
||||||
|
|
||||||
class NATSSettings(BaseSettings):
|
class NATSSettings(BaseSettings):
|
||||||
NATS_URL: str
|
NATS_URL: str
|
||||||
NATS_SEED: str
|
NATS_SEED: str = "" # пусто — подключение к NATS без nkeys (локальная разработка, тесты)
|
||||||
NATS_JWT: str
|
NATS_JWT: str = ""
|
||||||
|
|
||||||
class SMBSettings(BaseSettings):
|
|
||||||
SMB_HOST: str
|
|
||||||
SMB_PATH: str
|
|
||||||
SMB_LOGIN: str
|
|
||||||
SMB_PASSWORD: str
|
|
||||||
|
|
||||||
class OpenBAOSettings(BaseSettings):
|
class OpenBAOSettings(BaseSettings):
|
||||||
DATABASE: DBSettings
|
|
||||||
MINIO: MinioSettings
|
MINIO: MinioSettings
|
||||||
NATS: NATSSettings
|
NATS: NATSSettings
|
||||||
SMB: SMBSettings
|
|
||||||
|
|
||||||
class ServerSettings(BaseSettings):
|
class ServerSettings(BaseSettings):
|
||||||
HOST: str = "0.0.0.0"
|
HOST: str = "0.0.0.0"
|
||||||
PORT: int = 8000
|
PORT: int = 8000
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
|
"""Настройки сервиса.
|
||||||
|
|
||||||
|
Файл окружения выбирается переменной ``ENV_FILE`` (по умолчанию ``.env``; значение читается при импорте модуля,
|
||||||
|
относительный путь считается от текущего каталога — ``api/``). Тесты используют ``.env.test``.
|
||||||
|
"""
|
||||||
|
|
||||||
OPENBAO: OpenBAOSettings
|
OPENBAO: OpenBAOSettings
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
server: ServerSettings = ServerSettings()
|
server: ServerSettings = ServerSettings()
|
||||||
ROOT_PATH: str = '/'
|
ROOT_PATH: str = "/"
|
||||||
VERSION: str = '1.0.0'
|
VERSION: str = "1.0.0"
|
||||||
|
|
||||||
|
SERVICE_NAME: str = "tabular-parser" # `service.name` конвертов контракта и имя queue group NATS
|
||||||
|
NATS_ENABLED: bool = True # false — приложение не подключается к NATS (воркер не стартует)
|
||||||
|
WORKER_CONCURRENCY: PositiveInt = 1 # число заданий, обрабатываемых одновременно
|
||||||
|
PUBLISH_RETRIES: PositiveInt = 3 # попытки публикации ack/результата в NATS
|
||||||
|
DONE_CACHE_SIZE: PositiveInt = 1000 # размер process-local LRU обработанных job_id
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
extra="ignore",
|
||||||
|
env_file=os.environ.get("ENV_FILE", ".env"),
|
||||||
|
env_nested_delimiter="__",
|
||||||
|
nested_model_default_partial_update=False,
|
||||||
|
)
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = "ignore"
|
|
||||||
env_file = ".env"
|
|
||||||
env_nested_delimiter = "__"
|
|
||||||
nested_model_default_partial_update = False
|
|
||||||
|
|
||||||
def get_settings() -> Settings:
|
def get_settings() -> Settings:
|
||||||
return Settings()
|
return Settings()
|
||||||
|
|
||||||
settings = get_settings()
|
|
||||||
|
settings = get_settings()
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from fastapi import FastAPI, HTTPException, Request, status
|
from fastapi import FastAPI, HTTPException, Request, status
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
|
||||||
|
|
||||||
|
|
||||||
def register_exception_handlers(app: FastAPI) -> None:
|
def register_exception_handlers(app: FastAPI) -> None:
|
||||||
@ -12,20 +11,6 @@ def register_exception_handlers(app: FastAPI) -> None:
|
|||||||
content={"detail": exc.errors()},
|
content={"detail": exc.errors()},
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.exception_handler(IntegrityError)
|
|
||||||
async def integrity_error_handler(_: Request, exc: IntegrityError):
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=status.HTTP_409_CONFLICT,
|
|
||||||
content={"detail": "нарушение целостности данных в БД"},
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(SQLAlchemyError)
|
|
||||||
async def sqlalchemy_error_handler(_: Request, exc: SQLAlchemyError):
|
|
||||||
return JSONResponse(
|
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
||||||
content={"detail": "ошибка базы данных"},
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.exception_handler(HTTPException)
|
@app.exception_handler(HTTPException)
|
||||||
async def http_exception_handler(_: Request, exc: HTTPException):
|
async def http_exception_handler(_: Request, exc: HTTPException):
|
||||||
# Не ломаем существующие ответы endpoint-ов и прокидываем исходный detail.
|
# Не ломаем существующие ответы endpoint-ов и прокидываем исходный detail.
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
### Task 2: [1.2] `src/app.py`
|
### Task 2: [1.2] `src/app.py`
|
||||||
|
|
||||||
- [ ] `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`; `src/core/config.py`: убрать `DBSettings`, `SMBSettings`, оставить `MINIO`, `NATS`; добавить `ENV_FILE`, `SERVICE_NAME="tabular-parser"`, `NATS_ENABLED=true`, `WORKER_CONCURRENCY=1`, `PUBLISH_RETRIES=3`, `DONE_CACHE_SIZE=1000`; `requirements.txt` без `asyncpg`, `SQLAlchemy`, `alembic`, `smbprotocol`, `argon2-cffi`, `raisa_fastapi_protected_api`; проверка: `pip install -r requirements.txt` в чистом venv (python3.11), `python run_server.py` стартует c `.env.example`, `/healthcheck` 200
|
- [x] `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`; `src/core/config.py`: убрать `DBSettings`, `SMBSettings`, оставить `MINIO`, `NATS`; добавить `ENV_FILE`, `SERVICE_NAME="tabular-parser"`, `NATS_ENABLED=true`, `WORKER_CONCURRENCY=1`, `PUBLISH_RETRIES=3`, `DONE_CACHE_SIZE=1000`; `requirements.txt` без `asyncpg`, `SQLAlchemy`, `alembic`, `smbprotocol`, `argon2-cffi`, `raisa_fastapi_protected_api`; проверка: `pip install -r requirements.txt` в чистом venv (python3.11), `python run_server.py` стартует c `.env.example`, `/healthcheck` 200
|
||||||
|
|
||||||
### Task 3: [1.3] `README.md` — описание сервиса (назначение, команды `make`, env, запуск против c
|
### Task 3: [1.3] `README.md` — описание сервиса (назначение, команды `make`, env, запуск против c
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
- [x] 1.1 Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`,
|
- [x] 1.1 Удалить `web/`, `setup/.npmrc`, npm-шаги из `setup/setup.sh`, `api/alembic/`, `api/alembic.ini`, `api/src/db/`,
|
||||||
`api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает
|
`api/src/models/`, `api/src/repositories/`; из `.gitignore` убрать `Dockerfile`; проверка: `git status` показывает
|
||||||
только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
только удаления и `.gitignore`; `python -c "import src"` пока падает ожидаемо (чинится в 1.2)
|
||||||
- [ ] 1.2 `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`;
|
- [x] 1.2 `src/app.py`: убрать mount SPA/`os.makedirs("web")`, CORS, SMB-сессию, импорт `raisa_fastapi_protected_api`;
|
||||||
`src/core/config.py`: убрать `DBSettings`, `SMBSettings`, оставить `MINIO`, `NATS`; добавить `ENV_FILE`,
|
`src/core/config.py`: убрать `DBSettings`, `SMBSettings`, оставить `MINIO`, `NATS`; добавить `ENV_FILE`,
|
||||||
`SERVICE_NAME="tabular-parser"`, `NATS_ENABLED=true`, `WORKER_CONCURRENCY=1`, `PUBLISH_RETRIES=3`, `DONE_CACHE_SIZE=1000`;
|
`SERVICE_NAME="tabular-parser"`, `NATS_ENABLED=true`, `WORKER_CONCURRENCY=1`, `PUBLISH_RETRIES=3`, `DONE_CACHE_SIZE=1000`;
|
||||||
`requirements.txt` без `asyncpg`, `SQLAlchemy`, `alembic`, `smbprotocol`, `argon2-cffi`, `raisa_fastapi_protected_api`;
|
`requirements.txt` без `asyncpg`, `SQLAlchemy`, `alembic`, `smbprotocol`, `argon2-cffi`, `raisa_fastapi_protected_api`;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user