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
+38
View File
@@ -0,0 +1,38 @@
import type { Request } from "express";
import { db } from "../db/index.js";
import { auditEvents } from "../db/schema.js";
import type { AuthenticatedUser } from "@fleetforge/shared";
export async function auditLog(params: {
req?: Request;
userId?: string;
organizationId: string;
action: string;
targetType: string;
targetId?: string;
deviceIds?: string[];
result: string;
metadata?: Record<string, unknown>;
correlationId?: string;
}) {
const correlationId =
params.correlationId ||
(params.req ? ((params.req as any).correlationId as string) : "unknown");
const ipAddress = params.req?.ip || params.req?.socket?.remoteAddress;
const user = params.req
? ((params.req as any).user as AuthenticatedUser)
: undefined;
await db.insert(auditEvents).values({
organizationId: params.organizationId,
userId: params.userId || user?.userId || null,
action: params.action,
targetType: params.targetType,
targetId: params.targetId || null,
deviceIds: params.deviceIds || [],
result: params.result,
ipAddress: ipAddress || null,
metadata: params.metadata || {},
correlationId,
});
}
+492
View File
@@ -0,0 +1,492 @@
import pg from "pg";
interface PolicyRule {
type: string;
condition: string;
value: string | number | boolean;
}
interface DeviceRow {
id: string;
organization_id: string;
os_name: string;
os_version: string;
platform: string;
architecture: string;
agent_version: string;
cpu_cores: number | null;
total_memory_bytes: number | null;
total_disk_bytes: number | null;
status: string;
tags: string[];
kernel_version: string | null;
}
interface MetricRow {
cpu_usage_percent: number | null;
memory_usage_percent: number | null;
disk_usage_percent: number | null;
load_average_1m: number | null;
uptime_seconds: number | null;
}
interface PolicyRow {
id: string;
organization_id: string;
name: string;
rules: PolicyRule[];
}
interface InventoryRow {
packages: { name: string; version: string }[];
services: { name: string; status: string; enabled: boolean }[];
}
function compareVersions(a: string, b: string): number {
const pa = a.split(".").map(Number);
const pb = b.split(".").map(Number);
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const na = pa[i] || 0;
const nb = pb[i] || 0;
if (na > nb) return 1;
if (na < nb) return -1;
}
return 0;
}
function getDeviceField(
device: DeviceRow,
metrics: MetricRow | null,
inventory: InventoryRow | null,
fieldType: string
): unknown {
switch (fieldType) {
case "os_version":
return device.os_version;
case "os_name":
return device.os_name;
case "platform":
return device.platform;
case "architecture":
return device.architecture;
case "agent_version":
return device.agent_version;
case "cpu_cores":
return device.cpu_cores;
case "memory_total":
return device.total_memory_bytes;
case "disk_total":
return device.total_disk_bytes;
case "status":
return device.status;
case "tags":
return device.tags || [];
case "kernel_version":
return device.kernel_version;
case "cpu_usage":
return metrics?.cpu_usage_percent;
case "memory_usage":
return metrics?.memory_usage_percent;
case "disk_usage":
return metrics?.disk_usage_percent;
case "load_average":
return metrics?.load_average_1m;
case "uptime":
return metrics?.uptime_seconds;
case "package_count":
return inventory?.packages?.length ?? 0;
case "has_package":
return inventory?.packages?.map((p) => p.name) ?? [];
case "service_count":
return inventory?.services?.length ?? 0;
case "service_status":
return inventory?.services?.map((s) => ({
name: s.name,
status: s.status,
enabled: s.enabled,
})) ?? [];
default:
return undefined;
}
}
function evaluateRule(
actual: unknown,
condition: string,
expected: string | number | boolean
): boolean {
if (actual === undefined || actual === null) {
return condition === "not_equals" && expected !== null;
}
switch (condition) {
case "equals":
return String(actual) === String(expected);
case "not_equals":
return String(actual) !== String(expected);
case "greater_than":
return Number(actual) > Number(expected);
case "less_than":
return Number(actual) < Number(expected);
case "greater_than_or_equal":
return Number(actual) >= Number(expected);
case "less_than_or_equal":
return Number(actual) <= Number(expected);
case "contains":
if (Array.isArray(actual)) {
return actual.includes(String(expected));
}
return String(actual).includes(String(expected));
case "not_contains":
if (Array.isArray(actual)) {
return !actual.includes(String(expected));
}
return !String(actual).includes(String(expected));
case "in":
if (Array.isArray(expected)) {
return expected.map(String).includes(String(actual));
}
return String(actual) === String(expected);
case "not_in":
if (Array.isArray(expected)) {
return !expected.map(String).includes(String(actual));
}
return String(actual) !== String(expected);
case "version_gte":
return compareVersions(String(actual), String(expected)) >= 0;
case "version_lte":
return compareVersions(String(actual), String(expected)) <= 0;
case "version_gt":
return compareVersions(String(actual), String(expected)) > 0;
case "version_lt":
return compareVersions(String(actual), String(expected)) < 0;
case "is_true":
return actual === true || actual === "true";
case "is_false":
return actual === false || actual === "false";
case "is_empty":
if (Array.isArray(actual)) return actual.length === 0;
if (typeof actual === "string") return actual.length === 0;
return actual === null || actual === undefined;
case "is_not_empty":
if (Array.isArray(actual)) return actual.length > 0;
if (typeof actual === "string") return actual.length > 0;
return actual !== null && actual !== undefined;
default:
return false;
}
}
export async function evaluatePolicies(
pool: pg.Pool,
organizationId: string
): Promise<{ evaluated: number; compliant: number; nonCompliant: number; unknown: number }> {
const client = await pool.connect();
let evaluated = 0;
let compliant = 0;
let nonCompliant = 0;
let unknown = 0;
try {
const { rows: policies } = await client.query<PolicyRow>(
`SELECT id, organization_id, name, rules
FROM policies
WHERE organization_id = $1 AND is_enabled = true`,
[organizationId]
);
if (policies.length === 0) {
return { evaluated: 0, compliant: 0, nonCompliant: 0, unknown: 0 };
}
const { rows: devices } = await client.query<DeviceRow>(
`SELECT id, organization_id, os_name, os_version, platform, architecture,
agent_version, cpu_cores, total_memory_bytes, total_disk_bytes,
status, tags, kernel_version
FROM devices
WHERE organization_id = $1
AND is_disabled = false
AND is_archived = false`,
[organizationId]
);
if (devices.length === 0) {
return { evaluated: 0, compliant: 0, nonCompliant: 0, unknown: 0 };
}
const deviceIds = devices.map((d) => d.id);
const metricsMap = new Map<string, MetricRow>();
const inventoryMap = new Map<string, InventoryRow>();
if (deviceIds.length > 0) {
const { rows: metricsRows } = await client.query(
`SELECT DISTINCT ON (device_id)
device_id, cpu_usage_percent, memory_usage_percent,
disk_usage_percent, load_average_1m, uptime_seconds
FROM metric_snapshots
WHERE device_id = ANY($1)
ORDER BY device_id, reported_at DESC`,
[deviceIds]
);
for (const row of metricsRows) {
metricsMap.set(row.device_id, row);
}
const { rows: inventoryRows } = await client.query(
`SELECT DISTINCT ON (device_id)
device_id, packages, services
FROM inventory_snapshots
WHERE device_id = ANY($1)
ORDER BY device_id, reported_at DESC`,
[deviceIds]
);
for (const row of inventoryRows) {
inventoryMap.set(row.device_id, row);
}
}
const now = new Date();
const upserts: Array<{
deviceId: string;
policyId: string;
status: string;
details: Record<string, unknown>;
}> = [];
for (const device of devices) {
const metrics = metricsMap.get(device.id) || null;
const inventory = inventoryMap.get(device.id) || null;
for (const policy of policies) {
const rules = (policy.rules || []) as PolicyRule[];
if (rules.length === 0) {
upserts.push({
deviceId: device.id,
policyId: policy.id,
status: "compliant",
details: { message: "No rules defined, defaulting to compliant" },
});
compliant++;
evaluated++;
continue;
}
let allPassed = true;
let hasUnknown = false;
const ruleResults: Record<string, unknown> = {};
for (const rule of rules) {
const actual = getDeviceField(device, metrics, inventory, rule.type);
if (actual === undefined) {
ruleResults[rule.type] = {
condition: rule.condition,
expected: rule.value,
actual: null,
passed: false,
reason: "field_not_found",
};
hasUnknown = true;
allPassed = false;
continue;
}
const passed = evaluateRule(actual, rule.condition, rule.value);
ruleResults[rule.type] = {
condition: rule.condition,
expected: rule.value,
actual,
passed,
};
if (!passed) {
allPassed = false;
}
}
let status: string;
if (hasUnknown && !allPassed) {
status = "unknown";
unknown++;
} else if (allPassed) {
status = "compliant";
compliant++;
} else {
status = "non_compliant";
nonCompliant++;
}
upserts.push({
deviceId: device.id,
policyId: policy.id,
status,
details: ruleResults,
});
evaluated++;
}
}
for (const u of upserts) {
await client.query(
`INSERT INTO compliance_results (device_id, policy_id, status, details, evaluated_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (device_id, policy_id) DO UPDATE SET
status = EXCLUDED.status,
details = EXCLUDED.details,
evaluated_at = EXCLUDED.evaluated_at`,
[u.deviceId, u.policyId, u.status, JSON.stringify(u.details), now]
);
}
return { evaluated, compliant, nonCompliant, unknown };
} finally {
client.release();
}
}
export async function evaluatePoliciesForDevice(
pool: pg.Pool,
deviceId: string,
organizationId: string
): Promise<{ evaluated: number; compliant: number; nonCompliant: number; unknown: number }> {
const client = await pool.connect();
let evaluated = 0;
let compliant = 0;
let nonCompliant = 0;
let unknown = 0;
try {
const { rows: devices } = await client.query<DeviceRow>(
`SELECT id, organization_id, os_name, os_version, platform, architecture,
agent_version, cpu_cores, total_memory_bytes, total_disk_bytes,
status, tags, kernel_version
FROM devices
WHERE id = $1 AND organization_id = $2`,
[deviceId, organizationId]
);
if (devices.length === 0) {
return { evaluated: 0, compliant: 0, nonCompliant: 0, unknown: 0 };
}
const device = devices[0];
const { rows: policies } = await client.query<PolicyRow>(
`SELECT id, organization_id, name, rules
FROM policies
WHERE organization_id = $1 AND is_enabled = true`,
[organizationId]
);
if (policies.length === 0) {
return { evaluated: 0, compliant: 0, nonCompliant: 0, unknown: 0 };
}
const { rows: metricsRows } = await client.query(
`SELECT cpu_usage_percent, memory_usage_percent,
disk_usage_percent, load_average_1m, uptime_seconds
FROM metric_snapshots
WHERE device_id = $1
ORDER BY reported_at DESC
LIMIT 1`,
[deviceId]
);
const metrics: MetricRow | null = metricsRows[0] || null;
const { rows: inventoryRows } = await client.query(
`SELECT packages, services
FROM inventory_snapshots
WHERE device_id = $1
ORDER BY reported_at DESC
LIMIT 1`,
[deviceId]
);
const inventory: InventoryRow | null = inventoryRows[0] || null;
const now = new Date();
for (const policy of policies) {
const rules = (policy.rules || []) as PolicyRule[];
if (rules.length === 0) {
await client.query(
`INSERT INTO compliance_results (device_id, policy_id, status, details, evaluated_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (device_id, policy_id) DO UPDATE SET
status = EXCLUDED.status, details = EXCLUDED.details, evaluated_at = EXCLUDED.evaluated_at`,
[deviceId, policy.id, "compliant", JSON.stringify({ message: "No rules defined" }), now]
);
compliant++;
evaluated++;
continue;
}
let allPassed = true;
let hasUnknown = false;
const ruleResults: Record<string, unknown> = {};
for (const rule of rules) {
const actual = getDeviceField(device, metrics, inventory, rule.type);
if (actual === undefined) {
ruleResults[rule.type] = {
condition: rule.condition,
expected: rule.value,
actual: null,
passed: false,
reason: "field_not_found",
};
hasUnknown = true;
allPassed = false;
continue;
}
const passed = evaluateRule(actual, rule.condition, rule.value);
ruleResults[rule.type] = {
condition: rule.condition,
expected: rule.value,
actual,
passed,
};
if (!passed) {
allPassed = false;
}
}
let status: string;
if (hasUnknown && !allPassed) {
status = "unknown";
unknown++;
} else if (allPassed) {
status = "compliant";
compliant++;
} else {
status = "non_compliant";
nonCompliant++;
}
await client.query(
`INSERT INTO compliance_results (device_id, policy_id, status, details, evaluated_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (device_id, policy_id) DO UPDATE SET
status = EXCLUDED.status, details = EXCLUDED.details, evaluated_at = EXCLUDED.evaluated_at`,
[deviceId, policy.id, status, JSON.stringify(ruleResults), now]
);
evaluated++;
}
return { evaluated, compliant, nonCompliant, unknown };
} finally {
client.release();
}
}
+68
View File
@@ -0,0 +1,68 @@
type LogLevel = "debug" | "info" | "warn" | "error";
interface LogEntry {
timestamp: string;
level: LogLevel;
message: string;
correlationId?: string;
context?: Record<string, unknown>;
error?: {
name: string;
message: string;
stack?: string;
};
}
function formatEntry(entry: LogEntry): string {
return JSON.stringify(entry);
}
function createLogger(defaultContext?: Record<string, unknown>) {
const log = (level: LogLevel, message: string, context?: Record<string, unknown>, correlationId?: string, error?: Error) => {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
...((defaultContext || context) && { context: { ...defaultContext, ...context } }),
};
if (correlationId) entry.correlationId = correlationId;
if (error) {
entry.error = {
name: error.name,
message: error.message,
stack: error.stack,
};
}
const line = formatEntry(entry);
switch (level) {
case "error":
console.error(line);
break;
case "warn":
console.warn(line);
break;
default:
console.log(line);
}
};
return {
debug: (message: string, context?: Record<string, unknown>, correlationId?: string) =>
log("debug", message, context, correlationId),
info: (message: string, context?: Record<string, unknown>, correlationId?: string) =>
log("info", message, context, correlationId),
warn: (message: string, context?: Record<string, unknown>, correlationId?: string) =>
log("warn", message, context, correlationId),
error: (message: string, context?: Record<string, unknown>, correlationId?: string, error?: Error) =>
log("error", message, context, correlationId, error),
child: (childContext: Record<string, unknown>) =>
createLogger({ ...defaultContext, ...childContext }),
};
}
export const logger = createLogger({ service: "fleetforge-api" });
export const workerLogger = createLogger({ service: "fleetforge-worker" });
export { createLogger };
+17
View File
@@ -0,0 +1,17 @@
import crypto from "crypto";
export function hashToken(token: string): string {
return crypto.createHash("sha256").update(token).digest("hex");
}
export function generateEnrollmentToken(): string {
return `ff-${crypto.randomBytes(32).toString("hex")}`;
}
export function generateDeviceCredential(): string {
return `fc-${crypto.randomBytes(32).toString("hex")}`;
}
export function hashCredential(credential: string): string {
return crypto.createHash("sha256").update(credential).digest("hex");
}