@agenticmail/api 0.5.59 → 0.5.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +41 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -264,17 +264,17 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
264
264
|
const router = Router2();
|
|
265
265
|
const deletionService = new AgentDeletionService(db, accountManager, config);
|
|
266
266
|
router.post("/accounts", requireMaster, async (req, res, next) => {
|
|
267
|
+
if (!req.body || typeof req.body !== "object") {
|
|
268
|
+
res.status(400).json({ error: "Request body must be JSON" });
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const { name: accountName, domain, password, metadata, role, persistent } = req.body;
|
|
267
272
|
try {
|
|
268
|
-
if (!
|
|
269
|
-
res.status(400).json({ error: "Request body must be JSON" });
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
const { name: name2, domain, password, metadata, role, persistent } = req.body;
|
|
273
|
-
if (!name2 || typeof name2 !== "string") {
|
|
273
|
+
if (!accountName || typeof accountName !== "string") {
|
|
274
274
|
res.status(400).json({ error: "name is required and must be a string" });
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
|
-
if (
|
|
277
|
+
if (accountName.length > 64) {
|
|
278
278
|
res.status(400).json({ error: "name must be 64 characters or fewer" });
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
@@ -293,7 +293,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
293
293
|
const cleanMeta = metadata ? Object.fromEntries(
|
|
294
294
|
Object.entries(metadata).filter(([k]) => !k.startsWith("_"))
|
|
295
295
|
) : void 0;
|
|
296
|
-
const agent = await accountManager.create({ name:
|
|
296
|
+
const agent = await accountManager.create({ name: accountName, domain, password: password || void 0, metadata: cleanMeta, role });
|
|
297
297
|
try {
|
|
298
298
|
db.prepare("UPDATE agents SET last_activity_at = datetime('now') WHERE id = ?").run(agent.id);
|
|
299
299
|
} catch {
|
|
@@ -310,7 +310,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
310
310
|
} catch (err) {
|
|
311
311
|
const msg = err.message ?? "";
|
|
312
312
|
if (msg.includes("UNIQUE") || msg.includes("unique") || msg.includes("already exists") || msg.includes("duplicate") || msg.includes("fieldAlreadyExists") || msg.toLowerCase().includes("alreadyexists")) {
|
|
313
|
-
res.status(409).json({ error: "Account already exists", name });
|
|
313
|
+
res.status(409).json({ error: "Account already exists", name: accountName });
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
316
|
next(err);
|
|
@@ -644,13 +644,13 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
644
644
|
});
|
|
645
645
|
router.post("/contacts", requireAgent, async (req, res, next) => {
|
|
646
646
|
try {
|
|
647
|
-
const { name
|
|
647
|
+
const { name, email, notes } = req.body || {};
|
|
648
648
|
if (!email) {
|
|
649
649
|
res.status(400).json({ error: "email is required" });
|
|
650
650
|
return;
|
|
651
651
|
}
|
|
652
652
|
const id = uuidv4();
|
|
653
|
-
db.prepare("INSERT OR REPLACE INTO contacts (id, agent_id, name, email, notes) VALUES (?, ?, ?, ?, ?)").run(id, req.agent.id,
|
|
653
|
+
db.prepare("INSERT OR REPLACE INTO contacts (id, agent_id, name, email, notes) VALUES (?, ?, ?, ?, ?)").run(id, req.agent.id, name || null, email, notes || null);
|
|
654
654
|
res.json({ ok: true, id, email });
|
|
655
655
|
} catch (err) {
|
|
656
656
|
next(err);
|
|
@@ -795,8 +795,8 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
795
795
|
});
|
|
796
796
|
router.post("/signatures", requireAgent, async (req, res, next) => {
|
|
797
797
|
try {
|
|
798
|
-
const { name
|
|
799
|
-
if (!
|
|
798
|
+
const { name, text, html, isDefault } = req.body || {};
|
|
799
|
+
if (!name) {
|
|
800
800
|
res.status(400).json({ error: "name is required" });
|
|
801
801
|
return;
|
|
802
802
|
}
|
|
@@ -804,7 +804,7 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
804
804
|
if (isDefault) {
|
|
805
805
|
db.prepare("UPDATE signatures SET is_default = 0 WHERE agent_id = ?").run(req.agent.id);
|
|
806
806
|
}
|
|
807
|
-
db.prepare("INSERT OR REPLACE INTO signatures (id, agent_id, name, text_content, html_content, is_default) VALUES (?, ?, ?, ?, ?, ?)").run(id, req.agent.id,
|
|
807
|
+
db.prepare("INSERT OR REPLACE INTO signatures (id, agent_id, name, text_content, html_content, is_default) VALUES (?, ?, ?, ?, ?, ?)").run(id, req.agent.id, name, text || null, html || null, isDefault ? 1 : 0);
|
|
808
808
|
res.json({ ok: true, id });
|
|
809
809
|
} catch (err) {
|
|
810
810
|
next(err);
|
|
@@ -832,13 +832,13 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
832
832
|
});
|
|
833
833
|
router.post("/templates", requireAgent, async (req, res, next) => {
|
|
834
834
|
try {
|
|
835
|
-
const { name
|
|
836
|
-
if (!
|
|
835
|
+
const { name, subject, text, html } = req.body || {};
|
|
836
|
+
if (!name) {
|
|
837
837
|
res.status(400).json({ error: "name is required" });
|
|
838
838
|
return;
|
|
839
839
|
}
|
|
840
840
|
const id = uuidv4();
|
|
841
|
-
db.prepare("INSERT OR REPLACE INTO templates (id, agent_id, name, subject, text_body, html_body) VALUES (?, ?, ?, ?, ?, ?)").run(id, req.agent.id,
|
|
841
|
+
db.prepare("INSERT OR REPLACE INTO templates (id, agent_id, name, subject, text_body, html_body) VALUES (?, ?, ?, ?, ?, ?)").run(id, req.agent.id, name, subject || null, text || null, html || null);
|
|
842
842
|
res.json({ ok: true, id });
|
|
843
843
|
} catch (err) {
|
|
844
844
|
next(err);
|
|
@@ -912,14 +912,14 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
912
912
|
});
|
|
913
913
|
router.post("/tags", requireAgent, async (req, res, next) => {
|
|
914
914
|
try {
|
|
915
|
-
const { name
|
|
916
|
-
if (!
|
|
915
|
+
const { name, color } = req.body || {};
|
|
916
|
+
if (!name) {
|
|
917
917
|
res.status(400).json({ error: "name is required" });
|
|
918
918
|
return;
|
|
919
919
|
}
|
|
920
920
|
const id = uuidv4();
|
|
921
|
-
db.prepare("INSERT OR IGNORE INTO tags (id, agent_id, name, color) VALUES (?, ?, ?, ?)").run(id, req.agent.id,
|
|
922
|
-
res.json({ ok: true, id, name:
|
|
921
|
+
db.prepare("INSERT OR IGNORE INTO tags (id, agent_id, name, color) VALUES (?, ?, ?, ?)").run(id, req.agent.id, name.trim(), color || "#888888");
|
|
922
|
+
res.json({ ok: true, id, name: name.trim(), color: color || "#888888" });
|
|
923
923
|
} catch (err) {
|
|
924
924
|
next(err);
|
|
925
925
|
}
|
|
@@ -1049,16 +1049,16 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
1049
1049
|
});
|
|
1050
1050
|
router.post("/rules", requireAgent, async (req, res, next) => {
|
|
1051
1051
|
try {
|
|
1052
|
-
const { name
|
|
1053
|
-
if (!
|
|
1052
|
+
const { name, conditions, actions, priority, enabled } = req.body || {};
|
|
1053
|
+
if (!name) {
|
|
1054
1054
|
res.status(400).json({ error: "name is required" });
|
|
1055
1055
|
return;
|
|
1056
1056
|
}
|
|
1057
1057
|
const id = uuidv4();
|
|
1058
1058
|
db.prepare(
|
|
1059
1059
|
"INSERT INTO email_rules (id, agent_id, name, priority, enabled, conditions, actions) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
1060
|
-
).run(id, req.agent.id,
|
|
1061
|
-
res.status(201).json({ id, name
|
|
1060
|
+
).run(id, req.agent.id, name, priority ?? 0, enabled !== false ? 1 : 0, JSON.stringify(conditions || {}), JSON.stringify(actions || {}));
|
|
1061
|
+
res.status(201).json({ id, name, conditions: conditions || {}, actions: actions || {}, priority: priority ?? 0, enabled: enabled !== false });
|
|
1062
1062
|
} catch (err) {
|
|
1063
1063
|
next(err);
|
|
1064
1064
|
}
|
|
@@ -1988,19 +1988,19 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
1988
1988
|
router.post("/mail/folders", requireAgent, async (req, res, next) => {
|
|
1989
1989
|
try {
|
|
1990
1990
|
const agent = req.agent;
|
|
1991
|
-
const { name
|
|
1992
|
-
if (!
|
|
1991
|
+
const { name } = req.body || {};
|
|
1992
|
+
if (!name || typeof name !== "string" || !name.trim()) {
|
|
1993
1993
|
res.status(400).json({ error: "name is required" });
|
|
1994
1994
|
return;
|
|
1995
1995
|
}
|
|
1996
|
-
if (
|
|
1996
|
+
if (name.length > 200 || /[\\*%]/.test(name)) {
|
|
1997
1997
|
res.status(400).json({ error: "Invalid folder name" });
|
|
1998
1998
|
return;
|
|
1999
1999
|
}
|
|
2000
2000
|
const password = getAgentPassword(agent);
|
|
2001
2001
|
const receiver = await getReceiver(agent.stalwartPrincipal, password, config);
|
|
2002
|
-
await receiver.createFolder(
|
|
2003
|
-
res.json({ ok: true, folder:
|
|
2002
|
+
await receiver.createFolder(name);
|
|
2003
|
+
res.json({ ok: true, folder: name });
|
|
2004
2004
|
} catch (err) {
|
|
2005
2005
|
next(err);
|
|
2006
2006
|
}
|
|
@@ -3478,7 +3478,7 @@ function buildColumnDDL(col, dialect) {
|
|
|
3478
3478
|
if (typeof col.default === "string") {
|
|
3479
3479
|
const trimmed = col.default.trim();
|
|
3480
3480
|
const isSqlExpr = /\(.*\)/.test(trimmed) || /^CURRENT_(?:TIMESTAMP|DATE|TIME)$/i.test(trimmed);
|
|
3481
|
-
val = isSqlExpr ? trimmed : `'${col.default.replace(/'/g, "''")}'`;
|
|
3481
|
+
val = isSqlExpr ? `(${trimmed})` : `'${col.default.replace(/'/g, "''")}'`;
|
|
3482
3482
|
} else {
|
|
3483
3483
|
val = col.default;
|
|
3484
3484
|
}
|
|
@@ -3491,15 +3491,15 @@ function buildColumnDDL(col, dialect) {
|
|
|
3491
3491
|
}
|
|
3492
3492
|
return ddl;
|
|
3493
3493
|
}
|
|
3494
|
-
function safeTableName(agentId,
|
|
3495
|
-
const clean =
|
|
3494
|
+
function safeTableName(agentId, name, shared) {
|
|
3495
|
+
const clean = name.replace(/[^a-zA-Z0-9_]/g, "").substring(0, 64);
|
|
3496
3496
|
if (!clean) throw new Error("Invalid table name");
|
|
3497
3497
|
const prefix = shared ? "shared" : `agt_${agentId.replace(/[^a-zA-Z0-9]/g, "").substring(0, 16)}`;
|
|
3498
3498
|
return `${prefix}_${clean}`;
|
|
3499
3499
|
}
|
|
3500
|
-
function resolveTable(agentId,
|
|
3501
|
-
if (
|
|
3502
|
-
return safeTableName(agentId,
|
|
3500
|
+
function resolveTable(agentId, name) {
|
|
3501
|
+
if (name.startsWith("agt_") || name.startsWith("shared_")) return name;
|
|
3502
|
+
return safeTableName(agentId, name, false);
|
|
3503
3503
|
}
|
|
3504
3504
|
function isSafeTable(tableName) {
|
|
3505
3505
|
return tableName.startsWith("agt_") || tableName.startsWith("shared_");
|
|
@@ -3657,8 +3657,8 @@ function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite")
|
|
|
3657
3657
|
if (!agent) return;
|
|
3658
3658
|
await ensureMetaTable();
|
|
3659
3659
|
try {
|
|
3660
|
-
const { name
|
|
3661
|
-
if (!
|
|
3660
|
+
const { name, columns, indexes, shared, description, timestamps } = req.body;
|
|
3661
|
+
if (!name || !columns?.length) return res.status(400).json({ error: "name and columns are required" });
|
|
3662
3662
|
const hasPK = columns.some((c) => c.primaryKey);
|
|
3663
3663
|
const allCols = [...hasPK ? [] : [{ name: "id", type: "text", primaryKey: true }], ...columns];
|
|
3664
3664
|
if (timestamps !== false) {
|
|
@@ -3669,9 +3669,9 @@ function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite")
|
|
|
3669
3669
|
allCols.push({ name: "updated_at", type: "timestamp", default: nowExpr(dialect) });
|
|
3670
3670
|
}
|
|
3671
3671
|
}
|
|
3672
|
-
const tableName = safeTableName(agent.id,
|
|
3672
|
+
const tableName = safeTableName(agent.id, name, !!shared);
|
|
3673
3673
|
const existing = await db.get("SELECT table_name FROM agenticmail_storage_meta WHERE table_name = ?", [tableName]);
|
|
3674
|
-
if (existing) return res.status(409).json({ error: `Table "${
|
|
3674
|
+
if (existing) return res.status(409).json({ error: `Table "${name}" already exists`, table: tableName });
|
|
3675
3675
|
const colDefs = allCols.map((c) => buildColumnDDL(c, dialect)).join(",\n ");
|
|
3676
3676
|
await db.run(`CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
3677
3677
|
${colDefs}
|
|
@@ -3692,7 +3692,7 @@ function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite")
|
|
|
3692
3692
|
}
|
|
3693
3693
|
await db.run(
|
|
3694
3694
|
"INSERT INTO agenticmail_storage_meta (table_name, agent_id, display_name, description, shared, columns, indexes) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
3695
|
-
[tableName, agent.id,
|
|
3695
|
+
[tableName, agent.id, name, description || "", shared ? 1 : 0, JSON.stringify(allCols), JSON.stringify(idxMeta)]
|
|
3696
3696
|
);
|
|
3697
3697
|
res.json({ ok: true, table: tableName, columns: allCols, indexes: idxMeta });
|
|
3698
3698
|
} catch (err) {
|