From d03da40eeeab207d3800cbfb1b3719b432ef9b3e Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sun, 11 Oct 2026 18:26:48 +0300 Subject: [PATCH 01/17] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B2=D0=BE=D1=81=D0=BF=D1=80=D0=BE=D0=B8=D0=B7?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4=D0=B8=D0=BC=D1=83=D1=8E=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=83=20=D0=B8=D0=BD=D1=84=D1=80=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yaml | 20 ++++ AGENTS.md | 23 +++++ CHANGELOG.md | 1 + README.md | 6 +- backend/README.md | 4 +- backend/search/api.py | 10 +- backend/search/minjust_opensearch.py | 2 +- backend/search/review.html | 10 +- backend/test_search_api.py | 23 +++++ deploy/production/.env.example | 5 +- deploy/production/README.md | 11 ++- deploy/production/compose.yaml | 23 ++++- docs/README.md | 6 +- docs/operations/ACCESS.md | 58 +++++++++++ docs/operations/BACKUP.md | 28 ++++++ docs/operations/CURRENT_STATE.md | 61 ++++++++++++ docs/operations/DECISIONS.md | 9 ++ docs/operations/DEPLOYMENT.md | 37 +++++++ docs/operations/INFRASTRUCTURE.md | 56 +++++++++++ docs/operations/RECOVERY.md | 33 +++++++ docs/operations/ROLLBACK.md | 17 ++++ docs/operations/project-status.md | 4 +- docs/product/frontend-search-sps-plan.md | 2 +- docs/product/project-overview.md | 2 +- infra/bootstrap/bootstrap-ubuntu-24.04.sh | 52 ++++++++++ infra/bootstrap/verify.sh | 114 ++++++++++++++++++++++ infra/inventory/production.yml | 72 ++++++++++++++ 27 files changed, 664 insertions(+), 25 deletions(-) create mode 100644 .gitea/workflows/ci.yaml create mode 100644 AGENTS.md create mode 100644 docs/operations/ACCESS.md create mode 100644 docs/operations/BACKUP.md create mode 100644 docs/operations/CURRENT_STATE.md create mode 100644 docs/operations/DECISIONS.md create mode 100644 docs/operations/DEPLOYMENT.md create mode 100644 docs/operations/INFRASTRUCTURE.md create mode 100644 docs/operations/RECOVERY.md create mode 100644 docs/operations/ROLLBACK.md create mode 100755 infra/bootstrap/bootstrap-ubuntu-24.04.sh create mode 100755 infra/bootstrap/verify.sh create mode 100644 infra/inventory/production.yml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..ead6a6e --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,20 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +jobs: + tests-and-images: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run backend tests + run: PYTHONPATH=backend python3 -m unittest discover -s backend -p 'test_*.py' -v + - name: Run Telegram bot tests + run: python3 -m unittest discover -s tools/telegram-bot -p 'test_*.py' -v + - name: Build production images + run: | + docker build -f deploy/production/Dockerfile.backend -t akyldash-backend:ci . + docker build -f deploy/production/Dockerfile.opensearch -t akyldash-opensearch:ci . diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..4049606 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,23 @@ +# Акылдаш — инструкции для агента + +Проект: юридическая информационно-аналитическая платформа; Search API на +Python, SQLite и OpenSearch. Production пока не развернут. + +- Начинай с [инфраструктурного состояния](docs/operations/CURRENT_STATE.md) и + [правил доступа](docs/operations/ACCESS.md). Не запрашивай доступы повторно. +- Архитектура и процедуры: [инфраструктура](docs/operations/INFRASTRUCTURE.md), + [деплой](docs/operations/DEPLOYMENT.md), [backup](docs/operations/BACKUP.md), + [rollback](docs/operations/ROLLBACK.md), [восстановление](docs/operations/RECOVERY.md). +- Работай в feature/fix/refactor-ветке. `main` защищена; изменения вноси через + PR, не сливай его автоматически. Сохраняй незакоммиченные изменения. +- SSH alias — `akyldash_vps`; используй только read-only диагностику. Не давай + `codex` общий sudo или доступ к Docker socket. Root-действия требует владелец. + Для Gitea используй `gitea-api`, не читай файлы с учётными данными. +- Локальные проверки: `PYTHONPATH=backend python3 -m unittest discover -s backend -p 'test_*.py' -v` и + `python3 -m unittest discover -s tools/telegram-bot -p 'test_*.py' -v`. + Production images собираются командами из `.gitea/workflows/ci.yaml`. +- Перед изменениями production необходим проверенный backup. Формального + механизма миграций нет; проверяй совместимость SQLite/OpenSearch. Не меняй + SSH, firewall или production вручную без владельца и последующего PR. +- После каждого изменения инфраструктуры обновляй + `docs/operations/CURRENT_STATE.md` по фактическим проверкам. diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ba8ac..ee7ec4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Не выпущено +- Backend `0.11.3`: ключ подписи поисковых снимков читается из Docker secret-файла, а не из аргументов процесса. Добавлена базовая инфраструктура VPS, drift-проверка и CI для тестов и сборки образов; production-деплой остаётся выключен до проверки runner, backup/restore и ограниченного deploy-helper. - Frontend `0.1.2`: на планшетной ширине экран поиска переходит в одну колонку, чтобы фильтры, результаты и документ помещались в окно. - Backend `0.11.2`: смена пароля требует текущий пароль и запрещает оставить прежний, включая временный пароль при первом входе. - Backend `0.11.1` и frontend `0.1.1`: в управлении пользователями показываются даты первого и последнего входа, а также последнего сброса пароля. diff --git a/README.md b/README.md index 89fe752..58ab1d4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Telegram-бот — только часть рабочего окружения ## Текущее состояние Сейчас реализованы Telegram-бот-секретарь версии `0.2.2`, backend версии -`0.11.2` и внутренний frontend лаборатории `0.1.2`. Авторизация хранится в +`0.11.3` и внутренний frontend лаборатории `0.1.2`. Авторизация хранится в SQLite; первый пользователь `admin` создаётся интерактивной CLI-командой. Поиск расширяет аббревиатуры организационно-правовых форм и поднимает действующий Налоговый кодекс для запросов о налоге на прибыль. @@ -18,7 +18,7 @@ SQLite; первый пользователь `admin` создаётся инт | Компонент | Версия | Состояние | |---|---:|---| | Telegram-бот | `0.2.2` | на Synology работает `0.2.1`; обновление после слияния | -| Backend | `0.11.2` | авторизация и поиск с фильтрами; тематический поиск ОсОО/ЖЧК | +| Backend | `0.11.3` | авторизация и поиск с фильтрами; тематический поиск ОсОО/ЖЧК | | Frontend | `0.1.2` | лаборатория поиска, личный кабинет и управление учетными записями | | Сбор и обработка правовых данных | `0.8.0` | добавлены relevance set и оценка выдачи | | RAG и база знаний | — | ещё не созданы | @@ -62,4 +62,4 @@ python3 -m unittest discover -s tools/telegram-bot -v --- -Акылдаш · Telegram-бот v0.2.2 · Backend v0.11.2 · Frontend v0.1.2 +Акылдаш · Telegram-бот v0.2.2 · Backend v0.11.3 · Frontend v0.1.2 diff --git a/backend/README.md b/backend/README.md index fb4bb2d..7d03fb0 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,6 +1,6 @@ # Backend Акылдаш -Версия: `0.11.2` +Версия: `0.11.3` Первая backend-область проекта — загрузка правовых документов из официального Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в @@ -253,4 +253,4 @@ PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --la --- -Акылдаш · Backend v0.11.2 · Frontend v0.1.2 +Акылдаш · Backend v0.11.3 · Frontend v0.1.2 diff --git a/backend/search/api.py b/backend/search/api.py index df96ae6..37337ef 100644 --- a/backend/search/api.py +++ b/backend/search/api.py @@ -451,6 +451,7 @@ def main() -> int: parser.add_argument("--reviews-db", type=Path, default=Path("data/search-reviews.sqlite3")) parser.add_argument("--users-db", type=Path, default=Path("data/search-users.sqlite3")) parser.add_argument("--review-secret", default=None) + parser.add_argument("--review-secret-file", type=Path) parser.add_argument("--cookie-secure", action=argparse.BooleanOptionalAction, default=os.environ.get("COOKIE_SECURE", "false").lower() == "true") parser.add_argument("--create-admin", action="store_true", help="create the initial admin account and exit") parser.add_argument("--reset-admin-password", action="store_true", help="set a temporary password for admin and exit") @@ -458,7 +459,14 @@ def main() -> int: parser.add_argument("--port", type=int, default=8080) parser.add_argument("--version", action="version", version=APP_VERSION) arguments = parser.parse_args() - secret = arguments.review_secret.encode() if arguments.review_secret else None + if arguments.review_secret and arguments.review_secret_file: + parser.error("choose either --review-secret or --review-secret-file") + try: + secret = arguments.review_secret.encode() if arguments.review_secret else arguments.review_secret_file.read_bytes().strip() if arguments.review_secret_file else None + except OSError as error: + parser.error(f"cannot read review secret file: {error}") + if arguments.review_secret_file and not secret: + parser.error("review secret file is empty") api = Api(arguments.url, arguments.index, arguments.data, arguments.reviews_db, secret, arguments.users_db, arguments.cookie_secure) if arguments.create_admin or arguments.reset_admin_password: users = api.auth_store.list_users() diff --git a/backend/search/minjust_opensearch.py b/backend/search/minjust_opensearch.py index 8af8236..f62a681 100644 --- a/backend/search/minjust_opensearch.py +++ b/backend/search/minjust_opensearch.py @@ -17,7 +17,7 @@ from typing import Iterator from search.catalog import authority_codes, legal_force_code, source_code -APP_VERSION = "0.11.2" +APP_VERSION = "0.11.3" LANGUAGES = {"ru", "ky"} DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json") diff --git a/backend/search/review.html b/backend/search/review.html index 97bd25c..5817921 100644 --- a/backend/search/review.html +++ b/backend/search/review.html @@ -5,7 +5,7 @@ Оценка поисковой выдачи · Акылдаш