Files

244 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# История getpgdata — 2026-08-18
## Суть проекта
`getpgdata` — отдельное приложение для доступа к PostgreSQL (managed k8s, internal-only).
Разворачивается на платформе Nubes как **NodeJS managed service** (`nodejsk8s`).
Репозиторий: `https://gitea.services.ngcloud.ru/Nail/getpgdata.git` (ветка `master`).
Локальный путь: `/home/naeel/ipwhitelist-app/getpgdata`.
## 1. Создание (Flask-версия, ошибочная)
Первоначально приложение было собрано как **Flask (Python)** по howto `~/nubes/howto-flask-nubes.md`
(структура `site/app.py` + `requirements.txt`). Запушено коммитом `5c5e11c`.
**Функции Flask-версии:** главная со списком таблиц, просмотр таблицы, SQL-консоль (read-only), `/healthz`.
## 2. Ошибка пользователя и замена на Node.js
Пользователь указал, что перепутал стек — нужно **Node.js**, а не Flask.
**Решение:** Flask-файлы (`site/`, `requirements.txt`) удалены, создана Node.js структура
по образцу рабочего `ipwhitelist-app`:
```
getpgdata/
├── package.json # version 1.0.x; main server.js; start: node server.js
├── server.js # точка входа: Express + pg Pool
├── public/style.css
└── views/
├── index.ejs # главная: подключение + список таблиц
├── table.ejs # просмотр таблицы с пагинацией
├── query.ejs # SQL-консоль (read-only, только SELECT)
└── error.ejs
```
Коммит замены: `b061dcd`.
**Проверено:** `node -c server.js`, `npm install` (90 пакетов), реальный запуск сервера:
- `GET /` → 200 (рендер страницы)
- `GET /healthz` при недоступной БД → 503 (degraded, приложение не падает)
- `GET /table` без имени → 302 redirect
- `POST /query` не-SELECT → блокируется (read-only)
- статика `/public/style.css` → 200
## 3. Настройки подключения к БД
- Хост: `postgresqlk8s-master.60bdf3e3-5087-41ff-b760-fe6ea544a80e.svc.cluster.local`
- Порт: `5432`
- БД: `ipwhitelist` (коммит `102689d` — дефолт изменён с `postgres` на `ipwhitelist`)
- Пользователи: `super` (полный доступ) и `contracts` — оба с паролями из jsonEnv
- `DB_SSLMODE`: `disable`
Пароли в коде НЕ хранятся (читаются через `process.env.DB_PASS`). Готовые jsonEnv-блоки
на обоих пользователей добавлены в `README.md` (коммит `a890bb9`).
## 4. Проблема деплоя: под `nodejsk8s` не стартует
**Симптом (Nubes UI):**
> Приложение не запустилось. Производится полный откат установки.
> Error: jlib.k8s [correctReplicaActive] | ERROR | Под(ы) не работают: 'nodejsk8s' (Deployment).
**Диагноз (сравнение с работающим `whitelist`):**
| Параметр | `whitelist` (работает) | `getpgdata` (падал) |
|---|---|---|
| Точка входа | `server.js`, `main` в package.json | `server.js` ✓ |
| `npm start` | `node server.js` | `node server.js` ✓ |
| **Дефолтный порт** | **3000** | **5000** ✗ |
| Liveness | `/healthz` | `/healthz` ✓ |
| Старт без доступной БД | стартует | стартует ✓ |
**Гипотеза:** дефолтный порт `5000` (перенесён из Flask-версии) не совпадает с портом `3000`,
который ожидает платформа Nubes для NodeJS managed service. Health-проба не находила сервис →
под не Ready → «не работает nodejsk8s» → откат.
**Исправление:** дефолтный `PORT` в `server.js` изменён с `5000` на `3000`;
в `README.md` jsonEnv-блоки обновлены (`PORT: 3000`); `package.json` version → `1.0.1`.
**Проверено:** сервер запущен без env `PORT` → слушает `0.0.0.0:3000`, `/` → 200, `/healthz` → 503 (degraded).
**⚠️ НЕ проверено:** доступ к кластеру Nubes с этой машины отсутствует (`iot-naeel` и
`naeel-test-3` требуют OIDC-аутентификацию, managed-сервисы в них не видны). Поэтому
точную причину «почему под упал» по логам пода подтвердить нельзя — требуется лог пода
из Nubes UI. Если деплой после фикса порта снова не поднимется — смотреть логи пода
(`nodejsk8s` Deployment) в UI, а не только полагаться на гипотезу о порте.
## 5. Прочее
- `package-lock.json` закоммичен (зависимости: express, ejs, pg).
- `node_modules/` в `.gitignore`.
- Версия в `package.json` повышается при каждой правке кода (правило проекта).
## 6. Минимальный вывод на экран (фикс падения пода)
Несмотря на фикс порта (п.4), деплой снова упал — под `nodejsk8s` не поднялся.
**Требование пользователя:** «сделай код минимальным. Сам код не удаляй — сделай вывод
на экран какой-то информации и всё».
**Решение (коммит c8a776e, версия 1.0.2):**
- Роут `GET /` переписан так, чтобы **не зависеть от EJS-шаблонов**: отдаёт простой HTML
напрямую (`res.send`), выводя:
- версию Node, `APP_ENV`, PID
- конфиг БД (host, port, database, user)
- статус подключения к БД (или текст ошибки)
- список таблиц (или сообщение об ошибке)
- Каждая попытка работы с БД обёрнута в try/catch, поэтому страница **всегда** отдаёт 200
с информацией, даже если БД недоступна.
- Весь остальной код (роуты `/table`, `/query`, `/healthz`, `dbConfig`, `pool`) **не удалён** —
только главная страница упрощена.
**Проверено:** синтаксис OK; запуск без env `PORT` → слушает `0.0.0.0:3000`;
`GET /` → 200 (HTML с конфигом и статусом БД), `GET /healthz` при недоступной БД → 503 degraded.
**⚠️ Точная причина падения пода по-прежнему не подтверждена** (нет доступа к логам Nubes).
Если под не поднимется и после этого — смотреть логи `nodejsk8s` Deployment в Nubes UI:
replica не активна (CrashLoopBackOff, invalidImage, порт/health, зависший старт).
## 7. JSON API POST /api/sql (перенос данных в новую ПГ)
**Контекст:** нужно перенести белые списки (IP, кто создал, когда) из прежней БД в **новую**
PostgreSQL (тот же realm k8s). Доступ к новой БД — через Keycloak, автоматизировать нельзя.
Решение: код в репе `getpgdata` + передеплой с кредами новой ПГ.
**Что добавлено (версия 1.0.3):**
- Эндпоинт **`POST /api/sql`** — JSON API для произвольных SQL-команд.
Body: `{ "sql": "...", "params": [...] }` (params → `$1..$n`).
- `SELECT`/`WITH` → `{ ok:true, fields, rows, rowCount }` (всегда доступен)
- `INSERT`/`UPDATE`/`DELETE`/DDL → `{ ok:true, rowCount }` **только при `WRITE_SQL=1`**
- иначе → HTTP 403 «Write-SQL отключён»
- **Защита:** выполняется только первый statement (split по `;`) — блокирует мульти-команды
вроде `SELECT 1; DROP TABLE ...`.
- Флаг в env: `WRITE_SQL=1` включает запись.
**Логика переноса:**
1. Приложение подключено к прежней БД — читать данные через `/api/sql` (SELECT).
2. Сменить креды в jsonEnv на новую ПГ (+ `WRITE_SQL=1`).
3. Передеплой — теперь `/api/sql` выполняет запросы уже в новой БД; выполнить INSERT данных.
**Проверено:** `node -c server.js` OK; локально: пустой SQL → 400; SELECT → идёт в БД;
`INSERT` без `WRITE_SQL` → 403 (до попытки подключения); `WRITE_SQL=1` + мульти-statement →
берётся только первый; процесс проверки остановлен.
## 8. Перенос данных из прежней БД в новую (60bdf3e3 → a5e9d24f)
**Цель:** перенести белые списки (IP, кто создал, когда) и связанные данные в **новую**
PostgreSQL (инстанс `a5e9d24f`), тот же realm k8s. Доступ к новой БД — через Keycloak,
автоматизировать нельзя → перенос выполнен через `getpgdata` + `POST /api/sql`.
### 8.1 Выгрузка данных из прежней БД
Пока приложение было подключено к прежней БД (`60bdf3e3`), все таблицы выгружены через
`/api/sql` (SELECT * ORDER BY id) и **сохранены в репо** в `data/*.json`:
| Файл | Таблица | Строк | Статус |
|---|---|---|---|
| `data/companies.json` | companies | 9 | полный, валиден |
| `data/whitelist_entries.json` | whitelist_entries | 20 | полный, валиден |
| `data/audit_log.json` | audit_log | 39 | полный, валиден |
| `data/_migrations.json` | _migrations | 2 | полный, валиден |
| session | session | 1147 | исключён (битый дамп, не нужен для переноса) |
Сессии намеренно НЕ переносятся: это временные пользовательские сессии входа
(`connect-pg-simple`), в новой БД они создаются заново.
Коммиты: данные `~`, генератор `data/import.sql` (коммит `0b2d7ac`).
⚠️ **Урок (зафиксирован в памяти):** нужно было выгрузить ВСЕ таблицы до смены кредов.
Первая выгрузка сохранила только `whitelist_entries`; `companies`/`audit_log` пришлось
догонять после временного возврата кредов на прежнюю БД.
### 8.2 Переключение на новую БД
В jsonEnv Nubes (`modify`) заданы креды новой ПГ + `WRITE_SQL=1`:
```json
{
"DB_HOST": "postgresqlk8s-master.a5e9d24f-0b5d-4196-8903-7ed60f6e6557.svc.cluster.local",
"DB_PORT": "5432",
"DB_NAME": "ipwhitelist",
"DB_USER": "super",
"DB_PASS": "<пароль новой ПГ>",
"DB_SSLMODE": "disable",
"WRITE_SQL": "1",
"PORT": "3000"
}
```
После `modify` проверено: `GET /` показывает host новой ПГ; `CREATE TEMP TABLE` → 200
(запись разрешена).
### 8.3 Проблема: несоответствие id компаний
В новой БД **изначально уже были компании** под другими id (типовая схема), поэтому при
прямой вставке по старым id возникли конфликты:
- Вставка с явным id: `WZ03709`(id=23) упал — duplicate key по `client_id` (в новой БД
`WZ03709` уже был под id=2)
- `WZ01112` и `WZ03709` оказались на неверных id (1 и 2 вместо 2 и 23)
- `WZ01325` (должен быть id=1) — вообще отсутствовал
**Причина:** уникальный индекс `companies_client_id_key` (client_id), id в новой БД не
совпадали со старой.
**Решение (безопасно, т.к. записи/аудит в новой БД были пусты и FK на companies отсутствуют):**
1. `DELETE FROM companies WHERE client_id IN ('WZ01112','WZ03709')` — убрать с неверных id
2. Вставить заново под правильные id: id=1→WZ01325, id=2→WZ01112, id=23→WZ03709
3. Восстановить `custom_limit` и даты для этих 3 строк из дампа (первая вставка ставила `NOW()`)
После этого маппинг id компаний новой БД **полностью совпал** со старой.
### 8.4 Вставка данных через /api/sql
Так как `/api/sql` выполняет **один statement** за вызов, INSERT отправлены по одному
стейтменту (клиент на Python):
1. `companies` — 9 вставок (с явными id, `ON CONFLICT (id) DO NOTHING`)
2. `whitelist_entries` — 20 вставок (id, company_id, value_cidr, comment, created_by,
created_at, updated_by, updated_at, deleted_by, deleted_at)
3. `audit_log` — 39 вставок
4. `SELECT setval(...)` — сброс sequence для трёх таблиц на MAX(id)
### 8.5 Проверка целостности и соответствия
После переноса выполнена **посимвольная сверка** новой БД против дампа старой:
| Таблица | Строк old/new | missing | extra | diffs | Результат |
|---|---|---|---|---|---|
| whitelist_entries | 20/20 | 0 | 0 | 0 | **MATCH** |
| audit_log | 39/39 | 0 | 0 | 0 | **MATCH** |
| companies | 9/9 | 0 | 0 | 0 (после фикса) | **MATCH** |
- `orphan` (записи без компании): **0** — все связи верны
- sequences: companies→368, whitelist_entries→20, audit_log→39
### 8.6 Итоговое состояние новой БД
- companies = 9
- whitelist_entries = 20 (из них активных 12, удалённых 8 — перенесены с deleted_at)
- audit_log = 39
- Данные идентичны прежней БД.
**Активные белые списки (12):** WZ02425 (2), WZ02727 (6), WZ03656 (2), WZ01112 (8.8.8.8).