Files
AgentCompany/src/main/database/social.ts
T
2026-04-05 20:14:48 -04:00

176 lines
8.4 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
import type { BrowserActionRecord, SocialAccountRecord } from "@shared/types";
import type { ReadAll, SqliteRow, Transaction } from "./helpers";
import { ensureId, nowIso, toBoolean } from "./helpers";
export function buildSocialMethods(
getDb: () => DatabaseSync,
readAll: ReadAll,
transaction: Transaction,
) {
return {
saveSocialAccount(input: {
id?: string | null;
companyId: string;
platform: string;
accountName: string;
displayName?: string;
profileUrl?: string;
credentialSecretId?: string | null;
status?: string;
requireApproval?: boolean;
metadataJson?: string;
}): string {
const id = ensureId(input.id);
const now = nowIso();
getDb().prepare(`INSERT INTO social_accounts (id, company_id, platform, account_name, display_name, profile_url, credential_secret_id, status, require_approval, metadata_json, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
platform = excluded.platform,
account_name = excluded.account_name,
display_name = excluded.display_name,
profile_url = excluded.profile_url,
credential_secret_id = excluded.credential_secret_id,
status = excluded.status,
require_approval = excluded.require_approval,
metadata_json = excluded.metadata_json,
updated_at = excluded.updated_at`).run(
id, input.companyId, input.platform, input.accountName,
input.displayName ?? "", input.profileUrl ?? "",
input.credentialSecretId ?? null, input.status ?? "login_required",
input.requireApproval !== false ? 1 : 0,
input.metadataJson ?? "{}", now, now,
);
return id;
},
deleteSocialAccount(id: string, companyId: string) {
transaction(() => {
getDb().prepare("DELETE FROM social_accounts WHERE id = ? AND company_id = ?").run(id, companyId);
getDb().prepare("DELETE FROM browser_actions WHERE social_account_id = ? AND company_id = ?").run(id, companyId);
});
},
listSocialAccounts(companyId: string): SocialAccountRecord[] {
const rows = getDb().prepare("SELECT * FROM social_accounts WHERE company_id = ? ORDER BY created_at DESC").all(companyId) as SqliteRow[];
return rows.map((r) => ({
id: r.id, companyId: r.company_id, platform: r.platform,
accountName: r.account_name, displayName: r.display_name,
profileUrl: r.profile_url, credentialSecretId: r.credential_secret_id,
status: r.status, requireApproval: toBoolean(r.require_approval),
metadataJson: r.metadata_json, lastUsedAt: r.last_used_at,
createdAt: r.created_at, updatedAt: r.updated_at,
}));
},
getSocialAccount(id: string, companyId: string): SocialAccountRecord | null {
const row = getDb()
.prepare("SELECT * FROM social_accounts WHERE id = ? AND company_id = ?")
.get(id, companyId) as SqliteRow | undefined;
if (!row) return null;
return {
id: row.id, companyId: row.company_id, platform: row.platform,
accountName: row.account_name, displayName: row.display_name,
profileUrl: row.profile_url, credentialSecretId: row.credential_secret_id,
status: row.status, requireApproval: toBoolean(row.require_approval),
metadataJson: row.metadata_json, lastUsedAt: row.last_used_at,
createdAt: row.created_at, updatedAt: row.updated_at,
};
},
saveBrowserAction(input: {
companyId: string;
socialAccountId: string;
agentId: string;
taskId?: string | null;
actionType: string;
payloadJson: string;
status?: string;
approvalId?: string | null;
}): string {
const id = randomUUID();
const now = nowIso();
getDb().prepare(`INSERT INTO browser_actions (id, company_id, social_account_id, agent_id, task_id, action_type, payload_json, status, approval_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
id, input.companyId, input.socialAccountId, input.agentId,
input.taskId ?? null, input.actionType, input.payloadJson,
input.status ?? "queued", input.approvalId ?? null, now, now,
);
return id;
},
updateBrowserAction(id: string, updates: { status?: string; resultSummary?: string; screenshotPath?: string | null; errorMessage?: string | null; startedAt?: string; finishedAt?: string }) {
const now = nowIso();
const sets: string[] = ["updated_at = ?"];
const values: SQLInputValue[] = [now];
if (updates.status) { sets.push("status = ?"); values.push(updates.status); }
if (updates.resultSummary !== undefined) { sets.push("result_summary = ?"); values.push(updates.resultSummary); }
if (updates.screenshotPath !== undefined) { sets.push("screenshot_path = ?"); values.push(updates.screenshotPath); }
if (updates.errorMessage !== undefined) { sets.push("error_message = ?"); values.push(updates.errorMessage); }
if (updates.startedAt) { sets.push("started_at = ?"); values.push(updates.startedAt); }
if (updates.finishedAt) { sets.push("finished_at = ?"); values.push(updates.finishedAt); }
values.push(id);
getDb().prepare(`UPDATE browser_actions SET ${sets.join(", ")} WHERE id = ?`).run(...values);
},
listBrowserActions(companyId: string, filters?: { socialAccountId?: string; status?: string; limit?: number }): BrowserActionRecord[] {
let sql = "SELECT * FROM browser_actions WHERE company_id = ?";
const params: SQLInputValue[] = [companyId];
if (filters?.socialAccountId) { sql += " AND social_account_id = ?"; params.push(filters.socialAccountId); }
if (filters?.status) {
const statuses = filters.status.split(",").filter(Boolean);
if (statuses.length > 0) {
sql += ` AND status IN (${statuses.map(() => "?").join(",")})`;
params.push(...statuses);
}
}
sql += " ORDER BY created_at DESC";
if (filters?.limit) { sql += " LIMIT ?"; params.push(filters.limit); }
const rows = getDb().prepare(sql).all(...params) as SqliteRow[];
return rows.map((r) => ({
id: r.id, companyId: r.company_id, socialAccountId: r.social_account_id,
agentId: r.agent_id, taskId: r.task_id, status: r.status,
actionType: r.action_type, payloadJson: r.payload_json,
resultSummary: r.result_summary, screenshotPath: r.screenshot_path,
approvalId: r.approval_id, errorMessage: r.error_message,
startedAt: r.started_at, finishedAt: r.finished_at,
createdAt: r.created_at, updatedAt: r.updated_at,
}));
},
getBrowserAction(id: string, companyId: string): BrowserActionRecord | null {
const row = getDb()
.prepare("SELECT * FROM browser_actions WHERE id = ? AND company_id = ?")
.get(id, companyId) as SqliteRow | undefined;
if (!row) return null;
return {
id: row.id, companyId: row.company_id, socialAccountId: row.social_account_id,
agentId: row.agent_id, taskId: row.task_id, status: row.status,
actionType: row.action_type, payloadJson: row.payload_json,
resultSummary: row.result_summary, screenshotPath: row.screenshot_path,
approvalId: row.approval_id, errorMessage: row.error_message,
startedAt: row.started_at, finishedAt: row.finished_at,
createdAt: row.created_at, updatedAt: row.updated_at,
};
},
getBrowserActionByApprovalId(approvalId: string, companyId: string): BrowserActionRecord | null {
const row = getDb()
.prepare("SELECT * FROM browser_actions WHERE approval_id = ? AND company_id = ? ORDER BY created_at DESC LIMIT 1")
.get(approvalId, companyId) as SqliteRow | undefined;
if (!row) return null;
return {
id: row.id, companyId: row.company_id, socialAccountId: row.social_account_id,
agentId: row.agent_id, taskId: row.task_id, status: row.status,
actionType: row.action_type, payloadJson: row.payload_json,
resultSummary: row.result_summary, screenshotPath: row.screenshot_path,
approvalId: row.approval_id, errorMessage: row.error_message,
startedAt: row.started_at, finishedAt: row.finished_at,
createdAt: row.created_at, updatedAt: row.updated_at,
};
},
};
}