194 lines
7.7 KiB
Markdown
194 lines
7.7 KiB
Markdown
|
|
# IT Glue Data Sync
|
|||
|
|
|
|||
|
|
Full backup of all IT Glue data into local PostgreSQL tables prefixed `itg_`.
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
The IT Glue sync pulls all data from the IT Glue REST API and upserts it into Postgres. This gives Pulse a local, queryable copy of all IT documentation for use in pipelines, AI context, reporting, and cross-referencing with RMM/PSA data.
|
|||
|
|
|
|||
|
|
- **40,000+ records** synced across 23 entity types
|
|||
|
|
- Full sync takes ~7–10 minutes
|
|||
|
|
- All tables use `ON CONFLICT DO UPDATE` — fully idempotent
|
|||
|
|
- Sync history tracked in `itg_sync_history`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Environment Variables
|
|||
|
|
|
|||
|
|
| Variable | Description |
|
|||
|
|
|---|---|
|
|||
|
|
| `ITGLUE_API_KEY` | IT Glue API key (from IT Glue → Account → API Keys) |
|
|||
|
|
|
|||
|
|
Set in `/opt/stacks/pulse/.env` and `docker-compose.yml` under the `app` service environment.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API Details
|
|||
|
|
|
|||
|
|
- **Base URL:** `https://api.itglue.com`
|
|||
|
|
- **Auth:** `x-api-key: <key>` header
|
|||
|
|
- **Content-Type:** `application/vnd.api+json` (JSON:API format)
|
|||
|
|
- **Pagination:** `page[size]` + `page[number]`, `meta.total-pages` for total
|
|||
|
|
- **Attribute keys:** hyphenated (`organization-type-id`, `created-at`, etc.)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Files
|
|||
|
|
|
|||
|
|
| File | Purpose |
|
|||
|
|
|---|---|
|
|||
|
|
| `lib/services/itglue-client.ts` | IT Glue API client — typed methods + `getRaw`/`getRawAllPages` for sync |
|
|||
|
|
| `lib/services/itglue-sync-service.ts` | Full sync service — iterates all entities, upserts to Postgres |
|
|||
|
|
| `app/api/itglue/status/route.ts` | `GET /api/itglue/status` — connection test |
|
|||
|
|
| `app/api/itglue/sync/route.ts` | `POST /api/itglue/sync` — trigger sync; `GET` — status + history + counts |
|
|||
|
|
| `app/admin/sync/itglue/page.tsx` | Admin UI page for IT Glue sync |
|
|||
|
|
| `migrations/037_create_itglue_tables.sql` | Creates all `itg_*` tables |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Database Tables
|
|||
|
|
|
|||
|
|
### Reference / Lookup Tables
|
|||
|
|
| Table | Description |
|
|||
|
|
|---|---|
|
|||
|
|
| `itg_organization_types` | Org type definitions |
|
|||
|
|
| `itg_organization_statuses` | Org status definitions |
|
|||
|
|
| `itg_configuration_types` | Config type definitions (Server, Workstation, etc.) |
|
|||
|
|
| `itg_configuration_statuses` | Config status definitions (Active, Inactive, etc.) |
|
|||
|
|
| `itg_contact_types` | Contact type definitions |
|
|||
|
|
| `itg_password_categories` | Password category definitions |
|
|||
|
|
| `itg_manufacturers` | Hardware manufacturers |
|
|||
|
|
| `itg_models` | Hardware models (linked to manufacturer) |
|
|||
|
|
| `itg_operating_systems` | OS definitions |
|
|||
|
|
| `itg_platforms` | Platform definitions |
|
|||
|
|
| `itg_countries` | Country list with ISO codes |
|
|||
|
|
|
|||
|
|
### Core Tables
|
|||
|
|
| Table | Key Columns | Notes |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `itg_organizations` | `id`, `name`, `short_name`, `organization_type_id/name`, `organization_status_id/name`, `psa_integration`, `psa_id`, `parent_id` | 330 orgs |
|
|||
|
|
| `itg_locations` | `id`, `organization_id`, `name`, `primary_location`, `address_*`, `city`, `region_name`, `postal_code`, `country_name`, `phone` | 751 locations |
|
|||
|
|
| `itg_contacts` | `id`, `organization_id`, `first_name`, `last_name`, `title`, `contact_type_id/name`, `location_id`, `emails` (JSONB), `phones` (JSONB) | 7,113 contacts |
|
|||
|
|
| `itg_configurations` | `id`, `organization_id`, `name`, `hostname`, `primary_ip`, `mac_address`, `serial_number`, `asset_tag`, `configuration_type_id/name`, `configuration_status_id/name`, `manufacturer_id/name`, `model_id/name`, `operating_system_id/name`, `rmm_id`, `rmm_integration_type` | 14,712 configs |
|
|||
|
|
| `itg_flexible_asset_types` | `id`, `name`, `description`, `icon`, `enabled`, `builtin` | 41 types |
|
|||
|
|
| `itg_flexible_asset_fields` | `id`, `flexible_asset_type_id`, `name`, `kind`, `required`, `use_for_title` | 1,140 fields |
|
|||
|
|
| `itg_flexible_assets` | `id`, `organization_id`, `flexible_asset_type_id/name`, `name`, `traits` (JSONB), `archived` | 3,161 assets |
|
|||
|
|
| `itg_password_folders` | `id`, `organization_id`, `name`, `inherited` | Per-org |
|
|||
|
|
| `itg_passwords` | `id`, `organization_id`, `name`, `username`, `password`, `url`, `password_category_id/name`, `password_folder_id`, `otp_enabled`, `archived` | 270 passwords |
|
|||
|
|
| `itg_documents` | `id`, `organization_id`, `name`, `content`, `draft`, `archived` | 537 documents |
|
|||
|
|
| `itg_domains` | `id`, `organization_id`, `name`, `expires_at`, `registrar_name`, `whois_updated_at` | 212 domains |
|
|||
|
|
| `itg_expirations` | `id`, `organization_id`, `resource_id`, `resource_type`, `resource_name`, `expiration_type`, `expiration_date`, `notify` | 9,770 expirations |
|
|||
|
|
| `itg_sync_history` | `id`, `sync_type`, `status`, `triggered_by`, `started_at`, `completed_at`, `duration_ms`, `entities` (JSONB), `total_upserted` | Sync audit log |
|
|||
|
|
|
|||
|
|
All tables include `synced_at TIMESTAMPTZ` updated on every upsert.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API Quirks & Workarounds
|
|||
|
|
|
|||
|
|
### Flexible Assets — require per-type filter
|
|||
|
|
The `/flexible_assets` endpoint **requires** `filter[flexible-asset-type-id]`. Without it, the API returns 422. The sync iterates over all 41 flexible asset types and fetches assets per type.
|
|||
|
|
|
|||
|
|
### Password Folders, Documents, Expirations — no flat endpoint
|
|||
|
|
These endpoints only exist as nested routes:
|
|||
|
|
- `/organizations/:id/relationships/password_folders`
|
|||
|
|
- `/organizations/:id/relationships/documents`
|
|||
|
|
- `/organizations/:id/relationships/expirations`
|
|||
|
|
|
|||
|
|
The sync iterates over all 330 organizations for each of these.
|
|||
|
|
|
|||
|
|
### Configuration Interfaces — skipped
|
|||
|
|
`/configuration_interfaces` has no flat endpoint. Per-config calls across 14,712 configurations would require 14,712+ API requests and take hours. This entity is intentionally excluded from the sync.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Triggering a Sync
|
|||
|
|
|
|||
|
|
### Via Admin UI
|
|||
|
|
Navigate to **Admin → IT Glue Sync** (or **Admin → Integrations & Sync → IT Glue**) and click **Full Sync**.
|
|||
|
|
|
|||
|
|
### Via API
|
|||
|
|
```bash
|
|||
|
|
# Trigger sync
|
|||
|
|
curl -X POST http://localhost:3100/api/itglue/sync \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"triggeredBy": "manual"}'
|
|||
|
|
|
|||
|
|
# Check status / history
|
|||
|
|
curl http://localhost:3100/api/itglue/sync
|
|||
|
|
|
|||
|
|
# Test connection
|
|||
|
|
curl http://localhost:3100/api/itglue/status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Querying the Data
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- All organizations
|
|||
|
|
SELECT id, name, organization_type_name, organization_status_name FROM itg_organizations;
|
|||
|
|
|
|||
|
|
-- Configurations for a specific org
|
|||
|
|
SELECT name, hostname, primary_ip, configuration_type_name, configuration_status_name
|
|||
|
|
FROM itg_configurations
|
|||
|
|
WHERE organization_name ILIKE '%acme%';
|
|||
|
|
|
|||
|
|
-- Flexible assets by type
|
|||
|
|
SELECT o.name AS org, fa.name, fa.traits
|
|||
|
|
FROM itg_flexible_assets fa
|
|||
|
|
JOIN itg_organizations o ON o.id = fa.organization_id
|
|||
|
|
WHERE fa.flexible_asset_type_name = 'Servers';
|
|||
|
|
|
|||
|
|
-- Expiring domains (next 90 days)
|
|||
|
|
SELECT organization_name, name, expires_at, registrar_name
|
|||
|
|
FROM itg_domains
|
|||
|
|
WHERE expires_at BETWEEN NOW() AND NOW() + INTERVAL '90 days'
|
|||
|
|
ORDER BY expires_at;
|
|||
|
|
|
|||
|
|
-- Upcoming expirations
|
|||
|
|
SELECT organization_name, resource_name, resource_type, expiration_type, expiration_date
|
|||
|
|
FROM itg_expirations
|
|||
|
|
WHERE expiration_date > NOW()
|
|||
|
|
ORDER BY expiration_date
|
|||
|
|
LIMIT 50;
|
|||
|
|
|
|||
|
|
-- Last sync summary
|
|||
|
|
SELECT status, total_upserted, duration_ms,
|
|||
|
|
started_at, completed_at
|
|||
|
|
FROM itg_sync_history
|
|||
|
|
ORDER BY started_at DESC
|
|||
|
|
LIMIT 5;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Sync Performance (Initial Run)
|
|||
|
|
|
|||
|
|
| Entity | Records | Notes |
|
|||
|
|
|---|---|---|
|
|||
|
|
| organization_types | 10 | |
|
|||
|
|
| organization_statuses | 2 | |
|
|||
|
|
| configuration_types | 51 | |
|
|||
|
|
| configuration_statuses | 2 | |
|
|||
|
|
| contact_types | 9 | |
|
|||
|
|
| password_categories | 11 | |
|
|||
|
|
| manufacturers | 183 | |
|
|||
|
|
| operating_systems | 365 | |
|
|||
|
|
| platforms | 22 | |
|
|||
|
|
| countries | 243 | |
|
|||
|
|
| models | 1,803 | Per-manufacturer iteration |
|
|||
|
|
| flexible_asset_types | 41 | |
|
|||
|
|
| flexible_asset_fields | 1,140 | Per-type iteration |
|
|||
|
|
| organizations | 330 | |
|
|||
|
|
| locations | 751 | |
|
|||
|
|
| contacts | 7,113 | |
|
|||
|
|
| configurations | 14,712 | Largest entity |
|
|||
|
|
| flexible_assets | 3,161 | Per-type iteration (41 API calls) |
|
|||
|
|
| password_folders | 1 | Per-org iteration |
|
|||
|
|
| passwords | 270 | |
|
|||
|
|
| documents | 537 | Per-org iteration |
|
|||
|
|
| domains | 212 | |
|
|||
|
|
| expirations | 9,770 | Per-org iteration |
|
|||
|
|
| **Total** | **40,739** | ~7.7 minutes |
|