Export: убран filename из Content-Disposition для диалога сохранения

Версия: 0.1.13
Документация: db-query-endpoint.md
This commit is contained in:
2026-07-08 14:57:17 +04:00
parent d20bd85a7d
commit 4bdcb4c363
5 changed files with 81 additions and 5 deletions
+76
View File
@@ -0,0 +1,76 @@
# Endpoint SQL-запросов к БД (`/v2/db/query`)
> Создан: 2026-07-08
## Назначение
Дать DevOps-инженеру (одному человеку, заданному через `ADMIN_EMAIL`) возможность выполнять SQL-запросы к БД напрямую из браузера — без psql, без kubectl, без внешнего IP.
## Доступ
- Только один пользователь: `req.email === ADMIN_EMAIL`
- Обычный юзер сервиса (проходит SSO как все)
- Пример: `ADMIN_EMAIL=atazet@narod.ru`
## Адрес
```
GET /v2/db/query?sql=<часть запроса после SELECT>
```
## Как работает
1. Пользователь заходит в сервис через SSO (любой URL, например `/v2/app`)
2. Меняет в адресной строке путь на `/v2/db/query?sql=...`
3. Сервер:
- Проверяет сессию (resolveContext)
- Проверяет что `req.email === ADMIN_EMAIL`
- Добавляет префикс `SELECT` перед вводом пользователя
- Выполняет: `pool.query('SELECT ' + userInput)`
- Возвращает JSON
## Примеры
| Ввод в браузере | Что выполняется |
|---|---|
| `/v2/db/query?sql=* FROM _migrations` | `SELECT * FROM _migrations` |
| `/v2/db/query?sql=count(*) FROM companies` | `SELECT count(*) FROM companies` |
| `/v2/db/query?sql=* FROM whitelist_entries LIMIT 10` | `SELECT * FROM whitelist_entries LIMIT 10` |
| `/v2/db/query?sql=* FROM audit_log ORDER BY created_at DESC LIMIT 5` | `SELECT * FROM audit_log ORDER BY created_at DESC LIMIT 5` |
## Безопасность
1. **SELECT префикс** — жёстко добавляется `SELECT` перед вводом пользователя. DROP/INSERT/UPDATE/DELETE не работают:
```
/v2/db/query?sql=DROP TABLE companies
→ SELECT DROP TABLE companies (ошибка SQL, не выполнится)
```
2. **Одиночные запросы** — драйвер `pg` не поддерживает множественные запросы в одном `query()`. Попытка `1; DROP TABLE` не сработает.
3. **Доступ по email** — только один человек, заданный в `ADMIN_EMAIL`.
4. **Rate limiter** — endpoint защищён лимитером (как mutationLimiter).
## Ответ
```json
{
"rows": [ ... ],
"rowCount": 5,
"fields": ["id", "filename", "applied_at"]
}
```
## Реализация
- Файл: `v2/src/db/query.js`
- Middleware: `resolveContext` + проверка `req.canQueryDB`
- Роутер монтируется в `v2/server.js` на `/db`
- Флаг `req.canQueryDB` выставляется в `v2/src/router/index.js` (applyContext)
## Ограничения
- Только SELECT (через префикс)
- Нет GUI — только JSON в браузере
- SQL в URL (логируется в access_log nginx/ingress — это нормально для devops-инструмента)