auto execution implemented and fix issue when uninstalling clients and them being shown in the menu when they connect to purgatory

This commit is contained in:
KingKDot
2026-03-29 17:53:25 -05:00
parent a55c3193e0
commit fd2749dcf0
11 changed files with 884 additions and 1 deletions
+190
View File
@@ -190,6 +190,39 @@ db.run(
`CREATE INDEX IF NOT EXISTS idx_auto_script_runs_ts ON auto_script_runs(ts DESC);`,
);
db.run(`
CREATE TABLE IF NOT EXISTS auto_deploys (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
trigger TEXT NOT NULL,
file_path TEXT NOT NULL,
file_name TEXT NOT NULL,
file_size INTEGER NOT NULL,
file_os TEXT NOT NULL,
args TEXT NOT NULL DEFAULT '',
hide_window INTEGER NOT NULL DEFAULT 1,
enabled INTEGER NOT NULL,
os_filter TEXT NOT NULL DEFAULT '[]',
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
`);
db.run(
`CREATE INDEX IF NOT EXISTS idx_auto_deploys_trigger ON auto_deploys(trigger, enabled);`,
);
db.run(`
CREATE TABLE IF NOT EXISTS auto_deploy_runs (
deploy_id TEXT NOT NULL,
client_id TEXT NOT NULL,
ts INTEGER NOT NULL,
PRIMARY KEY (deploy_id, client_id)
);
`);
db.run(
`CREATE INDEX IF NOT EXISTS idx_auto_deploy_runs_ts ON auto_deploy_runs(ts DESC);`,
);
export function upsertClientRow(
partial: Partial<ClientInfo> & {
id: string;
@@ -218,6 +251,7 @@ export function upsertClientRow(
last_seen=excluded.last_seen,
online=COALESCE(excluded.online, clients.online),
ping_ms=COALESCE(excluded.ping_ms, clients.ping_ms),
enrollment_status=CASE WHEN excluded.enrollment_status <> 'pending' THEN excluded.enrollment_status ELSE clients.enrollment_status END,
public_key=COALESCE(excluded.public_key, clients.public_key),
key_fingerprint=COALESCE(excluded.key_fingerprint, clients.key_fingerprint)
`,
@@ -1052,3 +1086,159 @@ export function getPendingClients(): {
lastSeen: Number(r.lastSeen) || 0,
}));
}
export type AutoDeployTrigger = "on_connect" | "on_first_connect" | "on_connect_once";
export type AutoDeploy = {
id: string;
name: string;
trigger: AutoDeployTrigger;
filePath: string;
fileName: string;
fileSize: number;
fileOs: string;
args: string;
hideWindow: boolean;
enabled: boolean;
osFilter: string[];
createdAt: number;
updatedAt: number;
};
function mapAutoDeployRow(row: any): AutoDeploy {
let osFilter: string[] = [];
try {
const parsed = JSON.parse(row.os_filter || "[]");
osFilter = Array.isArray(parsed) ? parsed : [];
} catch { }
return {
id: row.id,
name: row.name,
trigger: row.trigger as AutoDeployTrigger,
filePath: row.file_path,
fileName: row.file_name,
fileSize: Number(row.file_size) || 0,
fileOs: row.file_os || "unknown",
args: row.args || "",
hideWindow: row.hide_window === 1,
enabled: row.enabled === 1,
osFilter,
createdAt: Number(row.created_at) || 0,
updatedAt: Number(row.updated_at) || 0,
};
}
export function listAutoDeploys(): AutoDeploy[] {
const rows = db.query<any>(`SELECT * FROM auto_deploys ORDER BY created_at DESC`).all();
return rows.map(mapAutoDeployRow);
}
export function getAutoDeploysByTrigger(trigger: AutoDeployTrigger): AutoDeploy[] {
const rows = db
.query<any>(
`SELECT * FROM auto_deploys WHERE trigger=? AND enabled=1 ORDER BY created_at ASC`,
)
.all(trigger);
return rows.map(mapAutoDeployRow);
}
export function getAutoDeploy(id: string): AutoDeploy | null {
const row = db.query<any>(`SELECT * FROM auto_deploys WHERE id=?`).get(id);
return row ? mapAutoDeployRow(row) : null;
}
export function createAutoDeploy(input: {
id: string;
name: string;
trigger: AutoDeployTrigger;
filePath: string;
fileName: string;
fileSize: number;
fileOs: string;
args: string;
hideWindow: boolean;
enabled: boolean;
osFilter: string[];
}): AutoDeploy {
const now = Date.now();
db.run(
`INSERT INTO auto_deploys (id, name, trigger, file_path, file_name, file_size, file_os, args, hide_window, enabled, os_filter, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
input.id,
input.name,
input.trigger,
input.filePath,
input.fileName,
input.fileSize,
input.fileOs,
input.args,
input.hideWindow ? 1 : 0,
input.enabled ? 1 : 0,
JSON.stringify(input.osFilter ?? []),
now,
now,
);
return getAutoDeploy(input.id)!;
}
export function updateAutoDeploy(
id: string,
input: Partial<{
name: string;
trigger: AutoDeployTrigger;
args: string;
hideWindow: boolean;
enabled: boolean;
osFilter: string[];
}>,
): AutoDeploy | null {
const current = getAutoDeploy(id);
if (!current) return null;
const next = {
name: input.name ?? current.name,
trigger: (input.trigger ?? current.trigger) as AutoDeployTrigger,
args: input.args ?? current.args,
hideWindow: typeof input.hideWindow === "boolean" ? input.hideWindow : current.hideWindow,
enabled: typeof input.enabled === "boolean" ? input.enabled : current.enabled,
osFilter: Array.isArray(input.osFilter) ? input.osFilter : current.osFilter,
};
db.run(
`UPDATE auto_deploys SET name=?, trigger=?, args=?, hide_window=?, enabled=?, os_filter=?, updated_at=? WHERE id=?`,
next.name,
next.trigger,
next.args,
next.hideWindow ? 1 : 0,
next.enabled ? 1 : 0,
JSON.stringify(next.osFilter),
Date.now(),
id,
);
return getAutoDeploy(id);
}
export function deleteAutoDeploy(id: string): boolean {
const result = db.run(`DELETE FROM auto_deploys WHERE id=?`, id);
db.run(`DELETE FROM auto_deploy_runs WHERE deploy_id=?`, id);
return (result as any)?.changes ? (result as any).changes > 0 : true;
}
export function hasAutoDeployRun(deployId: string, clientId: string): boolean {
const row = db
.query<any>(
`SELECT deploy_id FROM auto_deploy_runs WHERE deploy_id=? AND client_id=?`,
)
.get(deployId, clientId);
return !!row?.deploy_id;
}
export function recordAutoDeployRun(deployId: string, clientId: string) {
db.run(
`INSERT OR REPLACE INTO auto_deploy_runs (deploy_id, client_id, ts) VALUES (?, ?, ?)`,
deployId,
clientId,
Date.now(),
);
}