feat: Fleet Forge UEM - device management, agent, remote desktop (VNC), TLS reverse proxy

This commit is contained in:
Pepe44DEV
2026-09-06 22:09:28 +00:00
commit db16ad5cc1
154 changed files with 22920 additions and 0 deletions
+234
View File
@@ -0,0 +1,234 @@
CREATE TABLE IF NOT EXISTS "organizations" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"name" varchar(255) NOT NULL,
"slug" varchar(255) NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "organizations_slug_unique" UNIQUE ("slug")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"email" varchar(255) NOT NULL,
"password_hash" varchar(255) NOT NULL,
"display_name" varchar(255) NOT NULL,
"role" varchar(50) DEFAULT 'read_only' NOT NULL,
"is_active" boolean DEFAULT true NOT NULL,
"last_login_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "users_email_org_unique" UNIQUE ("email","organization_id")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "enrollment_tokens" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"token_hash" varchar(255) NOT NULL,
"expires_at" timestamp with time zone NOT NULL,
"max_uses" integer,
"use_count" integer DEFAULT 0 NOT NULL,
"is_revoked" boolean DEFAULT false NOT NULL,
"label" varchar(255),
"created_by_user_id" uuid NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "enrollment_tokens_token_hash_unique" UNIQUE ("token_hash")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "device_groups" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"name" varchar(255) NOT NULL,
"description" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "device_groups_name_org_unique" UNIQUE ("name","organization_id")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "devices" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"device_group_id" uuid,
"name" varchar(255) NOT NULL,
"device_type" varchar(50) NOT NULL,
"platform" varchar(50) NOT NULL,
"os_name" varchar(100) NOT NULL,
"os_version" varchar(100) NOT NULL,
"architecture" varchar(50) NOT NULL,
"kernel_version" varchar(100),
"cpu_model" varchar(255),
"cpu_cores" integer,
"total_memory_bytes" bigint,
"total_disk_bytes" bigint,
"ip_address" varchar(45),
"agent_version" varchar(50) NOT NULL,
"status" varchar(20) DEFAULT 'offline' NOT NULL,
"last_heartbeat_at" timestamp with time zone,
"enrolled_at" timestamp with time zone DEFAULT now() NOT NULL,
"is_disabled" boolean DEFAULT false NOT NULL,
"is_archived" boolean DEFAULT false NOT NULL,
"tags" jsonb DEFAULT '[]' NOT NULL,
"metadata" jsonb DEFAULT '{}' NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "device_credentials" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"device_id" uuid NOT NULL,
"credential_type" varchar(50) NOT NULL,
"credential_hash" varchar(255) NOT NULL,
"expires_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "inventory_snapshots" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"device_id" uuid NOT NULL,
"reported_at" timestamp with time zone NOT NULL,
"packages" jsonb DEFAULT '[]' NOT NULL,
"services" jsonb DEFAULT '[]' NOT NULL,
"network_interfaces" jsonb DEFAULT '[]' NOT NULL,
"platform_data" jsonb DEFAULT '{}' NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "metric_snapshots" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"device_id" uuid NOT NULL,
"reported_at" timestamp with time zone NOT NULL,
"cpu_usage_percent" integer,
"memory_usage_percent" integer,
"memory_used_bytes" bigint,
"memory_total_bytes" bigint,
"disk_usage_percent" integer,
"disk_used_bytes" bigint,
"disk_total_bytes" bigint,
"load_average_1m" integer,
"load_average_5m" integer,
"load_average_15m" integer,
"uptime_seconds" bigint,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "policies" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"name" varchar(255) NOT NULL,
"description" text,
"rules" jsonb DEFAULT '[]' NOT NULL,
"is_enabled" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "compliance_results" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"device_id" uuid NOT NULL,
"policy_id" uuid NOT NULL,
"status" varchar(50) NOT NULL,
"details" jsonb DEFAULT '{}' NOT NULL,
"evaluated_at" timestamp with time zone NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "jobs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"created_by_user_id" uuid NOT NULL,
"job_type" varchar(100) NOT NULL,
"status" varchar(50) DEFAULT 'queued' NOT NULL,
"parameters" jsonb DEFAULT '{}' NOT NULL,
"dispatched_at" timestamp with time zone,
"started_at" timestamp with time zone,
"completed_at" timestamp with time zone,
"expires_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "job_targets" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"job_id" uuid NOT NULL,
"device_id" uuid NOT NULL,
"status" varchar(50) DEFAULT 'queued' NOT NULL,
"result" jsonb,
"started_at" timestamp with time zone,
"completed_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "alerts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"device_id" uuid,
"alert_type" varchar(100) NOT NULL,
"severity" varchar(20) NOT NULL,
"title" varchar(255) NOT NULL,
"message" text NOT NULL,
"is_resolved" boolean DEFAULT false NOT NULL,
"resolved_at" timestamp with time zone,
"resolved_by_user_id" uuid,
"metadata" jsonb DEFAULT '{}' NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "audit_events" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"user_id" uuid,
"action" varchar(255) NOT NULL,
"target_type" varchar(100) NOT NULL,
"target_id" uuid,
"device_ids" jsonb DEFAULT '[]' NOT NULL,
"result" varchar(50) NOT NULL,
"ip_address" varchar(45),
"metadata" jsonb DEFAULT '{}' NOT NULL,
"correlation_id" varchar(255) NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "api_tokens" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" uuid NOT NULL,
"user_id" uuid NOT NULL,
"name" varchar(255) NOT NULL,
"token_hash" varchar(255) NOT NULL,
"expires_at" timestamp with time zone,
"last_used_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "api_tokens_token_hash_unique" UNIQUE ("token_hash")
);
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "idx_users_email_org" ON "users" ("email","organization_id");
CREATE INDEX IF NOT EXISTS "idx_users_organization_id" ON "users" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_enrollment_tokens_org" ON "enrollment_tokens" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_enrollment_tokens_hash" ON "enrollment_tokens" ("token_hash");
CREATE INDEX IF NOT EXISTS "idx_devices_org" ON "devices" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_devices_status" ON "devices" ("status");
CREATE INDEX IF NOT EXISTS "idx_devices_group" ON "devices" ("device_group_id");
CREATE INDEX IF NOT EXISTS "idx_devices_type" ON "devices" ("device_type");
CREATE INDEX IF NOT EXISTS "idx_devices_last_heartbeat" ON "devices" ("last_heartbeat_at");
CREATE INDEX IF NOT EXISTS "idx_device_credentials_device" ON "device_credentials" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_inventory_device" ON "inventory_snapshots" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_inventory_reported_at" ON "inventory_snapshots" ("reported_at");
CREATE INDEX IF NOT EXISTS "idx_metrics_device" ON "metric_snapshots" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_metrics_reported_at" ON "metric_snapshots" ("reported_at");
CREATE INDEX IF NOT EXISTS "idx_policies_org" ON "policies" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_compliance_device" ON "compliance_results" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_compliance_policy" ON "compliance_results" ("policy_id");
CREATE INDEX IF NOT EXISTS "idx_jobs_org" ON "jobs" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_jobs_status" ON "jobs" ("status");
CREATE INDEX IF NOT EXISTS "idx_jobs_type" ON "jobs" ("job_type");
CREATE INDEX IF NOT EXISTS "idx_job_targets_job" ON "job_targets" ("job_id");
CREATE INDEX IF NOT EXISTS "idx_job_targets_device" ON "job_targets" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_job_targets_status" ON "job_targets" ("status");
CREATE INDEX IF NOT EXISTS "idx_alerts_org" ON "alerts" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_alerts_device" ON "alerts" ("device_id");
CREATE INDEX IF NOT EXISTS "idx_alerts_resolved" ON "alerts" ("is_resolved");
CREATE INDEX IF NOT EXISTS "idx_audit_org" ON "audit_events" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_audit_user" ON "audit_events" ("user_id");
CREATE INDEX IF NOT EXISTS "idx_audit_action" ON "audit_events" ("action");
CREATE INDEX IF NOT EXISTS "idx_audit_created_at" ON "audit_events" ("created_at");
CREATE INDEX IF NOT EXISTS "idx_audit_correlation" ON "audit_events" ("correlation_id");
CREATE INDEX IF NOT EXISTS "idx_api_tokens_org" ON "api_tokens" ("organization_id");
CREATE INDEX IF NOT EXISTS "idx_api_tokens_hash" ON "api_tokens" ("token_hash");
@@ -0,0 +1,3 @@
-- Add unique constraint on (device_id, policy_id) for compliance_results upsert
CREATE UNIQUE INDEX IF NOT EXISTS "idx_compliance_device_policy"
ON "compliance_results" ("device_id", "policy_id");
@@ -0,0 +1,68 @@
-- Update Requests: approval workflow for software updates
CREATE TABLE IF NOT EXISTS "update_requests" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" UUID NOT NULL REFERENCES "organizations"("id"),
"device_id" UUID NOT NULL REFERENCES "devices"("id") ON DELETE CASCADE,
"package_name" VARCHAR(255) NOT NULL,
"package_manager" VARCHAR(50) NOT NULL DEFAULT 'apt',
"current_version" VARCHAR(100),
"target_version" VARCHAR(100) NOT NULL,
"requested_by_user_id" UUID NOT NULL REFERENCES "users"("id"),
"status" VARCHAR(50) NOT NULL DEFAULT 'pending',
"approved_by_user_id" UUID REFERENCES "users"("id"),
"approved_at" TIMESTAMPTZ,
"rejection_reason" TEXT,
"deployment_id" UUID,
"notes" TEXT,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS "idx_update_requests_org" ON "update_requests"("organization_id");
CREATE INDEX IF NOT EXISTS "idx_update_requests_device" ON "update_requests"("device_id");
CREATE INDEX IF NOT EXISTS "idx_update_requests_status" ON "update_requests"("status");
-- Deployments: track bulk operations across devices
CREATE TABLE IF NOT EXISTS "deployments" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"organization_id" UUID NOT NULL REFERENCES "organizations"("id"),
"name" VARCHAR(255) NOT NULL,
"description" TEXT,
"job_type" VARCHAR(100) NOT NULL,
"parameters" JSONB NOT NULL DEFAULT '{}',
"status" VARCHAR(50) NOT NULL DEFAULT 'draft',
"created_by_user_id" UUID NOT NULL REFERENCES "users"("id"),
"approved_by_user_id" UUID REFERENCES "users"("id"),
"approved_at" TIMESTAMPTZ,
"scheduled_at" TIMESTAMPTZ,
"started_at" TIMESTAMPTZ,
"completed_at" TIMESTAMPTZ,
"total_targets" INTEGER NOT NULL DEFAULT 0,
"completed_targets" INTEGER NOT NULL DEFAULT 0,
"failed_targets" INTEGER NOT NULL DEFAULT 0,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW(),
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS "idx_deployments_org" ON "deployments"("organization_id");
CREATE INDEX IF NOT EXISTS "idx_deployments_status" ON "deployments"("status");
-- Deployment Targets: per-device tracking within a deployment
CREATE TABLE IF NOT EXISTS "deployment_targets" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"deployment_id" UUID NOT NULL REFERENCES "deployments"("id") ON DELETE CASCADE,
"device_id" UUID NOT NULL REFERENCES "devices"("id") ON DELETE CASCADE,
"job_id" UUID REFERENCES "jobs"("id"),
"status" VARCHAR(50) NOT NULL DEFAULT 'pending',
"result" JSONB,
"started_at" TIMESTAMPTZ,
"completed_at" TIMESTAMPTZ,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS "idx_deployment_targets_deployment" ON "deployment_targets"("deployment_id");
CREATE INDEX IF NOT EXISTS "idx_deployment_targets_device" ON "deployment_targets"("device_id");
CREATE INDEX IF NOT EXISTS "idx_deployment_targets_status" ON "deployment_targets"("status");
-- Link update_requests.deployment_id foreign key
ALTER TABLE "update_requests"
ADD CONSTRAINT "fk_update_requests_deployment"
FOREIGN KEY ("deployment_id") REFERENCES "deployments"("id")
ON DELETE SET NULL;
@@ -0,0 +1,8 @@
-- Convert metric snapshot columns from integer to real for float values from agents
ALTER TABLE metric_snapshots ALTER COLUMN cpu_usage_percent TYPE real;
ALTER TABLE metric_snapshots ALTER COLUMN memory_usage_percent TYPE real;
ALTER TABLE metric_snapshots ALTER COLUMN disk_usage_percent TYPE real;
ALTER TABLE metric_snapshots ALTER COLUMN load_average_1m TYPE real;
ALTER TABLE metric_snapshots ALTER COLUMN load_average_5m TYPE real;
ALTER TABLE metric_snapshots ALTER COLUMN load_average_15m TYPE real;
@@ -0,0 +1,24 @@
-- Software packages table for uploaded installers
CREATE TABLE software_packages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id),
name VARCHAR(255) NOT NULL,
description TEXT,
version VARCHAR(100) NOT NULL,
package_type VARCHAR(50) NOT NULL,
platform VARCHAR(50) NOT NULL,
architecture VARCHAR(50) NOT NULL,
filename VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL,
checksum VARCHAR(255),
install_command TEXT,
tags JSONB NOT NULL DEFAULT '[]',
metadata JSONB NOT NULL DEFAULT '{}',
uploaded_by_user_id UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_software_packages_org ON software_packages(organization_id);
CREATE INDEX idx_software_packages_platform ON software_packages(platform);
CREATE INDEX idx_software_packages_name ON software_packages(name);
@@ -0,0 +1,20 @@
-- Remote desktop session table for VNC tunneled sessions
CREATE TABLE remote_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id),
device_id UUID NOT NULL REFERENCES devices(id),
created_by_user_id UUID NOT NULL REFERENCES users(id),
job_id UUID REFERENCES jobs(id),
status VARCHAR(50) NOT NULL DEFAULT 'pending',
vnc_port INTEGER NOT NULL DEFAULT 5900,
vnc_password VARCHAR(64),
error_message TEXT,
started_at TIMESTAMP WITH TIME ZONE,
ended_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_remote_sessions_org ON remote_sessions(organization_id);
CREATE INDEX idx_remote_sessions_device ON remote_sessions(device_id);
CREATE INDEX idx_remote_sessions_status ON remote_sessions(status);
@@ -0,0 +1,7 @@
-- Allow jobs referenced by a remote session to be deleted/reaped:
-- orphaned-session references are cleared instead of blocking the job removal.
ALTER TABLE remote_sessions
DROP CONSTRAINT remote_sessions_job_id_fkey,
ADD CONSTRAINT remote_sessions_job_id_fkey
FOREIGN KEY (job_id) REFERENCES jobs(id)
ON DELETE SET NULL;