netpulse-migrate замість PowerShell-скрипта: у контейнері немає ані psql, ані PowerShell, а тягнути клієнт Postgres в образ заради одного запуску — це половина дистрибутива на порожньому місці. Міграції вшиті через embed і переїхали в server/migrations: embed не бачить нічого за межами кореня свого модуля, а міграції поруч із бінарником, який їх накочує, не можуть розійтися версіями. Накочування під advisory-блокуванням: два інстанси при rolling update інакше застосували б ту саму міграцію двічі. Кожен файл в одній транзакції разом із записом у schema_migrations; виняток — continuous aggregates, які TimescaleDB забороняє в транзакції. Змінена вже застосована міграція зупиняє запуск: у різних інсталяціях інакше опиниться різна схема під одним номером. Перевірено на чистій базі: 23 міграції, 101 таблиця, повторний запуск каже «схема актуальна». Веб віддає сам API через embed: на self-hosted це прибирає з інструкції встановлення цілий компонент. Три політики кешування — назавжди для assets із хешем у імені, ніколи для index.html, коротко для решти. Знайдено живим прогоном: невідомий шлях під /api/ віддавав 200 з index.html, і клієнт падав на розборі HTML як JSON замість чесного 404. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52 lines
3.1 KiB
SQL
52 lines
3.1 KiB
SQL
-- =====================================================================
|
||
-- NetPulse :: 0023_agent_enrollment.sql
|
||
-- Реєстрація зонда одноразовим токеном.
|
||
--
|
||
-- Досі зонд заводився INSERT-ом у БД, а токен вписувався в командний
|
||
-- рядок руками. На стенді це прийнятно; у клієнта — ні: людина, яка
|
||
-- ставить агента, не має доступу до бази й не повинна його мати.
|
||
--
|
||
-- Схема така: адміністратор створює запрошення в UI, копіює команду
|
||
-- встановлення, агент обмінює одноразовий токен на постійний. Токен
|
||
-- згорає при першому використанні.
|
||
-- =====================================================================
|
||
|
||
CREATE TABLE core.agent_enrollments (
|
||
id uuid PRIMARY KEY DEFAULT core.new_id(),
|
||
tenant_id uuid NOT NULL REFERENCES core.tenants(id) ON DELETE CASCADE,
|
||
-- sha256 токена. Сам токен існує рівно один раз — у відповіді на
|
||
-- створення. Зберігати його означало б тримати в базі чинний доступ.
|
||
token_hash bytea NOT NULL UNIQUE,
|
||
-- Перші символи для впізнавання в списку: «np_enr_a1b2…».
|
||
prefix text NOT NULL,
|
||
-- Підказка імені майбутнього зонда. Саме підказка: остаточне ім'я
|
||
-- може прийти від самого агента або отримати суфікс при збігу.
|
||
name_hint text,
|
||
site_id uuid REFERENCES inv.sites(id) ON DELETE SET NULL,
|
||
-- Модулі, з якими зонд стартує. Далі керуються з UI.
|
||
modules core.slug[] NOT NULL DEFAULT '{icmp}',
|
||
|
||
expires_at timestamptz NOT NULL,
|
||
used_at timestamptz,
|
||
used_by uuid REFERENCES core.agents(id) ON DELETE SET NULL,
|
||
created_by uuid REFERENCES core.users(id) ON DELETE SET NULL,
|
||
created_at timestamptz NOT NULL DEFAULT now()
|
||
);
|
||
CREATE INDEX agent_enrollments_tenant_idx ON core.agent_enrollments (tenant_id)
|
||
WHERE used_at IS NULL;
|
||
|
||
ALTER TABLE core.agent_enrollments ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE core.agent_enrollments FORCE ROW LEVEL SECURITY;
|
||
CREATE POLICY tenant_isolation ON core.agent_enrollments
|
||
USING (tenant_id = core.current_tenant())
|
||
WITH CHECK (tenant_id = core.current_tenant());
|
||
|
||
GRANT SELECT, INSERT, UPDATE, DELETE ON core.agent_enrollments
|
||
TO netpulse_app, netpulse_worker;
|
||
|
||
-- Звідки взявся зонд: заведений руками чи прийшов сам по запрошенню.
|
||
-- Потрібно журналу аудиту й тому, хто через рік питає «а це що за
|
||
-- агент і хто його ставив».
|
||
ALTER TABLE core.agents
|
||
ADD COLUMN enrolled_at timestamptz,
|
||
ADD COLUMN enrolled_by uuid REFERENCES core.users(id) ON DELETE SET NULL;
|