6.4 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
dkb_armka — internal RSHB (aurora) app built on the "FastAPI template": a FastAPI backend (api/) plus a React SPA (web/) that the backend serves as static files. Domain: company groups, legal entities, regional offices (RF offices), uploaded financial documents (ОСВ / trial balances from XLS(X), бухотчётность / financial statements from PDF/images) and their recognition results. UI text, log messages and comments are in Russian.
Commands
Backend (run from api/, the working directory matters — .env, web/ static dir and pytest.ini are resolved relative to it):
python -m venv .venv && cp ../setup/pip.conf .venv/pip.conf && source .venv/bin/activate
pip install -r requirements.txt # prod deps (pinned); requirements-dev.txt holds pytest-asyncio / pytest-env
python run_server.py # uvicorn on SERVER__HOST/SERVER__PORT (default 0.0.0.0:8000)
alembic upgrade head # migrations; alembic revision --autogenerate -m "..."
pytest # all tests (asyncio_mode=auto, APP_ENV=test, ENV_FILE=.env.test)
pytest tests/unit/test_schemas_unit.py::test_name
setup/setup.sh does the venv + npm i bootstrap for both halves (uses internal pip/npm mirrors from setup/pip.conf, setup/.npmrc).
Frontend (run from web/):
npm install
npm run start # rsbuild dev server
npm run build # rsbuild build -> dist/
npx biome check src # lint/format (biome 1.9, lineWidth 140)
Known state of tests
The existing tests in api/tests/ are left over from another project: they import src.main, src.domain.schemas, src.services.membership_service, src.services.org_type_service — none of which exist here. Expect them to fail on import; they do not describe this codebase.
Configuration
api/src/core/config.py — pydantic-settings, .env file, nested delimiter __. All infra secrets live under OPENBAO (populated from OpenBao in deployment), e.g. OPENBAO__DATABASE__DATABASE_URL, OPENBAO__DATABASE__DATABASE_SCHEMA, OPENBAO__MINIO__*, OPENBAO__NATS__* (URL, NKEY seed, JWT), OPENBAO__SMB__*. Other keys: DEBUG, ROOT_PATH, SERVER__HOST, SERVER__PORT.
settingsis instantiated at import time (src/core/config.py,src/db/db.py,src/app.py), so importing almost any module requires the full env to be present.DEBUG=trueskips NATS connection/subscriptions and theraisa_fastapi_protected_apiauth middleware import.- The DB engine sets Postgres
search_pathtoDATABASE_SCHEMA; Alembic (api/alembic/env.py) takes the URL from settings (notalembic.ini), creates the schema if missing, filters autogenerate to that schema, and prompts for confirmation when-x env=prod/APP_ENV=prod.
Backend architecture (api/src)
Layered: api/endpoints/* (routers, exported via api/router.py and included in app.py) → services/* → repositories/* (subclasses of BaseRepository with model = ..., taking an AsyncSession) → models/* (SQLAlchemy 2.0 declarative, all re-exported from models/__init__.py, which Alembic imports). Wiring is manual FastAPI Depends factories in api/dependencies/services.py; FileService additionally pulls storage and nats from request.app.state. schemas/ holds Pydantic DTOs and enums (RecognitionStatus, FileStatus, DocumentType). Domain errors from common/exceptions.py are mapped to HTTP in core/exception_handlers.py (IntegrityError→409, SQLAlchemyError→500).
Document recognition pipeline (spans several files)
- Upload (
endpoints/files.py→FileService) stores the file in MinIO (core/clients.py:S3Storage) and createsStoredFile+RecognitionJob(statusQUEUED). PollWorker(core/clients.py, started in theapp.pylifespan, every ~7s) claims queued jobs withSELECT … FOR UPDATE SKIP LOCKEDand marks themPROCESSING. For image/PDF jobs it copies the file from S3 to the SMB share{SMB_PATH}/Input/and registers the job inAsyncInMemoryCache(in-process, 10-min expiry). On failure the job is re-queued. NATS publishing (publish_to_nats) is currently commented out.PDFPollWorker(core/PDFWorker/PDFTempPoller.py, every 10s) scans{SMB_PATH}/Output/<file_id>/for JSON results of the external recognizer, saves the combined JSON to S3, converts it (PDFWorker/utils.py:json_to_db_rows) intoFinancialStatementMeta/Line/Valuerows, and sets jobDONE/ fileCHECK+DocumentType.BUH_OT. Expired cache entries are marked timeout, failures marked error.core/nats_callback.py:build_file_handleris the older NATS path (subjectsfiles.tabular_parsed/files.img_parsed): loads result JSON from S3 and, for XLS(X), insertsOsvEntryrows mapped toChartOfAccountby normalized account code. Its subscriptions inapp.pyare currently commented out.
Because the job cache is in-memory, the pipeline assumes a single app instance.
App wiring quirks (app.py)
The lifespan registers the SMB session, checks the MinIO bucket (returns early — without yielding — if S3 is unreachable), connects NATS with NKEY/JWT callbacks, and starts both pollers. The SPA is served from api/web/ via SPAStaticFiles (falls back to index.html); routers must be included before the / mount.
Frontend architecture (web/src)
React 18 + TypeScript, rsbuild, Ant Design (primary UI kit; tailwind/daisyUI/raisa-ui also installed), Redux Toolkit (store/slices), react-router with routes defined in App.tsx (layouts/* are pages, widgets/* and components/* are building blocks; widgets/PDFResultViewer renders pdfjs + recognized data).
- Env vars must be prefixed
REACT_APP_(loaded inrsbuild.config.ts, exposed viaprocess.env) and read throughconf/config.ts. - Always call the backend as
`${Config.API_URL}/...`— in the built bundleAPI_URLresolves relative to the app's root path, nothttp://localhost:8000. REACT_APP_ROOT_PATHdrives rsbuildbase/assetPrefixand the routerbasename(default/aurora/apps/fastapi-dkb-armka); it must match the backendROOT_PATH.
Deployment
raisa_config.yml sets the platform resource profile (CPU/RAM). Dockerfile / docker-compose are gitignored (local only). The platform expects run_server.py to start the server on the host/port passed via env.