Преминете към основното съдържание

Модел на данните

Първоизточник: entrosity-axis.backend/db/migrations/*.sql и заявките в entrosity-axis.backend/db/queries/*.sql.

PostgreSQL 18. Конвенции:

  • Първичните ключове са uuid (UUIDv7, генерирани в Go, така че се сортират по време).
  • Всички времеви отметки са timestamptz; created_at/updated_at се поддържат от приложението.
  • Изброимите типове са text с ограничения CHECK (по-лесни за разширяване от enum типовете на Postgres).
  • Всяка таблица, принадлежаща на тенант, има tenant_id uuid NOT NULL REFERENCES tenants(id) и tenant_id като първа колона във всеки индекс, използван за списъци.
  • Меко изтриване (deleted_at) само за devices и packages (историята трябва да се запази); всичко останало се изтрива окончателно с ON DELETE CASCADE, където това е безопасно.
  • Разширения: citext (имейли), pg_trgm (търсене), pgcrypto (незадължително), pg_stat_statements, когато е предварително зареден; собственият набор от миграции на river за опашката за задачи.
  • Защитата на ниво ред (row-level security) (0007) е включена и наложена принудително за всяка таблица, принадлежаща на тенант. Ролята на приложението rmm_app вижда само редовете на тенанта на сесията (app.tenant_id) или всички редове при app.bypass. Редовете с tenant_id IS NULL са споделени и могат да се четат от всички. Вижте Множество тенанти.
  • Препратките между таблици на тенантите са съставни (tenant_id, x_id) → parent(tenant_id, id), така че ред никога не може да сочи към друг тенант.

Идентичност и тенанти​

tenants​

колонатипбележки
iduuid PK
nametextпоказвано име
slugcitext UNIQUEизползва се в URL адреси/имейли
statustextactive / suspended
settingsjsonbнастройки на ниво тенант, управлявани от Axis (часова зона по подразбиране, канал за обновяване, отмени на сроковете за съхранение)
created_at, updated_attimestamptz

sites​

Физическо/логическо местоположение в рамките на тенант; един AD домейн обикновено съответства на един обект. timezone (IANA) се използва за прозорците за поддръжка.

users​

Копие на потребителите на Entrosity Hub (със същите идентификатори), поддържано от синхронизацията за атрибуция (одитен журнал, задачи, изпълнения на скриптове) и роли. Потребителите са глобални редове; към кои тенанти принадлежи даден потребител и каква е ролята му във всеки от тях, се съхранява в tenant_memberships. Axis не съхранява идентификационни данни, сесии или броячи на влизания.

колонабележки
emailcitext, уникален сред потребителите, които не са deleted
display_nameкакто в Hub
statusactive / disabled / deleted (изтритите потребители остават, за да запазят миналите действия своя автор)
is_global_adminадминистраторите на платформата в Hub: всички права във всеки тенант
created_at, updated_at

Защита на ниво ред: всяка сесия може да чете потребителите; само глобалният обхват (синхронизацията с Hub) може да ги записва.

tenant_memberships​

По един ред за потребител и тенант: role (tenant_admin / technician / viewer) и предпочитанията на потребителя за имейли за аларми в този тенант (notification_prefs, alert_digest_at, а от 0014 и маркерите за доставка offline_rollup_at, resolved_mailed_at, quiet_held_since; синхронизацията с Hub ги запазва). Защитата на ниво ред го ограничава до тенанта.

platform_sync_state, platform_sessions_revoked, platform_step_ups_used​

ETag на последната моментна снимка на достъпа, приложена от Entrosity Hub; приключилите сесии на платформата (токените им се отказват преди да изтекат; пазят се един час); и идентификаторите (jti) на вече използваните step-up токени, така че всеки от тях да потвърждава само едно действие на която и да е реплика (изчистват се след изтичане).

Агенти, конектори, устройства​

enrollment_tokens​

kind е agent или connector. max_uses NULL = неограничено. Токените се показват веднъж при създаването; след това се пази само token_hash. Агентите, регистрирани с токен, записват enrollment_token_id за одит.

devices​

Централната таблица. По един ред за всеки управляван или открит компютър.

групаколони
идентичностhostname, fqdn, domain, machine_sid, machine_guid (HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid), agent_id
ОС/хардуерos_name, os_version, os_build, os_arch, manufacturer, model, serial_number, cpu_model, cpu_cores, ram_bytes, last_boot_at, last_user
обобщение на мрежатаip_addresses jsonb, mac_addresses jsonb (подробности в device_network_adapters)
състояниеstatus (online/offline/never_connected/decommissioned), source (agent/ad/both), agent_version, last_seen_at, last_inventory_at
ADad_object_guid, ad_dn, ad_ou, ad_enabled, ad_last_logon_at
потребителски данниtags text[], custom_fields jsonb, site_id

Индекси: UNIQUE(tenant_id, machine_guid) WHERE machine_guid IS NOT NULL, (tenant_id, lower(hostname)), (tenant_id, ad_object_guid), (tenant_id, status), GIN върху tags, триграмен GIN върху hostname за търсене.

От 0008:

  • search_text е генерирана колона (име на хоста, FQDN, показвано име, последен потребител, сериен номер, IP адреси) с триграмен индекс, който обслужва търсенето на устройства.
  • inventory_hashes (jsonb) съдържа отпечатък за всяка секция на инвентаризацията. При приемането секциите, които не са се променили, се пропускат.
  • Индексите за сортиране покриват колоните на списъка с устройства и offline_since.

agents​

По един ред за всяка инсталация на агента, device_id е уникален. Съдържа auth_key_hash, version, connection_state, last_heartbeat_at, config_version (увеличава се, когато конфигурацията на агента от страна на сървъра се промени, за да я изтегли агентът отново).

Таблици на инвентаризацията​

device_inventory_snapshots пази суровия отчет (payload jsonb, съхранение 30 дни). Нормализираните таблици (device_software, device_disks, device_network_adapters, device_services, device_updates, device_local_users) се заменят за всяко устройство при всеки отчет в една транзакция (изтриване + вмъкване или upsert със seen_at и изтриване на остарелите). device_software има UNIQUE(device_id, name, version) и триграмен индекс върху name за филтъра „устройства, на които има софтуер X“.

device_metrics​

device_metrics(device_id uuid, ts timestamptz, cpu_pct real, mem_pct real, disk_pct jsonb, cpu_temp_c real) -- cpu_temp_c NULL without a sensor (0016)
PARTITION BY RANGE (ts) -- monthly partitions, created 2 months ahead by the daily metrics.partitions job
PRIMARY KEY (device_id, ts)

jobs​

Обща единица работа за агент (device_id) или конектор (connector_id). payload и result са jsonb, типизирани според type (схемите са в entrosity-shared-go/proto). Индекси върху (device_id, status), (connector_id, status), (deployment_target_id), (expires_at) WHERE status IN ('created','sent','acked','running').

remote_sessions​

По един ред за всяка сесия на отдалечен работен плот: device_id, agent_id (агентът на устройството към момента на създаване на сесията; отнемането му прекратява сесията), job_id (задачата remote_desktop), mode (control/view), require_consent, show_banner (лентата на сесията на устройството; миграция 0011), status (pending → active → ended или failed, когато сесията изобщо не е започнала), created_by, ticket_hash (SHA-256 на еднократния билет за преглед, изчиства се при използване или при приключване на сесията) и ticket_expires_at, agent_node (URL адресът на репликата, която държи потока на агента), end_code (код за затваряне на потока) и end_reason, created_at, started_at, ended_at. Уникален частичен индекс позволява една отворена (pending или active) сесия на устройство. Приключилите сесии се изчистват заедно с историята на задачите (RMM_RETENTION_JOB_DAYS или отмяната за тенанта).

AD синхронизация​

connectors​

По един ред за всяка инсталация на конектор за обекта: име на хоста, домейн, версия, възможности, auth_key_hash, присъствие (status, last_seen_at), revoked_at. UNIQUE(tenant_id, lower(hostname), lower(domain)) WHERE revoked_at IS NULL — повторното инсталиране на същата машина завърта ключа. Изтриването на конектор го отнема и изтрива неговите конфигурации за синхронизация.

ad_sync_configs​

По една за всяка синхронизация на домейн; принадлежи на конектор. bind_password_enc, push_credential_enc и push_token_enc (суровият токен за регистриране на агента, предаван на инсталациите чрез push, push_token_id/push_token_expires_at) са AES-GCM шифротекст (base64, с префикс на идентификатора на ключа) с допълнителни данни, които ги обвързват с идентификатора на конфигурацията. tls_mode е ldaps/starttls/none; computer_filter се комбинира чрез AND с (objectCategory=computer); ou_include/ou_exclude съдържат DN. Състояние на планирането: last_sync_at, last_sync_status, consecutive_failures, next_sync_at (интервал × 2^неуспехи, максимум 24 h). auto_push_agent инсталира агента чрез push на новооткритите активни компютри след всяка синхронизация.

ad_sync_runs​

По един за всяко изпълнение (trigger manual/schedule, status pending/running/succeeded/failed, job_id), най-много едно активно за конфигурация (частичен уникален индекс). Броячите seen, created_count, updated_count, gone_count, matched_count; received_seqs и expected_total правят приемането на частите идемпотентно и независимо от реда. ad_sync_seen(run_id, object_guid) записва GUID идентификаторите, видени от изпълнението (безопасно „маркиране като изчезнали“ при няколко реплики); изчиства се при приключване на изпълнението.

ad_computers​

Огледално копие на компютърните обекти в AD. object_guid е уникален в рамките на тенанта; device_id сочи към съпоставеното устройство. gone_since се задава, когато обект изчезне от AD, и се изчиства, ако се появи отново; устройствата само от AD, чийто обект липсва повече от 30 дни, получават custom_fields.ad_missing = "true" (никога не се изтриват автоматично). Състояние на push инсталирането: push_status (queued/connecting/copying/installing/success/failed), push_error_code, push_error, push_at, push_job_id.

Пакети и внедрявания​

packages​

tenant_id NULL означава глобален пакет, създаден от глобален администратор и видим за всички тенанти (само за четене за тях). kind определя кои полета са задължителни:

  • msi/exe: object_key, sha256, size_bytes, install_args, uninstall_args
  • powershell: object_key на .ps1 (или вграден script_content)
  • winget: winget_id, незадължителен winget_version, winget_source (winget/msstore)

status е draft, докато каченият файл не бъде финализиран (обектът е проверен със stat, SHA-256 и размерът са сверени с декларираното от браузъра, метаданните на MSI, като msi_product_code, са прочетени, доколкото е възможно), след което става ready; winget пакетите и вграденият PowerShell са ready още при създаването. Обектите се намират в packages/<tenant or global>/<package id>/<sanitized file name>. Колони за изпълнението: install_args, uninstall_args, success_exit_codes (по подразбиране {0,3010,1641}), requires_reboot, timeout_seconds (60–86400), run_as (system/logged_on_user), arch, min_os_build. Пакетите се изтриват меко (deleted_at, обектът се запазва, защото приключилите внедрявания все още сочат към пакета); изтриването на пакет, използван от неприключило внедряване, се отказва с package_in_use.

Примери за detection jsonb:

{"type":"msi_product_code","product_code":"{GUID}"}
{"type":"registry","key":"HKLM\\SOFTWARE\\Vendor\\App","value":"Version","op":">=","expected":"2.0"}
{"type":"file","path":"C:\\Program Files\\App\\app.exe","min_version":"2.0.0"}
{"type":"winget","id":"Vendor.App"}

deployments​

action е install или uninstall. target_kind: all (целият тенант), devices (изричен списък, съхраняван в deployment_targets), filter (филтър за устройства в target_filter jsonb, включително условия software_present/software_absent {name, version_op?, version?}; определя се при стартирането и отново при „добавяне на нови устройства“). Устройствата без агент или изведените от експлоатация се изключват и се отчитат в excluded_count.

Планиране: schedule_kind now / at (scheduled_at) / window (ежедневен window_start–window_end във формат HH:MM; край < начало преминава през полунощ; оценява се в часовата зона на обекта на всяко устройство или в settings.default_timezone на тенанта). max_concurrency (1–1000), retry_count (0–10), retry_backoff_seconds, reboot_policy, expires_at. status: scheduled → running ⇄ paused → completed | failed | cancelled (failed, когато има неуспешни цели или цели с изтекло време и нито една не е успешна или пропусната, иначе completed). Колоните-броячи count_pending, count_queued, count_running (downloading + installing), count_success, count_failed, count_skipped, count_cancelled, count_timeout се преизчисляват от целите след всяка промяна за евтино показване на списъците.

deployment_targets​

По един ред за (внедряване, устройство), UNIQUE(deployment_id, device_id); две внедрявания на един и същ пакет на едно устройство са позволени. Колони: attempt, next_attempt_at (неуспешна цел, която чака повторен опит, е pending с бъдещ next_attempt_at), job_id (текущата задача; jobs.deployment_target_id сочи обратно), exit_code, error_code, last_error, output_tail, started_at, finished_at. Преходи между състоянията:

pending → queued → downloading → installing → success
→ failed (retry ≤ retry_count → pending)
pending → skipped (detection says already installed)
pending/queued → cancelled | timeout
failed | timeout → pending (manual retry)

winget_index​

Моментна снимка на общностния източник на winget (id, name, publisher, versions[]), опреснявана приблизително ежедневно от задачата winget.refresh от source.msix (RMM_WINGET_SOURCE_URL), в която съветникът за пакети търси чрез триграмни индекси.

SQL функции​

  • deployment_in_window(ts, tz, window_start, window_end) – дали ts в часовата зона tz попада в ежедневния прозорец; празен прозорец винаги съвпада, непозната зона – никога.
  • version_cmp(a, b) – сравнение на версии, разделени с точки (числово за всяка част, когато и двете части са числа, иначе текстово без отчитане на регистъра; началното v се игнорира), връща -1/0/1; използва се от условията за софтуер при целите.

Скриптове, аларми, издания, одит​

scripts, script_versions​

tenant_id NULL е глобалната библиотека (управлява се от глобалните администратори, може да се изпълнява от всеки тенант, но там е само за четене). language powershell (5.1) / pwsh / cmd; params_schema е подмножество на JSON Schema (properties от тип string/integer/number/boolean с title, default, enum, minimum/maximum, maxLength; required; x-order), което порталът показва като формуляр и което се валидира на сървъра преди изпълнение. Промяната на content или params_schema увеличава current_version и добавя запис в script_versions (script_id, version, content, params_schema, created_by, created_at); изтриването е меко (deleted_at) и запазва изпълненията.

script_runs​

По един за всяко устройство при всяка заявка за изпълнение: script_id + script_version + script_name (запазва се, ако скриптът бъде изтрит), job_id (уникален), params (валидирани, типизирани), run_as, status queued → running → succeeded|failed|timeout|cancelled, exit_code, error_code/error. output съдържа поточно предадения текст (частите в реално време се добавят по ред на своето байтово отместване); когато агентът качи пълния изход (PUT /jobs/{id}/output, ≤ 10 MiB), той отива в обектното хранилище под output_object_key, а output пази края му; output_bytes, output_truncated.

alert_rules, alert_rule_overrides, alerts​

  • alert_rules.tenant_id NULL са глобални правила, които се прилагат за всеки тенант; тенант може да изключи глобално правило за себе си чрез alert_rule_overrides (rule_id, tenant_id, enabled). Типове и ключове на condition: device_offline {minutes}, disk_free_pct {below, drive?}, agent_outdated {min_version?} (по подразбиране: най-новото стабилно издание на агента), cpu_pct/mem_pct {above, minutes} (средна стойност за прозореца), cpu_temp {above_c, minutes} (°C, средна стойност на пробите, които имат температура; 0016 създава глобалното правило CPU temperature high, 90 °C за 10 min), deployment_failed {threshold_pct} (по подразбиране 20: дял на неуспешните цели и целите с изтекло време от внедряване, създадено през последната седмица), adsync_failed {}, connector_offline {minutes}. Миграция 0006 създава пет глобални правила по подразбиране (офлайн 60 min, диск под 10 %, остарял агент, конектор офлайн 15 min с критична сериозност, неуспешна AD синхронизация).
  • alerts са за правило и субект (device, connector, deployment, adsync_config), с частичен уникален индекс, така че даден субект има най-много една активна (отворена или потвърдена) аларма за правило. Механизмът сам ги отваря, опреснява съобщението им и ги разрешава; потребителите могат да ги потвърждават (алармата пак се разрешава автоматично) или разрешават.
  • tenant_memberships.notification_prefs {alert_email: off|immediate|digest, min_severity, digest_interval: 15m|hourly|daily, digest_hour, timezone, offline_batch_minutes: 5|15|30|60, muted_types[], notify_resolved, quiet_hours{enabled, start, end, critical_bypass}} (стойности по подразбиране: immediate, warning, 15m, 8, зоната на тенанта, 15, няма, false, off/22:00–07:00/bypass) и alert_digest_at (последното изпратено обобщение).

agent_releases​

(component agent|connector, version) е уникален; channel stable/beta; status draft → published; object_key (releases/<component>/<version>/rmm-<component>.msi), size_bytes, sha256 (проверява се спрямо качения обект при публикуване), signature (Ed25519 върху манифеста на изданието, създава се при публикуване), rollout_pct, notes. agents и connectors получиха update_version / update_requested_at (последното предложение, за едночасовия период на изчакване преди повторен опит).

device_logons​

По един ред за всяко влизане в Windows, докладвано от агента: device_id, username, session_id, session_type (console/remote), client, logon_at, logoff_at (NULL, докато потребителят е влязъл), logoff_estimated. Уникален (device_id, session_id, logon_at); RLS както при другите таблици на тенантите. Редовете се изтриват 7 дни след logoff_at (фиксирано; категория за съхранение device_logons), както и заедно с устройството.

audit_log​

Само за добавяне (без права за UPDATE/DELETE за ролята на приложението); before/after съдържат редактирани моментни снимки (тайните са премахнати). Автоматизацията на изданията записва записи без потребител (actor_user_id NULL).

Миграции​

№файлсъдържание
0001initразширения, tenants, sites, users, refresh_tokens, password_resets, invitations, audit_log
0002agents_devicesenrollment_tokens, devices, agents, таблици на инвентаризацията, jobs
0003metricsdevice_metrics с дялове + помощна функция за създаване на дялове
0004adsyncconnectors, ad_sync_configs, ad_sync_runs, ad_computers
0005packages_deploymentspackages, deployments, deployment_targets
0006scripts_alertsscripts, script_versions, script_runs, alert_rules (+ глобални правила по подразбиране), alert_rule_overrides, alerts, agent_releases; users.notification_prefs; колони за предложения за обновяване в agents/connectors
0007rlsроля rmm_app, rmm_tenant_ok(), политики за всяка таблица, съставни външни ключове по тенант, одитен журнал само за добавяне с purge_audit_log(), помощни функции за дялове със SECURITY DEFINER
0008perf_indexesdevices.search_text (триграмен) и inventory_hashes; индекси за сортиране, състояние и препратки към задачи; rmm_devices_with_software(); pg_stat_statements, когато е наличен
0009login_attemptsброячи на влизанията с фиксиран прозорец, споделени от всички реплики (хеширани ключове, изчистват се след един ден)
0010remote_sessionsсесии на отдалечен работен плот (RLS, съставни ключове по тенант, една отворена сесия на устройство)
0011remote_sessions_bannerremote_sessions.show_banner
0012platform_mirrortenant_memberships (роля за всеки потребител и тенант, предпочитания за аларми за всеки тенант; попълнени от users.tenant_id/role), users.is_global_admin, platform_sync_state, platform_sessions_revoked, platform_step_ups_used; потребителите могат да се четат като глобални редове; имейлът е уникален сред неизтритите потребители; тригер за локално влизане
0013drop_local_authпремахва локалното влизане: таблиците refresh_tokens, password_resets, invitations, login_attempts; колоните на users tenant_id, role, password_hash, totp_*, must_change_password, failed_logins, locked_until, last_login_at, notification_prefs, alert_digest_at; тригера от 0012, който поддържаше users.tenant_id/role и tenant_memberships в синхрон. users.status става active/disabled/deleted (потребителите с invited стават deleted); политики за users: четене за всички, записи само в глобалния обхват. Обратната миграция създава отново таблиците и колоните празни: идентификационните данни не могат да бъдат възстановени, така че връщането назад преди 0013 означава възстановяване от резервно копие, направено преди нея.
0014notification_controlsмаркери в tenant_memberships за групирани имейли за офлайн устройства, имейли за разрешаване и тихи часове (offline_rollup_at, resolved_mailed_at, quiet_held_since); индекс върху разрешените аларми
0015device_logonsвлизания в устройствата (RLS, изтриват се 7 дни след изхода)
0016cpu_temperaturedevice_metrics.cpu_temp_c; тип правило за аларма cpu_temp и глобалното правило CPU temperature high
0017screen_wallРоля teacher в tenant_memberships; remote_sessions.profile ('' или wall, само преглед), room_firewall_id, room

Собствените миграции на river се прилагат от migrate up преди тези. Нови миграции се създават с make migration name=… (Make цели); не забравяйте да ги добавите в тази таблица.

База данни на Entrosity Hub​

Hub пази данните си в собствена база данни, platform, на същия PostgreSQL сървър (собственик rmm, услугата се свързва като platform_app, без защита на ниво ред: всяка заявка се ограничава от собствените правила за достъп на Hub). Миграциите са в entrosity-hub.backend/db/migrations (platform-server migrate); таблиците за задачи на river се създават първи, както в Axis.

ТаблицаСъдържа
usersПо един акаунт в Entrosity за всеки човек: имейл (уникален сред неизтритите потребители), argon2id хеш на паролата, показвано име, състояние (active, disabled, deleted), is_platform_admin, тайна за двуфакторна автентикация (шифрована с PLATFORM_MASTER_KEY), хешове на кодовете за възстановяване, броячи за заключване. Идентификаторите се запазват при импортиране от Axis (import-rmm, което изисква база данни на Axis отпреди миграция 0013).
organizationsКлиенти: име, уникален slug (^[a-z0-9-]{3,40}$), състояние (active, suspended), настройки (require_totp). Тенантите в Axis имат същите идентификатори.
productsПродуктите, в които Hub вписва потребителите: id (rmm), име, базов път (/axis), познатите му роли, дали е включен. Първоначално съдържа Axis; стартовият панел и пренасочването след влизане следват базовия път.
organization_productsКои продукти има дадена организация.
membershipsПотребител в организация, с роля в организацията admin или member.
product_rolesРолята на член в един продукт на организацията (за Axis tenant_admin, technician, viewer, teacher); изисква членството и продукта на организацията, а тригер проверява ролята спрямо списъка на продукта.
refresh_tokensБисквитки за сесия (хеширани), групирани в семейства, които се завъртат; отнето семейство без наследник е приключила сесия (моментната снимка ги изброява за продуктите).
password_resets, email_changes, invitationsЕднократни връзки (хеширани токени) със срок на валидност; поканата дава роля в организация, роли в продукти и/или администратор на платформата.
login_attemptsБроячи на влизанията и на ограничението на честотата на заявките, споделени от репликите.
audit_logВсяка промяна, само за добавяне за platform_app (тригер отказва обновявания и изтривания).

Миграции на Hub:

№файлсъдържание
0001initтаблиците по-горе; роля platform_app; products с първоначален запис за Axis (rmm, базов път /manage)
0002axis_pathбазовият път на Axis става /axis
0003edge_productрегистрира Entrosity Edge (edge, изключен)
0004edge_enableвключва Edge
0005product_betaproducts.beta: продукт в бета виждат само администраторите на платформата
0006sphere_productрегистрира Entrosity Sphere (sphere, изключен, бета)
0007sphere_enableвключва Sphere
0008matrix_productрегистрира Entrosity Matrix (matrix, изключен, бета)
0009matrix_enableвключва Matrix
0010email_changesчакащи смени на имейл (нов адрес, хеширан токен, срок)
0011matrix_teacherролята operator на Matrix става teacher (членове и отворени покани)
0012axis_teacherAxis получава ролята teacher (само стената с екрани)