Refactor Gmail Agent (#5439)

This commit is contained in:
Timothy Carambat 2026-04-14 14:46:54 -07:00 committed by GitHub
parent f17337fb97
commit 5aae72a5e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 155 additions and 68 deletions

View File

@ -0,0 +1,22 @@
import { API_BASE } from "@/utils/constants";
import { baseHeaders } from "@/utils/request";
const GmailAgent = {
/**
* Get the current configuration status for Gmail.
* @returns {Promise<{success: boolean, isConfigured?: boolean, config?: {deploymentId: string, apiKey: string}, error?: string}>}
*/
getStatus: async () => {
return await fetch(`${API_BASE}/admin/agent-skills/gmail/status`, {
method: "GET",
headers: baseHeaders(),
})
.then((res) => res.json())
.catch((e) => {
console.error(e);
return { success: false, error: e.message };
});
},
};
export default GmailAgent;

View File

@ -19,6 +19,7 @@ import {
import GMailIcon from "./gmail.png"; import GMailIcon from "./gmail.png";
import Admin from "@/models/admin"; import Admin from "@/models/admin";
import System from "@/models/system"; import System from "@/models/system";
import GmailAgent from "@/models/gmailAgent";
import { getGmailSkills, filterSkillCategories } from "./utils"; import { getGmailSkills, filterSkillCategories } from "./utils";
import { Tooltip } from "react-tooltip"; import { Tooltip } from "react-tooltip";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -46,22 +47,21 @@ export default function GMailSkillPanel({
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
Promise.all([ Promise.all([
Admin.systemPreferencesByFields([ Admin.systemPreferencesByFields(["disabled_gmail_skills"]),
"disabled_gmail_skills",
"gmail_deployment_id",
"gmail_api_key",
]),
System.keys(), System.keys(),
GmailAgent.getStatus(),
]) ])
.then(([prefsRes, settingsRes]) => { .then(([prefsRes, settingsRes, statusRes]) => {
const loadedDeploymentId =
prefsRes?.settings?.gmail_deployment_id ?? "";
const loadedApiKey = prefsRes?.settings?.gmail_api_key ?? "";
setDisabledSkills(prefsRes?.settings?.disabled_gmail_skills ?? []); setDisabledSkills(prefsRes?.settings?.disabled_gmail_skills ?? []);
setIsMultiUserMode(settingsRes?.MultiUserMode ?? false);
if (statusRes?.success && statusRes.config) {
const loadedDeploymentId = statusRes.config.deploymentId || "";
const loadedApiKey = statusRes.config.apiKey || "";
setDeploymentId(loadedDeploymentId); setDeploymentId(loadedDeploymentId);
setApiKey(loadedApiKey); setApiKey(loadedApiKey);
setIsMultiUserMode(settingsRes?.MultiUserMode ?? false);
setConfigDefaultExpanded(!(loadedDeploymentId && loadedApiKey)); setConfigDefaultExpanded(!(loadedDeploymentId && loadedApiKey));
}
}) })
.catch(() => { .catch(() => {
setDisabledSkills([]); setDisabledSkills([]);
@ -73,15 +73,16 @@ export default function GMailSkillPanel({
useEffect(() => { useEffect(() => {
if (prevHasChanges.current === true && hasChanges === false) { if (prevHasChanges.current === true && hasChanges === false) {
Admin.systemPreferencesByFields([ Promise.all([
"disabled_gmail_skills", Admin.systemPreferencesByFields(["disabled_gmail_skills"]),
"gmail_deployment_id", GmailAgent.getStatus(),
"gmail_api_key",
]) ])
.then((res) => { .then(([prefsRes, statusRes]) => {
setDisabledSkills(res?.settings?.disabled_gmail_skills ?? []); setDisabledSkills(prefsRes?.settings?.disabled_gmail_skills ?? []);
setDeploymentId(res?.settings?.gmail_deployment_id ?? ""); if (statusRes?.success && statusRes.config) {
setApiKey(res?.settings?.gmail_api_key ?? ""); setDeploymentId(statusRes.config.deploymentId || "");
setApiKey(statusRes.config.apiKey || "");
}
}) })
.catch(() => {}); .catch(() => {});
} }
@ -425,6 +426,11 @@ function SkillRow({ skill, disabled, onToggle }) {
} }
function HiddenFormInputs({ disabledSkills, deploymentId, apiKey }) { function HiddenFormInputs({ disabledSkills, deploymentId, apiKey }) {
const configJson = JSON.stringify({
deploymentId: deploymentId || "",
apiKey: apiKey || "",
});
return ( return (
<> <>
<input <input
@ -433,11 +439,10 @@ function HiddenFormInputs({ disabledSkills, deploymentId, apiKey }) {
value={disabledSkills.join(",")} value={disabledSkills.join(",")}
/> />
<input <input
name="system::gmail_deployment_id" name="system::gmail_agent_config"
type="hidden" type="hidden"
value={deploymentId} value={configJson}
/> />
<input name="system::gmail_api_key" type="hidden" value={apiKey} />
</> </>
); );
} }

View File

@ -415,12 +415,6 @@ function adminEndpoints(app) {
case "disabled_outlook_skills": case "disabled_outlook_skills":
requestedSettings[label] = safeJsonParse(setting?.value, []); requestedSettings[label] = safeJsonParse(setting?.value, []);
break; break;
case "gmail_deployment_id":
requestedSettings[label] = setting?.value || null;
break;
case "gmail_api_key":
requestedSettings[label] = setting?.value || null;
break;
case "imported_agent_skills": case "imported_agent_skills":
requestedSettings[label] = ImportedPlugin.listImportedPlugins(); requestedSettings[label] = ImportedPlugin.listImportedPlugins();
break; break;

View File

@ -0,0 +1,39 @@
const {
isSingleUserMode,
} = require("../../utils/middleware/multiUserProtected");
const { validatedRequest } = require("../../utils/middleware/validatedRequest");
const { GmailBridge } = require("../../utils/agents/aibitat/plugins/gmail/lib");
function gmailAgentEndpoints(app) {
if (!app) return;
app.get(
"/admin/agent-skills/gmail/status",
[validatedRequest, isSingleUserMode],
async (_request, response) => {
try {
const config = await GmailBridge.getConfig();
const hasDeploymentId = !!config.deploymentId;
const hasApiKey = !!config.apiKey;
const isConfigured = hasDeploymentId && hasApiKey;
const safeConfig = {
deploymentId: config.deploymentId || "",
apiKey: hasApiKey ? "********" : "",
};
return response.status(200).json({
success: true,
isConfigured,
config: safeConfig,
});
} catch (e) {
console.error("Gmail status error:", e);
response.status(500).json({ success: false, error: e.message });
}
}
);
}
module.exports = { gmailAgentEndpoints };

View File

@ -38,6 +38,7 @@ const { telegramEndpoints } = require("./endpoints/telegram");
const { const {
outlookAgentEndpoints, outlookAgentEndpoints,
} = require("./endpoints/utils/outlookAgentUtils"); } = require("./endpoints/utils/outlookAgentUtils");
const { gmailAgentEndpoints } = require("./endpoints/utils/gmailAgentUtils");
const { httpLogger } = require("./middleware/httpLogger"); const { httpLogger } = require("./middleware/httpLogger");
const app = express(); const app = express();
const apiRouter = express.Router(); const apiRouter = express.Router();
@ -93,6 +94,7 @@ mobileEndpoints(apiRouter);
webPushEndpoints(apiRouter); webPushEndpoints(apiRouter);
telegramEndpoints(apiRouter); telegramEndpoints(apiRouter);
outlookAgentEndpoints(apiRouter); outlookAgentEndpoints(apiRouter);
gmailAgentEndpoints(apiRouter);
// Externally facing embedder endpoints // Externally facing embedder endpoints
embeddedEndpoints(apiRouter); embeddedEndpoints(apiRouter);

View File

@ -51,8 +51,7 @@ const SystemSettings = {
"disabled_filesystem_skills", "disabled_filesystem_skills",
"disabled_create_files_skills", "disabled_create_files_skills",
"disabled_gmail_skills", "disabled_gmail_skills",
"gmail_deployment_id", "gmail_agent_config",
"gmail_api_key",
"disabled_outlook_skills", "disabled_outlook_skills",
"outlook_agent_config", "outlook_agent_config",
"imported_agent_skills", "imported_agent_skills",
@ -75,8 +74,7 @@ const SystemSettings = {
"disabled_filesystem_skills", "disabled_filesystem_skills",
"disabled_create_files_skills", "disabled_create_files_skills",
"disabled_gmail_skills", "disabled_gmail_skills",
"gmail_deployment_id", "gmail_agent_config",
"gmail_api_key",
"disabled_outlook_skills", "disabled_outlook_skills",
"outlook_agent_config", "outlook_agent_config",
"agent_sql_connections", "agent_sql_connections",
@ -208,21 +206,33 @@ const SystemSettings = {
return JSON.stringify([]); return JSON.stringify([]);
} }
}, },
gmail_deployment_id: (update) => { gmail_agent_config: async (update) => {
try {
if (!update || typeof update !== "string") return null;
return String(update).trim();
} finally {
const GmailBridge = require("../utils/agents/aibitat/plugins/gmail/lib"); const GmailBridge = require("../utils/agents/aibitat/plugins/gmail/lib");
GmailBridge.reset();
}
},
gmail_api_key: (update) => {
try { try {
if (!update || typeof update !== "string") return null; if (!update) return JSON.stringify({});
return String(update).trim();
const newConfig =
typeof update === "string" ? safeJsonParse(update, {}) : update;
const existingConfig = safeJsonParse(
(await SystemSettings.get({ label: "gmail_agent_config" }))?.value,
{}
);
const mergedConfig = { ...existingConfig };
mergeStringField(mergedConfig, newConfig, "deploymentId");
mergeStringField(
mergedConfig,
newConfig,
"apiKey",
(v) => !v.match(/^\*+$/)
);
return JSON.stringify(mergedConfig);
} catch (e) {
console.error(`Could not validate gmail agent config:`, e.message);
return JSON.stringify({});
} finally { } finally {
const GmailBridge = require("../utils/agents/aibitat/plugins/gmail/lib");
GmailBridge.reset(); GmailBridge.reset();
} }
}, },
@ -531,6 +541,8 @@ const SystemSettings = {
} }
} }
if (validatedValue === undefined) continue;
updatePromises.push( updatePromises.push(
prisma.system_settings.upsert({ prisma.system_settings.upsert({
where: { label: key }, where: { label: key },

View File

@ -5,6 +5,7 @@ const mime = require("mime");
const { SystemSettings } = require("../../../../../models/systemSettings"); const { SystemSettings } = require("../../../../../models/systemSettings");
const { CollectorApi } = require("../../../../collectorApi"); const { CollectorApi } = require("../../../../collectorApi");
const { humanFileSize } = require("../../../../helpers"); const { humanFileSize } = require("../../../../helpers");
const { safeJsonParse } = require("../../../../http");
const MAX_TOTAL_ATTACHMENT_SIZE = 20 * 1024 * 1024; // 20MB limit for all attachments combined const MAX_TOTAL_ATTACHMENT_SIZE = 20 * 1024 * 1024; // 20MB limit for all attachments combined
@ -222,6 +223,34 @@ class GmailBridge {
this.#isInitialized = false; this.#isInitialized = false;
} }
/**
* Gets the current Gmail agent configuration from system settings.
* @returns {Promise<{deploymentId?: string, apiKey?: string}>}
*/
static async getConfig() {
const configJson = await SystemSettings.getValueOrFallback(
{ label: "gmail_agent_config" },
"{}"
);
return safeJsonParse(configJson, {});
}
/**
* Updates the Gmail agent configuration in system settings.
* @param {Object} updates - Fields to update
* @returns {Promise<{success: boolean, error?: string}>}
*/
static async updateConfig(updates) {
try {
await SystemSettings.updateSettings({
gmail_agent_config: JSON.stringify(updates),
});
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
}
/** /**
* Initializes the Gmail bridge by fetching configuration from system settings. * Initializes the Gmail bridge by fetching configuration from system settings.
* @returns {Promise<{success: boolean, error?: string}>} * @returns {Promise<{success: boolean, error?: string}>}
@ -239,16 +268,8 @@ class GmailBridge {
}; };
} }
const deploymentId = await SystemSettings.getValueOrFallback( const config = await GmailBridge.getConfig();
{ label: "gmail_deployment_id" }, if (!config.deploymentId || !config.apiKey) {
null
);
const apiKey = await SystemSettings.getValueOrFallback(
{ label: "gmail_api_key" },
null
);
if (!deploymentId || !apiKey) {
return { return {
success: false, success: false,
error: error:
@ -256,8 +277,8 @@ class GmailBridge {
}; };
} }
this.#deploymentId = deploymentId; this.#deploymentId = config.deploymentId;
this.#apiKey = apiKey; this.#apiKey = config.apiKey;
this.#isInitialized = true; this.#isInitialized = true;
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
@ -282,16 +303,8 @@ class GmailBridge {
const isMultiUser = await SystemSettings.isMultiUserMode(); const isMultiUser = await SystemSettings.isMultiUserMode();
if (isMultiUser) return false; if (isMultiUser) return false;
const deploymentId = await SystemSettings.getValueOrFallback( const config = await GmailBridge.getConfig();
{ label: "gmail_deployment_id" }, return !!(config.deploymentId && config.apiKey);
null
);
const apiKey = await SystemSettings.getValueOrFallback(
{ label: "gmail_api_key" },
null
);
return !!(deploymentId && apiKey);
} }
get maskedDeploymentId() { get maskedDeploymentId() {