init: ТЗ, правила copilot, планы от моделей (DeepSeek, Claude, GPT-5.4, Gemini)

This commit is contained in:
“Naeel”
2026-05-29 19:54:41 +03:00
commit 65de34ba4a
6 changed files with 948 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
# План разработки — IP WhiteList Microservice v2
> **Автор:** GitHub Copilot (Claude Sonnet 4.6)
> **Дата:** 2026-05-29
---
## Стек
| Слой | Технология | Обоснование |
|---|---|---|
| Бэкенд | Python 3.11+ / FastAPI | Коллеги знают Python, авто-документация |
| БД | PostgreSQL | Надёжно, поддерживает аудит и сложные запросы |
| Работа с БД | psycopg2 + сырой SQL | Проще чем ORM, понятно всем, никакой магии |
| Миграции | Alembic | Только для версионирования схемы |
| Фронтенд | Jinja2 + обычные HTML-формы | Без JS-фреймворков, минимум зависимостей |
| Авторизация | Keycloak OIDC (JWT) | Заглушка только в dev через `.env` флаг |
| IP-логика | stdlib `ipaddress` + `netaddr` | Суммаризация CIDR через `netaddr` |
---
## Открытые вопросы (нужно прояснить до кодирования)
1. **Чекбокс администратора** — ТЗ: admin = `clientId == WZ01112` + «отдельный чек-бокс». Что это: отдельный claim в Keycloak-токене (`is_admin: true`)? Роль? Нужно уточнить у команды Keycloak.
2. **Создание Company в БД** — когда появляется запись: при первом входе пользователя автоматически, или администратор заводит вручную?
3. **Кто потребляет внешний endpoint** — endpoint без авторизации, доступ по IP. Список доверенных IP задаётся конфигом? Nginx ACL?
---
## Файловая структура
```
IPWhiteList/
├── app/
│ ├── main.py # FastAPI app, роутеры, startup
│ ├── config.py # Настройки из .env (DEFAULT_LIMIT, DEV_MODE, DB_DSN и др.)
│ ├── db.py # psycopg2 connection pool
│ ├── models/
│ │ └── sql.py # DDL-схема (только для документации, не ORM)
│ ├── validators.py # IPv4/CIDR: формат, маска, серые адреса, нормализация
│ ├── crud/
│ │ ├── entries.py # CRUD whitelist_entries
│ │ ├── companies.py # Компании и лимиты
│ │ └── audit.py # Запись в audit_log
│ ├── auth/
│ │ ├── oidc.py # Валидация JWT Keycloak
│ │ ├── stub.py # Dev-заглушка (только при DEV_MODE=true)
│ │ └── deps.py # FastAPI Depends: current_user
│ ├── routers/
│ │ ├── entries.py # CRUD UI-роуты + HTMX-фрагменты
│ │ ├── admin.py # Аудит, лимиты (только admin)
│ │ └── external.py # GET /api/v1/export — txt-файл
│ └── cidr_utils.py # Суммаризация через netaddr
├── templates/
│ ├── base.html
│ ├── index.html # Таблица записей + индикатор лимита
│ ├── partials/
│ │ ├── table.html # HTMX-фрагмент таблицы
│ │ └── form.html # Форма создания/редактирования
│ └── admin/
│ ├── audit.html
│ └── limits.html
├── static/
│ └── style.css
├── migrations/
│ ├── env.py
│ └── versions/
├── tests/
│ ├── test_validators.py # Юниты для IPv4-валидации (критично!)
│ └── test_crud.py
├── docs/
│ ├── plan.md # LEGACY
│ ├── plan-v2.md # Этот файл
│ └── WhiteIPlist.docx # Исходное ТЗ
├── .env.example
├── alembic.ini
├── docker-compose.yml # PostgreSQL для dev
├── requirements.txt
└── README.md
```
---
## Схема БД
```sql
-- Компании (создаются автоматически при первом входе или вручную админом — уточнить)
CREATE TABLE companies (
id SERIAL PRIMARY KEY,
client_id VARCHAR(64) UNIQUE NOT NULL, -- из Keycloak claim
name VARCHAR(255),
custom_limit INTEGER DEFAULT NULL -- NULL = использовать глобальный DEFAULT_LIMIT
);
-- Whitelist-записи
CREATE TABLE whitelist_entries (
id SERIAL PRIMARY KEY,
company_id INTEGER NOT NULL REFERENCES companies(id),
value CIDR NOT NULL, -- нормализованный CIDR
comment VARCHAR(255),
created_by VARCHAR(255) NOT NULL, -- email из токена
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_by VARCHAR(255),
updated_at TIMESTAMPTZ,
deleted_by VARCHAR(255),
deleted_at TIMESTAMPTZ -- NULL = активная запись
);
-- Аудит (только append, без UPDATE/DELETE)
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
user_email VARCHAR(255) NOT NULL,
company_id INTEGER NOT NULL,
action VARCHAR(32) NOT NULL, -- CREATE | UPDATE | DELETE
old_value TEXT,
new_value TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Индексы
CREATE INDEX ON whitelist_entries(company_id) WHERE deleted_at IS NULL;
CREATE INDEX ON audit_log(company_id);
```
---
## Этапы
### Этап 1 — Каркас + конфиг
- [ ] Структура папок
- [ ] `requirements.txt`: fastapi, uvicorn, psycopg2-binary, alembic, jinja2, python-jose, netaddr
- [ ] `.env.example` со всеми переменными: `DB_DSN`, `DEFAULT_LIMIT=15`, `DEV_MODE=false`, `KEYCLOAK_URL`, `KEYCLOAK_REALM`, `KEYCLOAK_CLIENT_ID`, `ALLOWED_EXPORT_IPS`
- [ ] `config.py` — читает `.env`, все параметры типизированы
- [ ] `db.py` — psycopg2 connection pool (SimpleConnectionPool)
- [ ] `docker-compose.yml` с PostgreSQL
### Этап 2 — Миграции (схема БД)
- [ ] Alembic init
- [ ] Initial migration: `companies`, `whitelist_entries`, `audit_log` + индексы
- [ ] Проверка `alembic upgrade head`
### Этап 3 — Валидатор IPv4 (с тестами)
- [ ] `validators.py`: принимает строку → возвращает нормализованный CIDR или ошибку
- [ ] Проверка формата: одиночный IP или CIDR
- [ ] Проверка маски: /22 – /32 (шире /21 — `ValidationError`)
- [ ] Нормализация host-битов: `192.168.1.5/24``192.168.1.0/24` + флаг `was_normalized=True`
- [ ] Запрет серых диапазонов (все из Приложения А ТЗ)
- [ ] `tests/test_validators.py` — покрыть все граничные случаи
### Этап 4 — CRUD-логика
- [ ] `crud/companies.py`: get_or_create по client_id, get_limit (custom_limit ?? DEFAULT_LIMIT)
- [ ] `crud/entries.py`: список активных, создание (лимит + дубликаты + пересечения), редактирование, soft-delete
- [ ] `crud/audit.py`: append-only запись
### Этап 5 — Авторизация
- [ ] `auth/oidc.py` — валидация JWT через JWKS Keycloak, извлечение `clientID`, `email`, определение роли
- [ ] Логика роли admin: `clientID == WZ01112` + (claim `is_admin == true`**уточнить**)
- [ ] `auth/stub.py` — только при `DEV_MODE=true`: читает `X-Dev-User` из заголовка
- [ ] `auth/deps.py``Depends(current_user)` для роутеров
### Этап 6 — Роутеры + UI
- [ ] `routers/entries.py`: список, форма создания, форма редактирования, удаление, переключатель компании
- [ ] `routers/admin.py`: журнал аудита, управление лимитами
- [ ] Шаблоны Jinja2: base.html, index.html, form.html, admin/audit.html, admin/limits.html
- [ ] Индикатор лимита «X из N» на странице
- [ ] Уведомление о нормализации адреса пользователю
### Этап 7 — Внешний endpoint
- [ ] `GET /api/v1/export` — только активные записи всех компаний
- [ ] Суммаризация через `netaddr.cidr_merge()`
- [ ] Ответ: `text/plain`, одна строка — один CIDR
- [ ] IP-фильтр из `ALLOWED_EXPORT_IPS` (middleware или Depends)
### Этап 8 — Деплой
- [ ] `Dockerfile` (python:3.11-slim, uvicorn)
- [ ] Systemd unit как альтернатива
- [ ] Nginx конфиг: reverse proxy + location для static
- [ ] README: как поднять с нуля
---
## Ключевые принципы
- **Синхронный код везде** — никакого async/await. FastAPI поддерживает синхронные роутеры.
- **Серверная валидация — авторитетная**. Клиентская — только UX.
- **`DEV_MODE=true`** — единственный способ обойти Keycloak. В prod недоступен.
- **Audit log — append only**. Никаких UPDATE/DELETE в `audit_log`.
- **Лимит `DEFAULT_LIMIT`** — всегда из `config.py`, который читает `.env`. Без пересборки.