- JsComponentRenderer: ctx.libs.charts (recharts) — графики в кастомных страницах и JS-вкладках без redeploy; справка get_js_coding_reference обновлена - Внешние БД (миграция 0088): вкладка «Настройки → Внешние БД» — реестр подключений (пароль AES-256-GCM, env EXTERNAL_DB_SECRET, по API не отдаётся), тест подключения, allowlist таблиц на подключение - Read-only шлюз POST /api/external-db/:id/query (finance.manage): только SELECT, без мульти-выражений, обёртка с LIMIT 5000, statement_timeout 10с, проверка таблиц по allowlist, аудит в external_db_query_log (кто/SQL/строки/мс) - Назначение: ИИ-агенты строят отчёты и виджеты поверх витрин (напр. НДС 1С) без новых серверных эндпоинтов и пересборки
52 lines
2.5 KiB
SQL
52 lines
2.5 KiB
SQL
-- Внешние подключения к БД (read-only шлюз для отчётов/виджетов, в т.ч. ИИ-агентами).
|
|
-- Пароль хранится в AES-256-GCM (server/routes/external-db.routes.ts), по API не отдаётся.
|
|
-- allowed_tables — jsonb-массив имён таблиц (lowercase), доступных через шлюз;
|
|
-- пустой массив = шлюз закрыт (админ обязан явно указать таблицы).
|
|
|
|
CREATE TABLE IF NOT EXISTS external_db_connections (
|
|
id SERIAL PRIMARY KEY,
|
|
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
host TEXT NOT NULL,
|
|
port INTEGER NOT NULL DEFAULT 5432,
|
|
database TEXT NOT NULL,
|
|
username TEXT NOT NULL,
|
|
password_encrypted TEXT NOT NULL,
|
|
ssl BOOLEAN NOT NULL DEFAULT false,
|
|
allowed_tables JSONB NOT NULL DEFAULT '[]',
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS external_db_connections_org_idx ON external_db_connections (organization_id);
|
|
|
|
-- Журнал запросов шлюза: кто, какой SQL, сколько строк, сколько мс
|
|
CREATE TABLE IF NOT EXISTS external_db_query_log (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
connection_id INTEGER NOT NULL REFERENCES external_db_connections(id) ON DELETE CASCADE,
|
|
user_id INTEGER,
|
|
bot_id INTEGER,
|
|
sql_text TEXT NOT NULL,
|
|
row_count INTEGER NOT NULL DEFAULT 0,
|
|
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
error TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
CREATE INDEX IF NOT EXISTS external_db_query_log_org_idx ON external_db_query_log (organization_id, created_at DESC);
|
|
|
|
-- Tenant isolation
|
|
ALTER TABLE external_db_connections ENABLE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS tenant_iso ON external_db_connections;
|
|
CREATE POLICY tenant_iso ON external_db_connections USING (
|
|
current_setting('app.is_superadmin', true) = 'true'
|
|
OR organization_id = NULLIF(current_setting('app.current_org_id', true), '')::int
|
|
);
|
|
|
|
ALTER TABLE external_db_query_log ENABLE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS tenant_iso ON external_db_query_log;
|
|
CREATE POLICY tenant_iso ON external_db_query_log USING (
|
|
current_setting('app.is_superadmin', true) = 'true'
|
|
OR organization_id = NULLIF(current_setting('app.current_org_id', true), '')::int
|
|
);
|