commit a306e27a18bc2db05f705d907e81a111adadba0f Author: Витковских Евгений Date: Thu Jul 30 18:45:43 2026 +0500 first commit diff --git a/Kit.Db.slnx b/Kit.Db.slnx new file mode 100644 index 0000000..f3b27c1 --- /dev/null +++ b/Kit.Db.slnx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..d5d2a49 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,26 @@ +# Kit.Db - Structure + +Файловая струтура + +- rules/ + - common/ + - pg/ + - sqlite/ +- skills/ +- projects/ + - pg/ + - kit_auth_pg/ + - README.md + - settings/ + - connection.json + - auth/ + - tables/ + - functions/ + - scripts/ + - init/ + - deploy/ + - auth.psql + - _docs/ + - diagrams/ + - openapi.yaml + - architecture.md \ No newline at end of file diff --git a/docs/projects/kit-auth-token-db.md b/docs/projects/kit-auth-token-db.md new file mode 100644 index 0000000..995f4d0 --- /dev/null +++ b/docs/projects/kit-auth-token-db.md @@ -0,0 +1,100 @@ +# Kit.Auth.Token.Db — Описание БД + +## Общее описание + +База данных для управления токенами авторизации. Хранит владельцев токенов, типы токенов и сами токены с возможностью валидации, отзыва и удаления. + +- **Движок:** PostgreSQL +- **Схема:** `token` + +--- + +## Структура проекта (по шаблону) + +``` +projects/pg/kit_auth_token_pg/ + readme.md + settings/ + connection.json + token/ + tables/ + owner.psql + token_type.psql + token.psql + functions/ + owner/ + owner_insert.psql + owner_select.psql + token/ + token_consume.psql + token_delete.psql + token_insert.psql + token_revoke_by_owner.psql + token_revoke.psql + token_select.psql + token_update.psql + token_validate.psql + token_type/ + token_type_select.psql + scripts/ + init/ + deploy/ + token.psql # Точка входа модуля + token.post-deploy.psql # Post-deploy скрипты + _docs/ + diagrams/ + openapi.yaml + architecture.md +``` + +--- + +## Таблицы + +### owner +Владельцы токенов (пользователи/системы). + +### token_type +Справочник типов токенов (access, refresh и т.д.). + +### token +Токены авторизации с привязкой к владельцу и типу. + +--- + +## Функции + +### owner/ +| Функция | Назначение | +|---------|-----------| +| `owner_insert` | Создание владельца | +| owner_select` | Получение владельца | + +### token/ +| Функция | Назначение | +|---------|-----------| +| `token_insert` | Создание токена | +| `token_select` | Получение токена | +| `token_update` | Обновление токена | +| `token_delete` | Удаление токена | +| `token_consume` | Использование токена | +| `token_validate` | Валидация токена | +| `token_revoke` | Отзыв токена | +| `token_revoke_by_owner` | Отзыв всех токенов владельца | + +### token_type/ +| Функция | Назначение | +|---------|-----------| +| `token_type_select` | Получение типа токена | + +--- + +## Миграции + +- `2026-07-23` — Полная схема (create_full) + +--- + +## Связи с другими БД + +- Связана с `Kit.Auth.Db` (модуль `auth`) — владельцы токенов могут быть пользователями из auth.users \ No newline at end of file diff --git a/docs/structure-diagram.md b/docs/structure-diagram.md new file mode 100644 index 0000000..9df6580 --- /dev/null +++ b/docs/structure-diagram.md @@ -0,0 +1,103 @@ +# Kit.Db — Структура проекта (ASCII) + +``` +Kit.Db/ +│ +├── rules/ # Правила для LLM по движкам +│ ├── common/ # Общие правила для всех СУБД +│ │ └── ... +│ ├── pg/ # Правила PostgreSQL +│ │ └── db-project-rule.md +│ └── sqlite/ # Правила SQLite +│ └── db-project-rule.md +│ +├── skills/ # LLM промпты (описания таблиц, функций) +│ └── ... +│ +└── projects/ # Проекты БД + │ + ├── pg/ # ── PostgreSQL проекты ── + │ │ + │ └── kit_auth_pg/ # Проект: БД авторизации + │ │ + │ ├── readme.md # Описание БД + │ │ + │ ├── settings/ # Настройки подключения + │ │ └── connection.json + │ │ + │ ├── auth/ # Модуль: авторизация + │ │ ├── tables/ # DDL таблиц + │ │ │ ├── user.psql + │ │ │ ├── cabinet.psql + │ │ │ └── user_cabinet.psql + │ │ │ + │ │ ├── functions/ # SQL функции + │ │ │ ├── user_select_by_ids/ + │ │ │ │ └── user_select_by_ids.psql + │ │ │ └── user_create/ + │ │ │ └── user_create.psql + │ │ │ + │ │ ├── scripts/ # Произвольные скрипты + │ │ │ └── seed_roles.sql + │ │ │ + │ │ ├── init/ # Инициализация + │ │ │ └── create_schema.psql + │ │ │ + │ │ ├── deploy/ # Миграции + │ │ │ ├── 2026-07-08_user-select-by-ids.sql + │ │ │ └── 2026-07-09-user-seed-merge.sql + │ │ │ + │ │ ├── auth.psql # Точка входа модуля + │ │ └── auth.post-deploy.psql + │ │ + │ └── _docs/ # Документация проекта + │ ├── diagrams/ + │ ├── openapi.yaml + │ └── architecture.md + │ + └── sl3/ # ── SQLite проекты ── + │ + └── kit_example_sl3/ # Проект: пример + │ + ├── readme.md + ├── settings/ + │ └── connection.json + │ + ├── main/ # Модуль: основной + │ ├── tables/ + │ ├── scripts/ + │ ├── init/ + │ ├── deploy/ + │ └── main.sql + │ + └── _docs/ + ├── diagrams/ + ├── openapi.yaml + └── architecture.md +``` + +--- + +## Связи между уровнями + +``` +rules/ ──────────► LLM использует при формировании проектов + common/ │ + pg/ │ + sqlite/ ▼ + projects/ + pg/ ─── PostgreSQL проекты + sl3/ ── SQLite проекты + +skills/ ─────────► LLM использует при генерации описаний + +projects/{engine}/{project}/ + readme.md ──────────► Описание БД + settings/ ──────────► Подключение + {module}/ ──────────► SQL исходники + tables/ ──────────► DDL таблиц + functions/ ───────► SQL функции (только PG) + scripts/ ─────────► Произвольные SQL + init/ ────────────► Инициализация + deploy/ ──────────► Миграции + _docs/ ─────────────► Документация \ No newline at end of file diff --git a/docs/tz.md b/docs/tz.md new file mode 100644 index 0000000..0965fba --- /dev/null +++ b/docs/tz.md @@ -0,0 +1,198 @@ +# ТЗ — Формирование проекта для БД + +## Общее описание + +На основании файловой структуры (см. architecture.md) формируется проект для конкретной базы данных. Каждый проект — автономная единица с собственными настройками, исходниками и документацией. + +--- + +## Шаблон структуры + +``` +Kit.Db/ +│ +├── rules/ # Правила для LLM по движкам +│ ├── common/ # Общие правила для всех СУБД +│ │ └── ... +│ ├── pg/ # Правила PostgreSQL +│ │ └── db-project-rule.md +│ └── sqlite/ # Правила SQLite +│ └── db-project-rule.md +│ +├── skills/ # LLM промпты (описания таблиц, функций) +│ └── ... +│ +└── projects/ # Проекты БД + │ + ├── pg/ # ── PostgreSQL проекты ── + │ │ + │ └── kit_auth_pg/ # Проект: БД авторизации + │ │ + │ ├── readme.md # Описание БД + │ │ + │ ├── settings/ # Настройки подключения + │ │ └── connection.json + │ │ + │ ├── auth/ # Модуль: авторизация + │ │ ├── tables/ # DDL таблиц + │ │ │ ├── user.psql + │ │ │ ├── cabinet.psql + │ │ │ └── user_cabinet.psql + │ │ │ + │ │ ├── functions/ # SQL функции + │ │ │ ├── user_select_by_ids/ + │ │ │ │ └── user_select_by_ids.psql + │ │ │ └── user_create/ + │ │ │ └── user_create.psql + │ │ │ + │ │ ├── scripts/ # Произвольные скрипты + │ │ │ └── seed_roles.sql + │ │ │ + │ │ ├── init/ # Инициализация + │ │ │ └── create_schema.psql + │ │ │ + │ │ ├── deploy/ # Миграции + │ │ │ ├── 2026-07-08_user-select-by-ids.sql + │ │ │ └── 2026-07-09-user-seed-merge.sql + │ │ │ + │ │ ├── auth.psql # Точка входа модуля + │ │ └── auth.post-deploy.psql + │ │ + │ └── _docs/ # Документация проекта + │ ├── diagrams/ + │ ├── openapi.yaml + │ └── architecture.md + │ + └── sl3/ # ── SQLite проекты ── + │ + └── kit_example_sl3/ # Проект: пример + │ + ├── readme.md + ├── settings/ + │ └── connection.json + │ + ├── main/ # Модуль: основной + │ ├── tables/ + │ ├── scripts/ + │ ├── init/ + │ ├── deploy/ + │ └── main.sql + │ + └── _docs/ + ├── diagrams/ + ├── openapi.yaml + └── architecture.md +``` + +--- + +## Структура проекта + +Каждый проект БД создаётся по пути: + +``` +projects/{engine}/{project_name}/ +``` + +Где: + +- `{engine}` — тип СУБД (pg, sqlite, mysql, mssql и т.д.) +- `{project_name}` — имя проекта в формате `имя_БД_движок` (например `kit_auth_pg`) + +--- + +## Обязательные компоненты проекта + +### 1. readme.md + +Описание проекта: + +- Назначение БД +- Движок и версия +- Краткое описание модулей +- Связи с другими БД (если есть) + +### 2. settings/ + +Папка с настройками подключения: + +- `connection.json` — параметры подключения к БД (хост, порт, БД, пользователь) + +Пример `connection.json`: + +```json +{ + "host": "localhost", + "port": 5432, + "database": "kit_auth", + "username": "postgres", + "password": "" +} +``` + +### 3. Модули (auth/, payments/, и т.д.) + +Каждый модуль содержит: + +- `tables/` — DDL-скрипты таблиц +- `functions/` — SQL-функции и хранимые процедуры +- `scripts/` — SQL-скрипты (миграции, seed-данные, утилиты) +- `init/` — скрипты инициализации (создание схемы, начальные данные) +- `deploy/` — инфраструктура развёртывания (docker-compose, bat, sh) +- `{module}.psql` — главный файл модуля (точка входа) + +### 4. _docs/ + +Документация проекта: + +- `diagrams/` — диаграммы (ER, зависимости, потоки) +- `openapi.yaml` — OpenAPI спецификация (если БД предоставляет API) +- `architecture.md` — описание архитектуры конкретной БД + +--- + +## Правила именования + +- Папки — в нижнем регистре, через подчёркивание: `kit_auth_pg` +- SQL-файлы — в нижнем регистре: `create_users_table.sql` +- Функции — по шаблону: `{module}_{action}.sql` (например `auth_get_user_by_id.sql`) +- Таблицы — по шаблону: `{module}_{entity}.sql` (например `auth_users.sql`) + +--- + +## Правила (rules/) + +При формировании проекта LLM использует правила из: + +- `rules/common/` — общие правила для всех СУБД +- `rules/{engine}/` — правила для конкретного движка + +Правила определяют: + +- Стиль написания SQL +- Шаблоны описания таблиц и функций +- Формат комментариев +- Стандарты именования + +--- + +## Skills + +При формировании проекта LLM использует промпты из `skills/` для: + +- Генерации описаний таблиц +- Генерации описаний функций +- Формирования документации +- Создания диаграмм + +--- + +## Порядок создания проекта + +1. Создать папку проекта `projects/{engine}/{project_name}/` +2. Создать `readme.md` с описанием +3. Создать `settings/connection.json` +4. Для каждого модуля БД создать папку модуля с подпапками (tables, functions, scripts, init, deploy) +5. Создать `_docs/` с диаграммами и спецификацией +6. Заполнить SQL-файлы на основании требований +7. Проверить структуру на соответствие шаблону diff --git a/projects/pg/kit_auth_token_pg/deploy/docker-compose.yml b/projects/pg/kit_auth_token_pg/deploy/docker-compose.yml new file mode 100644 index 0000000..edcb0d4 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/deploy/docker-compose.yml @@ -0,0 +1,25 @@ +name: kit-auth-token-db +networks: + default: + external: true + name: sdi_server +services: + token-db: + image: postgres + restart: always + ports: + - 6749:5432 + container_name: kit-auth-token-db + environment: + POSTGRES_DB: kit-auth-token + POSTGRES_USER: user_auth_token + POSTGRES_PASSWORD: 9b1d4c7a-2e63-4f08-a5d1-7c0e3b9f2a64 + PGDATA: "/data" + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + volumes: + - ./pg_data:/var/lib/postgresql/data + - ./init:/docker-entrypoint-initdb.d diff --git a/projects/pg/kit_auth_token_pg/deploy/down.bat b/projects/pg/kit_auth_token_pg/deploy/down.bat new file mode 100644 index 0000000..84e575c --- /dev/null +++ b/projects/pg/kit_auth_token_pg/deploy/down.bat @@ -0,0 +1,36 @@ +@echo off +setlocal + +set reset_data=0 + +if "%~1"=="" ( + goto end +) + +:checkArguments +if "%~1"=="" ( + goto end +) +if "%~1"=="--reset-data" ( + set reset_data=1 +) +if "%~1"=="-r" ( + set reset_data=1 +) +shift +goto checkArguments + +:end + +@echo on +docker compose down + +@echo off +if "%reset_data%"=="1" ( + rmdir /s /q .\pg_data + echo. + echo volume removed + echo. +) + +endlocal diff --git a/projects/pg/kit_auth_token_pg/deploy/init.sh b/projects/pg/kit_auth_token_pg/deploy/init.sh new file mode 100644 index 0000000..5fe0e08 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/deploy/init.sh @@ -0,0 +1,42 @@ +file_publish="/docker-entrypoint-initdb.d/scripts/publish.psql" +files=( +########################################################################## +# schemas +"/docker-entrypoint-initdb.d/schemas/token/token.psql" + +########################################################################## +# tables +# token_type и owner — раньше token (на них ссылаются type_id / owner_id) +"/docker-entrypoint-initdb.d/schemas/token/tables/token_type.psql" +"/docker-entrypoint-initdb.d/schemas/token/tables/owner.psql" +"/docker-entrypoint-initdb.d/schemas/token/tables/token.psql" + +########################################################################## +# functions +# token_type +"/docker-entrypoint-initdb.d/schemas/token/functions/token_type/token_type_select.psql" +# owner +"/docker-entrypoint-initdb.d/schemas/token/functions/owner/owner_select.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/owner/owner_insert.psql" +# token +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_select.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_insert.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_update.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_consume.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_validate.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_revoke.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_revoke_by_owner.psql" +"/docker-entrypoint-initdb.d/schemas/token/functions/token/token_delete.psql" + +########################################################################## +# post-deploy (seed справочников) +"/docker-entrypoint-initdb.d/schemas/token/token.post-deploy.psql" + +) + +> ${file_publish} +for item in "${files[@]}"; do + cat "${item}" >> "${file_publish}" && printf "\n\n\n" >> "${file_publish}" +done +# run publish.psql +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f "${file_publish}" diff --git a/projects/pg/kit_auth_token_pg/deploy/restart.bat b/projects/pg/kit_auth_token_pg/deploy/restart.bat new file mode 100644 index 0000000..87fa9e9 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/deploy/restart.bat @@ -0,0 +1,7 @@ +@echo off +setlocal + +CALL "down.bat" %* +CALL "up.bat" + +endlocal diff --git a/projects/pg/kit_auth_token_pg/deploy/up.bat b/projects/pg/kit_auth_token_pg/deploy/up.bat new file mode 100644 index 0000000..e3fec99 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/deploy/up.bat @@ -0,0 +1,7 @@ +@echo off +setlocal + +docker network create -d bridge sdi_server +docker compose up -d + +endlocal diff --git a/projects/pg/kit_auth_token_pg/functions/owner/owner_insert.psql b/projects/pg/kit_auth_token_pg/functions/owner/owner_insert.psql new file mode 100644 index 0000000..b3fea8c --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/owner/owner_insert.psql @@ -0,0 +1,22 @@ +-- Явная регистрация владельца по естественному ключу key (НЕ авто-создание при выпуске). +-- Идемпотентна: существует — возвращаем его id (title не перетираем); нет — создаём. +-- ON CONFLICT DO UPDATE (no-op по key) нужен, чтобы RETURNING вернул id и при конфликте; +-- это же делает функцию устойчивой к гонке двух параллельных выпусков (правило 06). +CREATE OR REPLACE FUNCTION token.owner_insert( + _key varchar, + _title varchar, + _date_created timestamptz +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _id int; +BEGIN + INSERT INTO token.owner(key, title, date_created) + VALUES (_key, _title, _date_created) + ON CONFLICT (key) DO UPDATE SET key = EXCLUDED.key + RETURNING id INTO _id; + RETURN _id; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/owner/owner_select.psql b/projects/pg/kit_auth_token_pg/functions/owner/owner_select.psql new file mode 100644 index 0000000..c4c2992 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/owner/owner_select.psql @@ -0,0 +1,23 @@ +-- Выборка владельцев: фильтры по id/key/title, без пейджинга (правило 03). +-- key сравнивается на равенство (естественный ключ), title — текстовый ILIKE. +CREATE OR REPLACE FUNCTION token.owner_select( + _id int DEFAULT NULL, + _key varchar DEFAULT NULL, + _title varchar DEFAULT NULL +) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT id, key, title, date_created + FROM token.owner + WHERE (_id IS NULL OR id = _id) + AND (_key IS NULL OR key = _key) + AND (_title IS NULL OR title ILIKE '%' || _title || '%') + ORDER BY id; + RETURN NEXT _data; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_consume.psql b/projects/pg/kit_auth_token_pg/functions/token/token_consume.psql new file mode 100644 index 0000000..0b8372b --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_consume.psql @@ -0,0 +1,37 @@ +-- Атомарное потребление токена за одно обращение (правило 06: логика записи — в БД). +-- В одной транзакции: проверка валидности, инкремент read_used, фиксация date_last_read, +-- продление срока при скользящей экспирации, авто-погашение при достижении лимита. +-- Возвращает курсор с одной строкой: data и итоговый признак is_valid обращения. +-- Невалиден (нет токена / неактивен / истёк / исчерпан лимит) → is_valid=false, data=NULL. +CREATE OR REPLACE FUNCTION token.token_consume(_hash varchar) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _result refcursor := 'data'; + _now timestamptz := now(); + _data text; + _valid boolean := false; +BEGIN + UPDATE token.token t + SET read_used = t.read_used + 1, + date_last_read = _now, + date_expired = CASE WHEN t.is_slide_expiration + THEN _now + make_interval(secs => t.lifetime_seconds) + ELSE t.date_expired END, + -- гасим, если этим обращением исчерпали лимит (0 = без лимита) + is_active = CASE WHEN t.read_limit > 0 AND t.read_used + 1 >= t.read_limit + THEN false ELSE t.is_active END + WHERE t.hash = _hash + AND t.is_active + AND t.date_expired > _now + AND (t.read_limit = 0 OR t.read_used < t.read_limit) + RETURNING t.data INTO _data; + + _valid := FOUND; + + OPEN _result FOR + SELECT _valid AS is_valid, _data AS data; + RETURN NEXT _result; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_delete.psql b/projects/pg/kit_auth_token_pg/functions/token/token_delete.psql new file mode 100644 index 0000000..abf023c --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_delete.psql @@ -0,0 +1,9 @@ +-- Физическое удаление токена по hash (мягкое удаление таблицей не предусмотрено). +CREATE OR REPLACE FUNCTION token.token_delete(_hash varchar) +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + DELETE FROM token.token WHERE hash = _hash; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_insert.psql b/projects/pg/kit_auth_token_pg/functions/token/token_insert.psql new file mode 100644 index 0000000..1a1ec3e --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_insert.psql @@ -0,0 +1,28 @@ +-- Вставка токена. PK — текстовый hash (не serial), возвращается он же. +-- read_used/date_last_read/date_revoked при выпуске не задаются (значения по умолчанию). +-- owner_id должен существовать заранее (резолв/создание владельца — см. owner_insert). +CREATE OR REPLACE FUNCTION token.token_insert( + _hash varchar, + _date_created timestamptz, + _date_expired timestamptz, + _data text, + _lifetime_seconds int, + _is_slide_expiration boolean, + _read_limit int, + _is_active boolean, + _type_id int, + _owner_id int +) +RETURNS varchar +LANGUAGE plpgsql +AS $$ +BEGIN + INSERT INTO token.token( + hash, date_created, date_expired, data, lifetime_seconds, + is_slide_expiration, read_limit, is_active, type_id, owner_id) + VALUES ( + _hash, _date_created, _date_expired, _data, _lifetime_seconds, + _is_slide_expiration, _read_limit, _is_active, _type_id, _owner_id); + RETURN _hash; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_revoke.psql b/projects/pg/kit_auth_token_pg/functions/token/token_revoke.psql new file mode 100644 index 0000000..1768f4a --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_revoke.psql @@ -0,0 +1,23 @@ +-- Мягкий отзыв одного токена по hash: is_active=false + фиксация момента и причины. +-- Отзываются только ещё активные токены (date_revoked/revoke_reason не перетираются +-- повторным вызовом). Возвращает число фактически отозванных (0 или 1). +CREATE OR REPLACE FUNCTION token.token_revoke( + _hash varchar, + _reason varchar +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _count int; +BEGIN + UPDATE token.token + SET is_active = false, + date_revoked = now(), + revoke_reason = _reason + WHERE hash = _hash + AND is_active; + GET DIAGNOSTICS _count = ROW_COUNT; + RETURN _count; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_revoke_by_owner.psql b/projects/pg/kit_auth_token_pg/functions/token/token_revoke_by_owner.psql new file mode 100644 index 0000000..52f17a6 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_revoke_by_owner.psql @@ -0,0 +1,23 @@ +-- Массовый отзыв всех активных токенов владельца (logout всех сессий приложения, +-- реакция на компрометацию источника). Опирается на индекс ix_token_owner. +-- Возвращает число фактически отозванных токенов. +CREATE OR REPLACE FUNCTION token.token_revoke_by_owner( + _owner_id int, + _reason varchar +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _count int; +BEGIN + UPDATE token.token + SET is_active = false, + date_revoked = now(), + revoke_reason = _reason + WHERE owner_id = _owner_id + AND is_active; + GET DIAGNOSTICS _count = ROW_COUNT; + RETURN _count; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_select.psql b/projects/pg/kit_auth_token_pg/functions/token/token_select.psql new file mode 100644 index 0000000..5831533 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_select.psql @@ -0,0 +1,28 @@ +-- Выборка токенов: фильтры по полям, без пейджинга (правило 03). +-- hash/owner_id/type_id — равенство; is_active — равенство; владельца фильтруем по id +-- (его естественный ключ резолвится через owner_select на стороне вызывающего кода). +CREATE OR REPLACE FUNCTION token.token_select( + _hash varchar DEFAULT NULL, + _type_id int DEFAULT NULL, + _owner_id int DEFAULT NULL, + _is_active boolean DEFAULT NULL +) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT hash, type_id, owner_id, date_created, date_expired, date_last_read, + data, lifetime_seconds, is_slide_expiration, read_limit, read_used, + is_active, date_revoked, revoke_reason + FROM token.token + WHERE (_hash IS NULL OR hash = _hash) + AND (_type_id IS NULL OR type_id = _type_id) + AND (_owner_id IS NULL OR owner_id = _owner_id) + AND (_is_active IS NULL OR is_active = _is_active) + ORDER BY date_created DESC; + RETURN NEXT _data; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_update.psql b/projects/pg/kit_auth_token_pg/functions/token/token_update.psql new file mode 100644 index 0000000..7e1179a --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_update.psql @@ -0,0 +1,31 @@ +-- Обновление токена по hash. date_created неизменяем и не обновляется. +-- read_used/date_last_read/date_revoked обновляются специализированными операциями +-- (потребление токена / отзыв), не общим update. +CREATE OR REPLACE FUNCTION token.token_update( + _hash varchar, + _date_expired timestamptz, + _data text, + _lifetime_seconds int, + _is_slide_expiration boolean, + _read_limit int, + _is_active boolean, + _type_id int, + _owner_id int +) +RETURNS varchar +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE token.token + SET date_expired = _date_expired, + data = _data, + lifetime_seconds = _lifetime_seconds, + is_slide_expiration = _is_slide_expiration, + read_limit = _read_limit, + is_active = _is_active, + type_id = _type_id, + owner_id = _owner_id + WHERE hash = _hash; + RETURN _hash; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token/token_validate.psql b/projects/pg/kit_auth_token_pg/functions/token/token_validate.psql new file mode 100644 index 0000000..5f47868 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token/token_validate.psql @@ -0,0 +1,28 @@ +-- Проверка токена БЕЗ расхода обращения (peek): read_used/date_last_read не меняются. +-- Критерий валидности тот же, что в token_consume: активен, не истёк, лимит не исчерпан. +-- Используется там, где проверка не должна «съедать» обращение (middleware, предпроверки). +-- Возвращает курсор с одной строкой: is_valid и data (data=NULL, если невалиден). +CREATE OR REPLACE FUNCTION token.token_validate(_hash varchar) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _result refcursor := 'data'; + _now timestamptz := now(); + _data text; + _valid boolean := false; +BEGIN + SELECT t.data INTO _data + FROM token.token t + WHERE t.hash = _hash + AND t.is_active + AND t.date_expired > _now + AND (t.read_limit = 0 OR t.read_used < t.read_limit); + + _valid := FOUND; + + OPEN _result FOR + SELECT _valid AS is_valid, _data AS data; + RETURN NEXT _result; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/functions/token_type/token_type_select.psql b/projects/pg/kit_auth_token_pg/functions/token_type/token_type_select.psql new file mode 100644 index 0000000..a88c871 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/functions/token_type/token_type_select.psql @@ -0,0 +1,15 @@ +-- Справочник: простой select без фильтров и пейджинга (правило 03). +CREATE OR REPLACE FUNCTION token.token_type_select() +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT id, title, key + FROM token.token_type + ORDER BY id; + RETURN NEXT _data; +END; +$$; diff --git a/projects/pg/kit_auth_token_pg/readme.md b/projects/pg/kit_auth_token_pg/readme.md new file mode 100644 index 0000000..bdd6d4a --- /dev/null +++ b/projects/pg/kit_auth_token_pg/readme.md @@ -0,0 +1,32 @@ +# Kit.Auth.Token.Db + +## Назначение + +База данных для управления токенами авторизации. Хранит владельцев токенов, типы токенов и сами токены с возможностью валидации, отзыва и удаления. + +## Движок + +PostgreSQL + +## Схема + +`token` + +## Модули + +### token +Основной модуль. Содержит таблицы и функции для работы с токенами. + +**Таблицы:** +- `owner` — владельцы токенов +- `token_type` — справочник типов токенов +- `token` — токены авторизации + +**Функции:** +- Управление владельцами: insert, select +- Управление токенами: insert, select, update, delete, consume, validate, revoke, revoke_by_owner +- Справочники: token_type_select + +## Связи + +- Связана с `Kit.Auth.Db` (модуль `auth`) \ No newline at end of file diff --git a/projects/pg/kit_auth_token_pg/scripts/create_full_20260723_124551.sql b/projects/pg/kit_auth_token_pg/scripts/create_full_20260723_124551.sql new file mode 100644 index 0000000..6671f18 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/scripts/create_full_20260723_124551.sql @@ -0,0 +1,382 @@ +-- Схема домена Token (собственная токен-БД Auth-сервиса: kit-auth-token). +-- Схема идентична Kit.Token.Db — её ожидает встроенный серверный модуль Kit.Token.Token. +-- Таблицы и функции (CRUD через хранимки) подключаются в init.sh (правила 03–04). +CREATE SCHEMA IF NOT EXISTS token; + + + +-- Справочник типов токена. id НЕ автоинкремент (на него ссылается token.type_id). +-- title обязателен (правило 03); key — естественный ключ для поиска типа из кода +-- (справочники ищутся по key/title, не по хардкод-id — правила 05–06). +CREATE TABLE token.token_type +( + id integer not null primary key, + title varchar(250) not null, + key varchar(250) not null unique +); + + + +-- Владелец/источник токена (ранее денормализованное поле token.app_name). +-- Обычная таблица: владельцы создаются динамически по ходу выпуска токенов. +-- key — естественный уникальный ключ для идемпотентного get-or-create (см. owner_insert). +CREATE TABLE token.owner +( + id serial not null primary key, + key varchar(250) not null unique, + title varchar(250) not null default '', + date_created timestamptz not null +); + + + +-- Токен: PK — текстовый hash (хеш токена, не сам токен в открытом виде). +-- owner_id — владелец/источник (ранее денормализованное поле app_name). +-- read_limit — разрешённое число обращений (0 = без лимита); read_used — уже израсходовано. +CREATE TABLE token.token +( + hash varchar(256) not null primary key, + type_id int not null references token.token_type (id), + owner_id int not null references token.owner (id), + date_created timestamptz not null, + date_expired timestamptz not null, + date_last_read timestamptz null, + data text not null, + lifetime_seconds int not null, + is_slide_expiration boolean not null, + read_limit int not null default 0, + read_used int not null default 0, + is_active boolean not null, + date_revoked timestamptz null, + revoke_reason varchar(250) null, + CONSTRAINT chk_token_lifetime CHECK (lifetime_seconds > 0), + CONSTRAINT chk_token_reads CHECK (read_used >= 0 AND (read_limit = 0 OR read_used <= read_limit)), + CONSTRAINT chk_token_expired CHECK (date_expired >= date_created) +); + +-- Индексы под фильтры token_select и фоновую чистку просроченных. +CREATE INDEX ix_token_owner ON token.token (owner_id); +CREATE INDEX ix_token_expired ON token.token (date_expired); +CREATE INDEX ix_token_active_type ON token.token (is_active, type_id); + + + +-- Справочник: простой select без фильтров и пейджинга (правило 03). +CREATE OR REPLACE FUNCTION token.token_type_select() +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT id, title, key + FROM token.token_type + ORDER BY id; + RETURN NEXT _data; +END; +$$; + + + +-- Выборка владельцев: фильтры по id/key/title, без пейджинга (правило 03). +-- key сравнивается на равенство (естественный ключ), title — текстовый ILIKE. +CREATE OR REPLACE FUNCTION token.owner_select( + _id int DEFAULT NULL, + _key varchar DEFAULT NULL, + _title varchar DEFAULT NULL +) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT id, key, title, date_created + FROM token.owner + WHERE (_id IS NULL OR id = _id) + AND (_key IS NULL OR key = _key) + AND (_title IS NULL OR title ILIKE '%' || _title || '%') + ORDER BY id; + RETURN NEXT _data; +END; +$$; + + + +-- Явная регистрация владельца по естественному ключу key (НЕ авто-создание при выпуске). +-- Идемпотентна: существует — возвращаем его id (title не перетираем); нет — создаём. +-- ON CONFLICT DO UPDATE (no-op по key) нужен, чтобы RETURNING вернул id и при конфликте; +-- это же делает функцию устойчивой к гонке двух параллельных выпусков (правило 06). +CREATE OR REPLACE FUNCTION token.owner_insert( + _key varchar, + _title varchar, + _date_created timestamptz +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _id int; +BEGIN + INSERT INTO token.owner(key, title, date_created) + VALUES (_key, _title, _date_created) + ON CONFLICT (key) DO UPDATE SET key = EXCLUDED.key + RETURNING id INTO _id; + RETURN _id; +END; +$$; + + + +-- Выборка токенов: фильтры по полям, без пейджинга (правило 03). +-- hash/owner_id/type_id — равенство; is_active — равенство; владельца фильтруем по id +-- (его естественный ключ резолвится через owner_select на стороне вызывающего кода). +CREATE OR REPLACE FUNCTION token.token_select( + _hash varchar DEFAULT NULL, + _type_id int DEFAULT NULL, + _owner_id int DEFAULT NULL, + _is_active boolean DEFAULT NULL +) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _data refcursor := 'data'; +BEGIN + OPEN _data FOR + SELECT hash, type_id, owner_id, date_created, date_expired, date_last_read, + data, lifetime_seconds, is_slide_expiration, read_limit, read_used, + is_active, date_revoked, revoke_reason + FROM token.token + WHERE (_hash IS NULL OR hash = _hash) + AND (_type_id IS NULL OR type_id = _type_id) + AND (_owner_id IS NULL OR owner_id = _owner_id) + AND (_is_active IS NULL OR is_active = _is_active) + ORDER BY date_created DESC; + RETURN NEXT _data; +END; +$$; + + + +-- Вставка токена. PK — текстовый hash (не serial), возвращается он же. +-- read_used/date_last_read/date_revoked при выпуске не задаются (значения по умолчанию). +-- owner_id должен существовать заранее (резолв/создание владельца — см. owner_insert). +CREATE OR REPLACE FUNCTION token.token_insert( + _hash varchar, + _date_created timestamptz, + _date_expired timestamptz, + _data text, + _lifetime_seconds int, + _is_slide_expiration boolean, + _read_limit int, + _is_active boolean, + _type_id int, + _owner_id int +) +RETURNS varchar +LANGUAGE plpgsql +AS $$ +BEGIN + INSERT INTO token.token( + hash, date_created, date_expired, data, lifetime_seconds, + is_slide_expiration, read_limit, is_active, type_id, owner_id) + VALUES ( + _hash, _date_created, _date_expired, _data, _lifetime_seconds, + _is_slide_expiration, _read_limit, _is_active, _type_id, _owner_id); + RETURN _hash; +END; +$$; + + + +-- Обновление токена по hash. date_created неизменяем и не обновляется. +-- read_used/date_last_read/date_revoked обновляются специализированными операциями +-- (потребление токена / отзыв), не общим update. +CREATE OR REPLACE FUNCTION token.token_update( + _hash varchar, + _date_expired timestamptz, + _data text, + _lifetime_seconds int, + _is_slide_expiration boolean, + _read_limit int, + _is_active boolean, + _type_id int, + _owner_id int +) +RETURNS varchar +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE token.token + SET date_expired = _date_expired, + data = _data, + lifetime_seconds = _lifetime_seconds, + is_slide_expiration = _is_slide_expiration, + read_limit = _read_limit, + is_active = _is_active, + type_id = _type_id, + owner_id = _owner_id + WHERE hash = _hash; + RETURN _hash; +END; +$$; + + + +-- Атомарное потребление токена за одно обращение (правило 06: логика записи — в БД). +-- Ð’ одной транзакции: проверка валидности, инкремент read_used, фиксация date_last_read, +-- продление срока при скользящей экспирации, авто-погашение при достижении лимита. +-- Возвращает курсор с одной строкой: data и итоговый признак is_valid обращения. +-- Невалиден (нет токена / неактивен / истёк / исчерпан лимит) → is_valid=false, data=NULL. +CREATE OR REPLACE FUNCTION token.token_consume(_hash varchar) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _result refcursor := 'data'; + _now timestamptz := now(); + _data text; + _valid boolean := false; +BEGIN + UPDATE token.token t + SET read_used = t.read_used + 1, + date_last_read = _now, + date_expired = CASE WHEN t.is_slide_expiration + THEN _now + make_interval(secs => t.lifetime_seconds) + ELSE t.date_expired END, + -- гасим, если этим обращением исчерпали лимит (0 = без лимита) + is_active = CASE WHEN t.read_limit > 0 AND t.read_used + 1 >= t.read_limit + THEN false ELSE t.is_active END + WHERE t.hash = _hash + AND t.is_active + AND t.date_expired > _now + AND (t.read_limit = 0 OR t.read_used < t.read_limit) + RETURNING t.data INTO _data; + + _valid := FOUND; + + OPEN _result FOR + SELECT _valid AS is_valid, _data AS data; + RETURN NEXT _result; +END; +$$; + + + +-- Проверка токена БЕЗ расхода обращения (peek): read_used/date_last_read не меняются. +-- Критерий валидности тот же, что в token_consume: активен, не истёк, лимит не исчерпан. +-- Используется там, где проверка не должна «съедать» обращение (middleware, предпроверки). +-- Возвращает курсор с одной строкой: is_valid и data (data=NULL, если невалиден). +CREATE OR REPLACE FUNCTION token.token_validate(_hash varchar) +RETURNS SETOF refcursor +LANGUAGE plpgsql +AS $$ +DECLARE + _result refcursor := 'data'; + _now timestamptz := now(); + _data text; + _valid boolean := false; +BEGIN + SELECT t.data INTO _data + FROM token.token t + WHERE t.hash = _hash + AND t.is_active + AND t.date_expired > _now + AND (t.read_limit = 0 OR t.read_used < t.read_limit); + + _valid := FOUND; + + OPEN _result FOR + SELECT _valid AS is_valid, _data AS data; + RETURN NEXT _result; +END; +$$; + + + +-- Мягкий отзыв одного токена по hash: is_active=false + фиксация момента и причины. +-- Отзываются только ещё активные токены (date_revoked/revoke_reason не перетираются +-- повторным вызовом). Возвращает число фактически отозванных (0 или 1). +CREATE OR REPLACE FUNCTION token.token_revoke( + _hash varchar, + _reason varchar +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _count int; +BEGIN + UPDATE token.token + SET is_active = false, + date_revoked = now(), + revoke_reason = _reason + WHERE hash = _hash + AND is_active; + GET DIAGNOSTICS _count = ROW_COUNT; + RETURN _count; +END; +$$; + + + +-- Массовый отзыв всех активных токенов владельца (logout всех сессий приложения, +-- реакция на компрометацию источника). Опирается на индекс ix_token_owner. +-- Возвращает число фактически отозванных токенов. +CREATE OR REPLACE FUNCTION token.token_revoke_by_owner( + _owner_id int, + _reason varchar +) +RETURNS int +LANGUAGE plpgsql +AS $$ +DECLARE + _count int; +BEGIN + UPDATE token.token + SET is_active = false, + date_revoked = now(), + revoke_reason = _reason + WHERE owner_id = _owner_id + AND is_active; + GET DIAGNOSTICS _count = ROW_COUNT; + RETURN _count; +END; +$$; + + + +-- Физическое удаление токена по hash (мягкое удаление таблицей не предусмотрено). +CREATE OR REPLACE FUNCTION token.token_delete(_hash varchar) +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + DELETE FROM token.token WHERE hash = _hash; +END; +$$; + + + +-- Стартовые данные схемы token (правило 03: seed справочников в post-deploy). +-- Типы токенов: id фиксированы (на них ссылается token.type_id), key — для поиска из кода. +INSERT INTO token.token_type(id, title, key) VALUES + (1, 'Сессия', 'session'), + (2, 'Подтверждение e-mail', 'email_confirm'), + (3, 'Сброс пароля', 'password_reset'), + (4, 'Magic-link (вход)', 'magic_link'), + (5, 'Одноразовый код', 'one_time_code') +ON CONFLICT (id) DO NOTHING; + +-- Владелец токенов этого сервиса (whitelist token.owner). Совпадает с Auth:TokenOwnerKey="auth" +-- (Program.cs дополнительно регистрирует его идемпотентно при старте). token_insert требует +-- существующий owner_id — неизвестный источник токен не получит. +INSERT INTO token.owner(key, title, date_created) VALUES + ('auth', 'Сервис авторизации', now()) +ON CONFLICT (key) DO NOTHING; + + + diff --git a/projects/pg/kit_auth_token_pg/settings/connection.json b/projects/pg/kit_auth_token_pg/settings/connection.json new file mode 100644 index 0000000..d0522ab --- /dev/null +++ b/projects/pg/kit_auth_token_pg/settings/connection.json @@ -0,0 +1,7 @@ +{ + "host": "localhost", + "port": 5432, + "database": "kit_auth_token", + "username": "postgres", + "password": "" +} \ No newline at end of file diff --git a/projects/pg/kit_auth_token_pg/tables/owner.psql b/projects/pg/kit_auth_token_pg/tables/owner.psql new file mode 100644 index 0000000..f974b51 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/tables/owner.psql @@ -0,0 +1,10 @@ +-- Владелец/источник токена (ранее денормализованное поле token.app_name). +-- Обычная таблица: владельцы создаются динамически по ходу выпуска токенов. +-- key — естественный уникальный ключ для идемпотентного get-or-create (см. owner_insert). +CREATE TABLE token.owner +( + id serial not null primary key, + key varchar(250) not null unique, + title varchar(250) not null default '', + date_created timestamptz not null +); diff --git a/projects/pg/kit_auth_token_pg/tables/token.psql b/projects/pg/kit_auth_token_pg/tables/token.psql new file mode 100644 index 0000000..a56e3f7 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/tables/token.psql @@ -0,0 +1,28 @@ +-- Токен: PK — текстовый hash (хеш токена, не сам токен в открытом виде). +-- owner_id — владелец/источник (ранее денормализованное поле app_name). +-- read_limit — разрешённое число обращений (0 = без лимита); read_used — уже израсходовано. +CREATE TABLE token.token +( + hash varchar(256) not null primary key, + type_id int not null references token.token_type (id), + owner_id int not null references token.owner (id), + date_created timestamptz not null, + date_expired timestamptz not null, + date_last_read timestamptz null, + data text not null, + lifetime_seconds int not null, + is_slide_expiration boolean not null, + read_limit int not null default 0, + read_used int not null default 0, + is_active boolean not null, + date_revoked timestamptz null, + revoke_reason varchar(250) null, + CONSTRAINT chk_token_lifetime CHECK (lifetime_seconds > 0), + CONSTRAINT chk_token_reads CHECK (read_used >= 0 AND (read_limit = 0 OR read_used <= read_limit)), + CONSTRAINT chk_token_expired CHECK (date_expired >= date_created) +); + +-- Индексы под фильтры token_select и фоновую чистку просроченных. +CREATE INDEX ix_token_owner ON token.token (owner_id); +CREATE INDEX ix_token_expired ON token.token (date_expired); +CREATE INDEX ix_token_active_type ON token.token (is_active, type_id); diff --git a/projects/pg/kit_auth_token_pg/tables/token_type.psql b/projects/pg/kit_auth_token_pg/tables/token_type.psql new file mode 100644 index 0000000..50b3904 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/tables/token_type.psql @@ -0,0 +1,9 @@ +-- Справочник типов токена. id НЕ автоинкремент (на него ссылается token.type_id). +-- title обязателен (правило 03); key — естественный ключ для поиска типа из кода +-- (справочники ищутся по key/title, не по хардкод-id — правила 05–06). +CREATE TABLE token.token_type +( + id integer not null primary key, + title varchar(250) not null, + key varchar(250) not null unique +); diff --git a/projects/pg/kit_auth_token_pg/token.post-deploy.psql b/projects/pg/kit_auth_token_pg/token.post-deploy.psql new file mode 100644 index 0000000..d705420 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/token.post-deploy.psql @@ -0,0 +1,16 @@ +-- Стартовые данные схемы token (правило 03: seed справочников в post-deploy). +-- Типы токенов: id фиксированы (на них ссылается token.type_id), key — для поиска из кода. +INSERT INTO token.token_type(id, title, key) VALUES + (1, 'Сессия', 'session'), + (2, 'Подтверждение e-mail', 'email_confirm'), + (3, 'Сброс пароля', 'password_reset'), + (4, 'Magic-link (вход)', 'magic_link'), + (5, 'Одноразовый код', 'one_time_code') +ON CONFLICT (id) DO NOTHING; + +-- Владелец токенов этого сервиса (whitelist token.owner). Совпадает с Auth:TokenOwnerKey="auth" +-- (Program.cs дополнительно регистрирует его идемпотентно при старте). token_insert требует +-- существующий owner_id — неизвестный источник токен не получит. +INSERT INTO token.owner(key, title, date_created) VALUES + ('auth', 'Сервис авторизации', now()) +ON CONFLICT (key) DO NOTHING; diff --git a/projects/pg/kit_auth_token_pg/token.psql b/projects/pg/kit_auth_token_pg/token.psql new file mode 100644 index 0000000..2fa2031 --- /dev/null +++ b/projects/pg/kit_auth_token_pg/token.psql @@ -0,0 +1,4 @@ +-- Схема домена Token (собственная токен-БД Auth-сервиса: kit-auth-token). +-- Схема идентична Kit.Token.Db — её ожидает встроенный серверный модуль Kit.Token.Token. +-- Таблицы и функции (CRUD через хранимки) подключаются в init.sh (правила 03–04). +CREATE SCHEMA IF NOT EXISTS token; diff --git a/rules/pg/db-project-rule.md b/rules/pg/db-project-rule.md new file mode 100644 index 0000000..21e477e --- /dev/null +++ b/rules/pg/db-project-rule.md @@ -0,0 +1,138 @@ +# Правило — Формирование проекта PostgreSQL БД + +--- + +## Структура проекта + +``` +projects/pg/{project_name}/ + readme.md + settings/ + connection.json + {schema}/ + tables/ + functions/ + scripts/ + init/ + {schema}.psql + deploy/ + _docs/ + diagrams/ + openapi.yaml + architecture.md +``` + +--- + +## Правила + +### 1. Схема = модуль + +Каждая схема PostgreSQL — это отдельная папка-модуль в корне проекта. + +### 2. Таблицы + +- Путь: `{schema}/tables/` +- Формат: `.psql` +- Одна таблица = один файл +- Именование: `{table_name}.psql` (например `user.psql`, `cabinet.psql`) +- Содержимое: `CREATE TABLE` с комментариями + +### 3. Функции + +- Путь: `{schema}/functions/{function_name}/` +- Каждая функция — отдельная папка +- Внутри: файл `.psql` с телом функции +- Именование папки: по имени функции (например `user_select_by_ids/`) + +### 4. Скрипты + +- Путь: `{schema}/scripts/` +- SQL-скрипты (миграции, seed-данные, утилиты) +- Именование миграций: `YYYY-MM-DD_description.sql` + +### 5. Init + +- Путь: `{schema}/init/` +- Скрипты инициализации: создание схемы, расширений, начальных данных +- Выполняются один раз при первом развёртывании + +### 6. Deploy + +- Путь: `deploy/` (в корне проекта) +- Инфраструктура развёртывания: docker-compose, bat-файлы, shell-скрипты +- НЕ SQL-миграции + +### 7. Главный файл модуля + +- Путь: `{schema}/{schema}.psql` +- Точка входа — подключает все таблицы и функции модуля +- Порядок: сначала таблицы, потом функции + +### 8. Post-deploy + +- Путь: `{schema}/{schema}.post-deploy.psql` +- Скрипты, выполняемые после деплоя (seed-данные, обновление зависимостей) + +--- + +## Пример: проект kit_auth_pg + +``` +projects/pg/kit_auth_pg/ + readme.md + settings/ + connection.json + auth/ + tables/ + user.psql + cabinet.psql + user_cabinet.psql + functions/ + user_select_by_ids/ + user_select_by_ids.psql + user_create/ + user_create.psql + scripts/ + seed_roles.sql + 2026-07-08_user-select-by-ids.sql + init/ + create_schema.psql + auth.psql + auth.post-deploy.psql + deploy/ + docker-compose.yml + up.bat + down.bat + restart.bat + init.sh + _docs/ + diagrams/ + openapi.yaml + architecture.md +``` + +--- + +## Настройки подключения + +Файл: `settings/connection.json` + +```json +{ + "host": "localhost", + "port": 5432, + "database": "{database_name}", + "username": "postgres", + "password": "" +} +``` + +--- + +## Инструменты + +- `apply-psql.sh` — применить SQL к БД +- `up.bat` / `down.bat` — запуск/остановка контейнера +- `docker-compose.yml` — конфигурация контейнера +- `export.bat` / `import.bat` — экспорт/импорт дампа \ No newline at end of file diff --git a/rules/sqlite/db-project-rule.md b/rules/sqlite/db-project-rule.md new file mode 100644 index 0000000..dbf0b2d --- /dev/null +++ b/rules/sqlite/db-project-rule.md @@ -0,0 +1,127 @@ +# Правило — Формирование проекта SQLite БД + +--- + +## Структура проекта + +``` +projects/sl3/{project_name}/ + readme.md + settings/ + connection.json + {module}/ + tables/ + scripts/ + init/ + {module}.sql + deploy/ + _docs/ + diagrams/ + openapi.yaml + architecture.md +``` + +--- + +## Правила + +### 1. Модуль + +Каждый функциональный модуль — отдельная папка в корне проекта. + +SQLite не поддерживает схемы, поэтому модуль = логическая группа объектов. + +### 2. Таблицы + +- Путь: `{module}/tables/` +- Формат: `.sql` +- Одна таблица = один файл +- Именование: `{table_name}.sql` (например `users.sql`, `sessions.sql`) +- Содержимое: `CREATE TABLE` с комментариями + +### 3. Скрипты + +- Путь: `{module}/scripts/` +- Произвольные SQL-скрипты (запросы, утилиты, seed-данные) +- SQLite не поддерживает хранимые процедуры — вся логика в скриптах + +### 4. Init + +- Путь: `{module}/init/` +- Скрипты инициализации: создание таблиц, индексов, начальных данных +- Выполняются один раз при первом развёртывании + +### 5. Deploy + +- Путь: `deploy/` (в корне проекта) +- Инфраструктура развёртывания: docker-compose, bat-файлы, shell-скрипты +- НЕ SQL-миграции + +### 6. Главный файл модуля + +- Путь: `{module}/{module}.sql` +- Точка входа — подключает все таблицы и скрипты модуля +- Порядок: сначала таблицы, потом скрипты + +--- + +## Особенности SQLite + +- Нет хранимых функций и процедур — папка `functions/` не используется +- Нет схем — все объекты в одном пространстве имён +- Один файл БД = один проект +- Типы данных ограничены: INTEGER, TEXT, REAL, BLOB, NUMERIC +- `PRAGMA` настройки размещаются в `init/` + +--- + +## Пример: проект kit_example_sl3 + +``` +projects/sl3/kit_example_sl3/ + readme.md + settings/ + connection.json + main/ + tables/ + users.sql + sessions.sql + roles.sql + scripts/ + seed_roles.sql + 2026-07-08_add-email-column.sql + init/ + create_indexes.sql + set_pragmas.sql + main.sql + deploy/ + docker-compose.yml + up.bat + down.bat + _docs/ + diagrams/ + openapi.yaml + architecture.md +``` + +--- + +## Настройки подключения + +Файл: `settings/connection.json` + +```json +{ + "database": "{database_name}.db", + "journal_mode": "WAL", + "foreign_keys": true +} +``` + +--- + +## Инструменты + +- `sqlite3 {database}.db < {script}.sql` — выполнить скрипт +- `.dump` — экспорт всей БД +- `.read` — выполнить файл SQL \ No newline at end of file diff --git a/Базы данных OT5.zip b/Базы данных OT5.zip new file mode 100644 index 0000000..bcd148d Binary files /dev/null and b/Базы данных OT5.zip differ