Reviewed-on: #5
This commit is contained in:
commit
29512f98f5
72
CLAUDE.md
Normal file
72
CLAUDE.md
Normal file
@ -0,0 +1,72 @@
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
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/`):
|
||||
|
||||
```bash
|
||||
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`.
|
||||
|
||||
- `settings` is 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=true` skips NATS connection/subscriptions and the `raisa_fastapi_protected_api` auth middleware import.
|
||||
- The DB engine sets Postgres `search_path` to `DATABASE_SCHEMA`; Alembic (`api/alembic/env.py`) takes the URL from settings (not `alembic.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)
|
||||
|
||||
1. Upload (`endpoints/files.py` → `FileService`) stores the file in MinIO (`core/clients.py:S3Storage`) and creates `StoredFile` + `RecognitionJob` (status `QUEUED`).
|
||||
2. `PollWorker` (`core/clients.py`, started in the `app.py` lifespan, every ~7s) claims queued jobs with `SELECT … FOR UPDATE SKIP LOCKED` and marks them `PROCESSING`. For image/PDF jobs it copies the file from S3 to the SMB share `{SMB_PATH}/Input/` and registers the job in `AsyncInMemoryCache` (in-process, 10-min expiry). On failure the job is re-queued. NATS publishing (`publish_to_nats`) is currently commented out.
|
||||
3. `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`) into `FinancialStatementMeta/Line/Value` rows, and sets job `DONE` / file `CHECK` + `DocumentType.BUH_OT`. Expired cache entries are marked timeout, failures marked error.
|
||||
4. `core/nats_callback.py:build_file_handler` is the older NATS path (subjects `files.tabular_parsed` / `files.img_parsed`): loads result JSON from S3 and, for XLS(X), inserts `OsvEntry` rows mapped to `ChartOfAccount` by normalized account code. Its subscriptions in `app.py` are 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 in `rsbuild.config.ts`, exposed via `process.env`) and read through `conf/config.ts`.
|
||||
- Always call the backend as `` `${Config.API_URL}/...` `` — in the built bundle `API_URL` resolves relative to the app's root path, not `http://localhost:8000`.
|
||||
- `REACT_APP_ROOT_PATH` drives rsbuild `base`/`assetPrefix` and the router `basename` (default `/aurora/apps/fastapi-dkb-armka`); it must match the backend `ROOT_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.
|
||||
Loading…
x
Reference in New Issue
Block a user