@agenticmail/enterprise 0.5.294 → 0.5.295
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/chunk-3YLLWCUC.js +1519 -0
- package/dist/chunk-HGSWCMB7.js +3952 -0
- package/dist/chunk-KWW53O2B.js +48 -0
- package/dist/cli-agent-BCISHZTV.js +1778 -0
- package/dist/cli-recover-PMJRFJNY.js +487 -0
- package/dist/cli-serve-TTN3XR4Q.js +143 -0
- package/dist/cli-verify-HYUKQELV.js +149 -0
- package/dist/cli.js +5 -5
- package/dist/dashboard/app.js +64 -1
- package/dist/dashboard/pages/login.js +148 -1
- package/dist/dashboard/pages/users.js +55 -8
- package/dist/factory-NTLTU26R.js +9 -0
- package/dist/index.js +3 -3
- package/dist/postgres-PO2XULHX.js +768 -0
- package/dist/server-ADSJRMMF.js +15 -0
- package/dist/setup-2VN7D4OT.js +20 -0
- package/dist/sqlite-C5PV4SCD.js +499 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +29 -0
- package/src/auth/routes.ts +105 -2
- package/src/dashboard/app.js +64 -1
- package/src/dashboard/pages/login.js +148 -1
- package/src/dashboard/pages/users.js +55 -8
- package/src/db/adapter.ts +1 -0
- package/src/db/postgres.ts +2 -0
- package/src/db/sqlite.ts +2 -3
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-HGSWCMB7.js";
|
|
4
|
+
import "./chunk-OF4MUWWS.js";
|
|
5
|
+
import "./chunk-UF3ZJMJO.js";
|
|
6
|
+
import "./chunk-3OC6RH7W.js";
|
|
7
|
+
import "./chunk-2DDKGTD6.js";
|
|
8
|
+
import "./chunk-YVK6F5OD.js";
|
|
9
|
+
import "./chunk-MKRNEM5A.js";
|
|
10
|
+
import "./chunk-DRXMYYKN.js";
|
|
11
|
+
import "./chunk-6WSX7QXF.js";
|
|
12
|
+
import "./chunk-KFQGP6VL.js";
|
|
13
|
+
export {
|
|
14
|
+
createServer
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-3YLLWCUC.js";
|
|
10
|
+
import "./chunk-KWW53O2B.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAllCreateStatements
|
|
3
|
+
} from "./chunk-XMDE2NGH.js";
|
|
4
|
+
import {
|
|
5
|
+
DatabaseAdapter
|
|
6
|
+
} from "./chunk-FLRYMSKY.js";
|
|
7
|
+
import "./chunk-KFQGP6VL.js";
|
|
8
|
+
|
|
9
|
+
// src/db/sqlite.ts
|
|
10
|
+
import { randomUUID, createHash } from "crypto";
|
|
11
|
+
var Database;
|
|
12
|
+
async function getSqlite() {
|
|
13
|
+
if (!Database) {
|
|
14
|
+
try {
|
|
15
|
+
const { resolveDriver } = await import("./resolve-driver-VQXMFKLJ.js");
|
|
16
|
+
const mod = await resolveDriver("better-sqlite3", "SQLite driver not found. Install it: npm install better-sqlite3");
|
|
17
|
+
Database = mod.default;
|
|
18
|
+
} catch {
|
|
19
|
+
throw new Error("SQLite driver not found. Install it: npm install better-sqlite3");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Database;
|
|
23
|
+
}
|
|
24
|
+
var SqliteAdapter = class extends DatabaseAdapter {
|
|
25
|
+
type = "sqlite";
|
|
26
|
+
db = null;
|
|
27
|
+
async connect(config) {
|
|
28
|
+
const Db = await getSqlite();
|
|
29
|
+
const path = config.connectionString || config.database || "./agenticmail-enterprise.db";
|
|
30
|
+
this.db = new Db(path);
|
|
31
|
+
this.db.pragma("journal_mode = WAL");
|
|
32
|
+
this.db.pragma("foreign_keys = ON");
|
|
33
|
+
}
|
|
34
|
+
async disconnect() {
|
|
35
|
+
if (this.db) this.db.close();
|
|
36
|
+
}
|
|
37
|
+
isConnected() {
|
|
38
|
+
return this.db !== null;
|
|
39
|
+
}
|
|
40
|
+
async migrate() {
|
|
41
|
+
const stmts = getAllCreateStatements();
|
|
42
|
+
const tx = this.db.transaction(() => {
|
|
43
|
+
for (const stmt of stmts) this.db.exec(stmt);
|
|
44
|
+
this.db.prepare(
|
|
45
|
+
`INSERT OR IGNORE INTO retention_policy (id) VALUES ('default')`
|
|
46
|
+
).run();
|
|
47
|
+
this.db.prepare(
|
|
48
|
+
`INSERT OR IGNORE INTO company_settings (id, name, subdomain) VALUES ('default', 'My Company', 'my-company')`
|
|
49
|
+
).run();
|
|
50
|
+
try {
|
|
51
|
+
this.db.exec(`ALTER TABLE users ADD COLUMN permissions TEXT DEFAULT '"*"'`);
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
this.db.exec(`ALTER TABLE users ADD COLUMN must_reset_password INTEGER DEFAULT 0`);
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
tx();
|
|
60
|
+
}
|
|
61
|
+
// ─── Engine Integration ──────────────────────────────────
|
|
62
|
+
getEngineDB() {
|
|
63
|
+
if (!this.db) return null;
|
|
64
|
+
const db = this.db;
|
|
65
|
+
return {
|
|
66
|
+
run: async (sql, params) => {
|
|
67
|
+
db.prepare(sql).run(...params || []);
|
|
68
|
+
},
|
|
69
|
+
get: async (sql, params) => {
|
|
70
|
+
return db.prepare(sql).get(...params || []);
|
|
71
|
+
},
|
|
72
|
+
all: async (sql, params) => {
|
|
73
|
+
return db.prepare(sql).all(...params || []);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ─── Company ─────────────────────────────────────────────
|
|
78
|
+
async getSettings() {
|
|
79
|
+
const r = this.db.prepare("SELECT * FROM company_settings WHERE id = ?").get("default");
|
|
80
|
+
return r ? this.mapSettings(r) : null;
|
|
81
|
+
}
|
|
82
|
+
async updateSettings(updates) {
|
|
83
|
+
const map = {
|
|
84
|
+
name: "name",
|
|
85
|
+
domain: "domain",
|
|
86
|
+
subdomain: "subdomain",
|
|
87
|
+
smtpHost: "smtp_host",
|
|
88
|
+
smtpPort: "smtp_port",
|
|
89
|
+
smtpUser: "smtp_user",
|
|
90
|
+
smtpPass: "smtp_pass",
|
|
91
|
+
dkimPrivateKey: "dkim_private_key",
|
|
92
|
+
logoUrl: "logo_url",
|
|
93
|
+
primaryColor: "primary_color",
|
|
94
|
+
plan: "plan",
|
|
95
|
+
deploymentKeyHash: "deployment_key_hash",
|
|
96
|
+
domainRegistrationId: "domain_registration_id",
|
|
97
|
+
domainDnsChallenge: "domain_dns_challenge",
|
|
98
|
+
domainVerifiedAt: "domain_verified_at",
|
|
99
|
+
domainRegisteredAt: "domain_registered_at",
|
|
100
|
+
domainStatus: "domain_status"
|
|
101
|
+
};
|
|
102
|
+
const sets = [];
|
|
103
|
+
const vals = [];
|
|
104
|
+
for (const [key, col] of Object.entries(map)) {
|
|
105
|
+
if (updates[key] !== void 0) {
|
|
106
|
+
sets.push(`${col} = ?`);
|
|
107
|
+
vals.push(updates[key]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (updates.ssoConfig !== void 0) {
|
|
111
|
+
sets.push("sso_config = ?");
|
|
112
|
+
vals.push(JSON.stringify(updates.ssoConfig));
|
|
113
|
+
}
|
|
114
|
+
if (updates.toolSecurityConfig !== void 0) {
|
|
115
|
+
sets.push("tool_security_config = ?");
|
|
116
|
+
vals.push(JSON.stringify(updates.toolSecurityConfig));
|
|
117
|
+
}
|
|
118
|
+
if (updates.firewallConfig !== void 0) {
|
|
119
|
+
sets.push("firewall_config = ?");
|
|
120
|
+
vals.push(JSON.stringify(updates.firewallConfig));
|
|
121
|
+
}
|
|
122
|
+
if (updates.modelPricingConfig !== void 0) {
|
|
123
|
+
sets.push("model_pricing_config = ?");
|
|
124
|
+
vals.push(JSON.stringify(updates.modelPricingConfig));
|
|
125
|
+
}
|
|
126
|
+
sets.push("updated_at = datetime('now')");
|
|
127
|
+
vals.push("default");
|
|
128
|
+
this.db.prepare(`UPDATE company_settings SET ${sets.join(", ")} WHERE id = ?`).run(...vals);
|
|
129
|
+
return this.getSettings();
|
|
130
|
+
}
|
|
131
|
+
// ─── Agents ──────────────────────────────────────────────
|
|
132
|
+
async createAgent(input) {
|
|
133
|
+
const id = input.id || randomUUID();
|
|
134
|
+
const email = input.email || `${input.name.toLowerCase().replace(/\s+/g, "-")}@localhost`;
|
|
135
|
+
this.db.prepare(
|
|
136
|
+
`INSERT INTO agents (id, name, email, role, metadata, created_by) VALUES (?, ?, ?, ?, ?, ?)`
|
|
137
|
+
).run(id, input.name, email, input.role || "assistant", JSON.stringify(input.metadata || {}), input.createdBy);
|
|
138
|
+
return await this.getAgent(id);
|
|
139
|
+
}
|
|
140
|
+
async getAgent(id) {
|
|
141
|
+
const r = this.db.prepare("SELECT * FROM agents WHERE id = ?").get(id);
|
|
142
|
+
return r ? this.mapAgent(r) : null;
|
|
143
|
+
}
|
|
144
|
+
async getAgentByName(name) {
|
|
145
|
+
const r = this.db.prepare("SELECT * FROM agents WHERE name = ?").get(name);
|
|
146
|
+
return r ? this.mapAgent(r) : null;
|
|
147
|
+
}
|
|
148
|
+
async listAgents(opts) {
|
|
149
|
+
let q = "SELECT * FROM agents";
|
|
150
|
+
const params = [];
|
|
151
|
+
if (opts?.status) {
|
|
152
|
+
q += " WHERE status = ?";
|
|
153
|
+
params.push(opts.status);
|
|
154
|
+
}
|
|
155
|
+
q += " ORDER BY created_at DESC";
|
|
156
|
+
if (opts?.limit) q += ` LIMIT ${opts.limit}`;
|
|
157
|
+
if (opts?.offset) q += ` OFFSET ${opts.offset}`;
|
|
158
|
+
return this.db.prepare(q).all(...params).map((r) => this.mapAgent(r));
|
|
159
|
+
}
|
|
160
|
+
async updateAgent(id, updates) {
|
|
161
|
+
const fields = [];
|
|
162
|
+
const vals = [];
|
|
163
|
+
for (const [key, col] of Object.entries({ name: "name", email: "email", role: "role", status: "status" })) {
|
|
164
|
+
if (updates[key] !== void 0) {
|
|
165
|
+
fields.push(`${col} = ?`);
|
|
166
|
+
vals.push(updates[key]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (updates.metadata) {
|
|
170
|
+
fields.push("metadata = ?");
|
|
171
|
+
vals.push(JSON.stringify(updates.metadata));
|
|
172
|
+
}
|
|
173
|
+
fields.push("updated_at = datetime('now')");
|
|
174
|
+
vals.push(id);
|
|
175
|
+
this.db.prepare(`UPDATE agents SET ${fields.join(", ")} WHERE id = ?`).run(...vals);
|
|
176
|
+
return await this.getAgent(id);
|
|
177
|
+
}
|
|
178
|
+
async archiveAgent(id) {
|
|
179
|
+
this.db.prepare("UPDATE agents SET status = 'archived', updated_at = datetime('now') WHERE id = ?").run(id);
|
|
180
|
+
}
|
|
181
|
+
async deleteAgent(id) {
|
|
182
|
+
this.db.prepare("DELETE FROM agents WHERE id = ?").run(id);
|
|
183
|
+
}
|
|
184
|
+
async countAgents(status) {
|
|
185
|
+
const r = status ? this.db.prepare("SELECT COUNT(*) as c FROM agents WHERE status = ?").get(status) : this.db.prepare("SELECT COUNT(*) as c FROM agents").get();
|
|
186
|
+
return r.c;
|
|
187
|
+
}
|
|
188
|
+
// ─── Users ───────────────────────────────────────────────
|
|
189
|
+
async createUser(input) {
|
|
190
|
+
const id = randomUUID();
|
|
191
|
+
let passwordHash = null;
|
|
192
|
+
if (input.password) {
|
|
193
|
+
const { default: bcrypt } = await import("bcryptjs");
|
|
194
|
+
passwordHash = await bcrypt.hash(input.password, 12);
|
|
195
|
+
}
|
|
196
|
+
this.db.prepare(
|
|
197
|
+
`INSERT INTO users (id, email, name, role, password_hash, sso_provider, sso_subject)
|
|
198
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
199
|
+
).run(id, input.email, input.name, input.role, passwordHash, input.ssoProvider || null, input.ssoSubject || null);
|
|
200
|
+
return await this.getUser(id);
|
|
201
|
+
}
|
|
202
|
+
async getUser(id) {
|
|
203
|
+
const r = this.db.prepare("SELECT * FROM users WHERE id = ?").get(id);
|
|
204
|
+
return r ? this.mapUser(r) : null;
|
|
205
|
+
}
|
|
206
|
+
async getUserByEmail(email) {
|
|
207
|
+
const r = this.db.prepare("SELECT * FROM users WHERE email = ?").get(email);
|
|
208
|
+
return r ? this.mapUser(r) : null;
|
|
209
|
+
}
|
|
210
|
+
async getUserBySso(provider, subject) {
|
|
211
|
+
const r = this.db.prepare("SELECT * FROM users WHERE sso_provider = ? AND sso_subject = ?").get(provider, subject);
|
|
212
|
+
return r ? this.mapUser(r) : null;
|
|
213
|
+
}
|
|
214
|
+
async listUsers(opts) {
|
|
215
|
+
let q = "SELECT * FROM users ORDER BY created_at DESC";
|
|
216
|
+
if (opts?.limit) q += ` LIMIT ${opts.limit}`;
|
|
217
|
+
if (opts?.offset) q += ` OFFSET ${opts.offset}`;
|
|
218
|
+
return this.db.prepare(q).all().map((r) => this.mapUser(r));
|
|
219
|
+
}
|
|
220
|
+
async updateUser(id, updates) {
|
|
221
|
+
const fields = [];
|
|
222
|
+
const vals = [];
|
|
223
|
+
for (const key of ["email", "name", "role"]) {
|
|
224
|
+
if (updates[key] !== void 0) {
|
|
225
|
+
fields.push(`${key} = ?`);
|
|
226
|
+
vals.push(updates[key]);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
fields.push("updated_at = datetime('now')");
|
|
230
|
+
vals.push(id);
|
|
231
|
+
this.db.prepare(`UPDATE users SET ${fields.join(", ")} WHERE id = ?`).run(...vals);
|
|
232
|
+
return await this.getUser(id);
|
|
233
|
+
}
|
|
234
|
+
async deleteUser(id) {
|
|
235
|
+
this.db.prepare("DELETE FROM users WHERE id = ?").run(id);
|
|
236
|
+
}
|
|
237
|
+
// ─── Audit ───────────────────────────────────────────────
|
|
238
|
+
async logEvent(event) {
|
|
239
|
+
this.db.prepare(
|
|
240
|
+
`INSERT INTO audit_log (id, actor, actor_type, action, resource, details, ip) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
241
|
+
).run(
|
|
242
|
+
randomUUID(),
|
|
243
|
+
event.actor,
|
|
244
|
+
event.actorType,
|
|
245
|
+
event.action,
|
|
246
|
+
event.resource,
|
|
247
|
+
JSON.stringify(event.details || {}),
|
|
248
|
+
event.ip || null
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
async queryAudit(filters) {
|
|
252
|
+
const where = [];
|
|
253
|
+
const params = [];
|
|
254
|
+
if (filters.actor) {
|
|
255
|
+
where.push("actor = ?");
|
|
256
|
+
params.push(filters.actor);
|
|
257
|
+
}
|
|
258
|
+
if (filters.action) {
|
|
259
|
+
where.push("action = ?");
|
|
260
|
+
params.push(filters.action);
|
|
261
|
+
}
|
|
262
|
+
if (filters.resource) {
|
|
263
|
+
where.push("resource LIKE ?");
|
|
264
|
+
params.push(`%${filters.resource}%`);
|
|
265
|
+
}
|
|
266
|
+
if (filters.from) {
|
|
267
|
+
where.push("timestamp >= ?");
|
|
268
|
+
params.push(filters.from.toISOString());
|
|
269
|
+
}
|
|
270
|
+
if (filters.to) {
|
|
271
|
+
where.push("timestamp <= ?");
|
|
272
|
+
params.push(filters.to.toISOString());
|
|
273
|
+
}
|
|
274
|
+
const wc = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
|
|
275
|
+
const total = this.db.prepare(`SELECT COUNT(*) as c FROM audit_log ${wc}`).get(...params).c;
|
|
276
|
+
let q = `SELECT * FROM audit_log ${wc} ORDER BY timestamp DESC`;
|
|
277
|
+
if (filters.limit) q += ` LIMIT ${filters.limit}`;
|
|
278
|
+
if (filters.offset) q += ` OFFSET ${filters.offset}`;
|
|
279
|
+
const rows = this.db.prepare(q).all(...params);
|
|
280
|
+
return {
|
|
281
|
+
events: rows.map((r) => ({
|
|
282
|
+
id: r.id,
|
|
283
|
+
timestamp: new Date(r.timestamp),
|
|
284
|
+
actor: r.actor,
|
|
285
|
+
actorType: r.actor_type,
|
|
286
|
+
action: r.action,
|
|
287
|
+
resource: r.resource,
|
|
288
|
+
details: JSON.parse(r.details || "{}"),
|
|
289
|
+
ip: r.ip
|
|
290
|
+
})),
|
|
291
|
+
total
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// ─── API Keys ────────────────────────────────────────────
|
|
295
|
+
async createApiKey(input) {
|
|
296
|
+
const id = randomUUID();
|
|
297
|
+
const plaintext = `ek_${randomUUID().replace(/-/g, "")}`;
|
|
298
|
+
const keyHash = createHash("sha256").update(plaintext).digest("hex");
|
|
299
|
+
const keyPrefix = plaintext.substring(0, 11);
|
|
300
|
+
this.db.prepare(
|
|
301
|
+
`INSERT INTO api_keys (id, name, key_hash, key_prefix, scopes, created_by, expires_at)
|
|
302
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
303
|
+
).run(id, input.name, keyHash, keyPrefix, JSON.stringify(input.scopes), input.createdBy, input.expiresAt?.toISOString() || null);
|
|
304
|
+
return { key: await this.getApiKey(id), plaintext };
|
|
305
|
+
}
|
|
306
|
+
async getApiKey(id) {
|
|
307
|
+
const r = this.db.prepare("SELECT * FROM api_keys WHERE id = ?").get(id);
|
|
308
|
+
return r ? this.mapApiKey(r) : null;
|
|
309
|
+
}
|
|
310
|
+
async validateApiKey(plaintext) {
|
|
311
|
+
const keyHash = createHash("sha256").update(plaintext).digest("hex");
|
|
312
|
+
const r = this.db.prepare("SELECT * FROM api_keys WHERE key_hash = ? AND revoked = 0").get(keyHash);
|
|
313
|
+
if (!r) return null;
|
|
314
|
+
const key = this.mapApiKey(r);
|
|
315
|
+
if (key.expiresAt && /* @__PURE__ */ new Date() > key.expiresAt) return null;
|
|
316
|
+
this.db.prepare("UPDATE api_keys SET last_used_at = datetime('now') WHERE id = ?").run(key.id);
|
|
317
|
+
return key;
|
|
318
|
+
}
|
|
319
|
+
async listApiKeys(opts) {
|
|
320
|
+
let q = "SELECT * FROM api_keys WHERE revoked = 0";
|
|
321
|
+
const params = [];
|
|
322
|
+
if (opts?.createdBy) {
|
|
323
|
+
q += " AND created_by = ?";
|
|
324
|
+
params.push(opts.createdBy);
|
|
325
|
+
}
|
|
326
|
+
q += " ORDER BY created_at DESC";
|
|
327
|
+
return this.db.prepare(q).all(...params).map((r) => this.mapApiKey(r));
|
|
328
|
+
}
|
|
329
|
+
async revokeApiKey(id) {
|
|
330
|
+
this.db.prepare("UPDATE api_keys SET revoked = 1 WHERE id = ?").run(id);
|
|
331
|
+
}
|
|
332
|
+
// ─── Rules ───────────────────────────────────────────────
|
|
333
|
+
async createRule(rule) {
|
|
334
|
+
const id = randomUUID();
|
|
335
|
+
this.db.prepare(
|
|
336
|
+
`INSERT INTO email_rules (id, name, agent_id, conditions, actions, priority, enabled)
|
|
337
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
338
|
+
).run(
|
|
339
|
+
id,
|
|
340
|
+
rule.name,
|
|
341
|
+
rule.agentId || null,
|
|
342
|
+
JSON.stringify(rule.conditions),
|
|
343
|
+
JSON.stringify(rule.actions),
|
|
344
|
+
rule.priority,
|
|
345
|
+
rule.enabled ? 1 : 0
|
|
346
|
+
);
|
|
347
|
+
const r = this.db.prepare("SELECT * FROM email_rules WHERE id = ?").get(id);
|
|
348
|
+
return this.mapRule(r);
|
|
349
|
+
}
|
|
350
|
+
async getRules(agentId) {
|
|
351
|
+
let q = "SELECT * FROM email_rules";
|
|
352
|
+
const params = [];
|
|
353
|
+
if (agentId) {
|
|
354
|
+
q += " WHERE agent_id = ? OR agent_id IS NULL";
|
|
355
|
+
params.push(agentId);
|
|
356
|
+
}
|
|
357
|
+
q += " ORDER BY priority DESC";
|
|
358
|
+
return this.db.prepare(q).all(...params).map((r) => this.mapRule(r));
|
|
359
|
+
}
|
|
360
|
+
async updateRule(id, updates) {
|
|
361
|
+
const fields = [];
|
|
362
|
+
const vals = [];
|
|
363
|
+
if (updates.name !== void 0) {
|
|
364
|
+
fields.push("name = ?");
|
|
365
|
+
vals.push(updates.name);
|
|
366
|
+
}
|
|
367
|
+
if (updates.conditions) {
|
|
368
|
+
fields.push("conditions = ?");
|
|
369
|
+
vals.push(JSON.stringify(updates.conditions));
|
|
370
|
+
}
|
|
371
|
+
if (updates.actions) {
|
|
372
|
+
fields.push("actions = ?");
|
|
373
|
+
vals.push(JSON.stringify(updates.actions));
|
|
374
|
+
}
|
|
375
|
+
if (updates.priority !== void 0) {
|
|
376
|
+
fields.push("priority = ?");
|
|
377
|
+
vals.push(updates.priority);
|
|
378
|
+
}
|
|
379
|
+
if (updates.enabled !== void 0) {
|
|
380
|
+
fields.push("enabled = ?");
|
|
381
|
+
vals.push(updates.enabled ? 1 : 0);
|
|
382
|
+
}
|
|
383
|
+
fields.push("updated_at = datetime('now')");
|
|
384
|
+
vals.push(id);
|
|
385
|
+
this.db.prepare(`UPDATE email_rules SET ${fields.join(", ")} WHERE id = ?`).run(...vals);
|
|
386
|
+
const r = this.db.prepare("SELECT * FROM email_rules WHERE id = ?").get(id);
|
|
387
|
+
return this.mapRule(r);
|
|
388
|
+
}
|
|
389
|
+
async deleteRule(id) {
|
|
390
|
+
this.db.prepare("DELETE FROM email_rules WHERE id = ?").run(id);
|
|
391
|
+
}
|
|
392
|
+
// ─── Retention ───────────────────────────────────────────
|
|
393
|
+
async getRetentionPolicy() {
|
|
394
|
+
const r = this.db.prepare("SELECT * FROM retention_policy WHERE id = ?").get("default");
|
|
395
|
+
if (!r) return { enabled: false, retainDays: 365, archiveFirst: true };
|
|
396
|
+
return { enabled: !!r.enabled, retainDays: r.retain_days, excludeTags: JSON.parse(r.exclude_tags || "[]"), archiveFirst: !!r.archive_first };
|
|
397
|
+
}
|
|
398
|
+
async setRetentionPolicy(policy) {
|
|
399
|
+
this.db.prepare(
|
|
400
|
+
`UPDATE retention_policy SET enabled = ?, retain_days = ?, exclude_tags = ?, archive_first = ? WHERE id = 'default'`
|
|
401
|
+
).run(policy.enabled ? 1 : 0, policy.retainDays, JSON.stringify(policy.excludeTags || []), policy.archiveFirst ? 1 : 0);
|
|
402
|
+
}
|
|
403
|
+
// ─── Stats ───────────────────────────────────────────────
|
|
404
|
+
async getStats() {
|
|
405
|
+
return {
|
|
406
|
+
totalAgents: this.db.prepare("SELECT COUNT(*) as c FROM agents").get().c,
|
|
407
|
+
activeAgents: this.db.prepare("SELECT COUNT(*) as c FROM agents WHERE status = 'active'").get().c,
|
|
408
|
+
totalUsers: this.db.prepare("SELECT COUNT(*) as c FROM users").get().c,
|
|
409
|
+
totalEmails: 0,
|
|
410
|
+
totalAuditEvents: this.db.prepare("SELECT COUNT(*) as c FROM audit_log").get().c
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
// ─── Mappers ─────────────────────────────────────────────
|
|
414
|
+
mapAgent(r) {
|
|
415
|
+
return {
|
|
416
|
+
id: r.id,
|
|
417
|
+
name: r.name,
|
|
418
|
+
email: r.email,
|
|
419
|
+
role: r.role,
|
|
420
|
+
status: r.status,
|
|
421
|
+
metadata: typeof r.metadata === "string" ? JSON.parse(r.metadata) : r.metadata,
|
|
422
|
+
createdAt: new Date(r.created_at),
|
|
423
|
+
updatedAt: new Date(r.updated_at),
|
|
424
|
+
createdBy: r.created_by
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
mapUser(r) {
|
|
428
|
+
return {
|
|
429
|
+
id: r.id,
|
|
430
|
+
email: r.email,
|
|
431
|
+
name: r.name,
|
|
432
|
+
role: r.role,
|
|
433
|
+
passwordHash: r.password_hash,
|
|
434
|
+
ssoProvider: r.sso_provider,
|
|
435
|
+
ssoSubject: r.sso_subject,
|
|
436
|
+
createdAt: new Date(r.created_at),
|
|
437
|
+
updatedAt: new Date(r.updated_at),
|
|
438
|
+
lastLoginAt: r.last_login_at ? new Date(r.last_login_at) : void 0
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
mapApiKey(r) {
|
|
442
|
+
return {
|
|
443
|
+
id: r.id,
|
|
444
|
+
name: r.name,
|
|
445
|
+
keyHash: r.key_hash,
|
|
446
|
+
keyPrefix: r.key_prefix,
|
|
447
|
+
scopes: typeof r.scopes === "string" ? JSON.parse(r.scopes) : r.scopes,
|
|
448
|
+
createdBy: r.created_by,
|
|
449
|
+
createdAt: new Date(r.created_at),
|
|
450
|
+
lastUsedAt: r.last_used_at ? new Date(r.last_used_at) : void 0,
|
|
451
|
+
expiresAt: r.expires_at ? new Date(r.expires_at) : void 0,
|
|
452
|
+
revoked: !!r.revoked
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
mapRule(r) {
|
|
456
|
+
return {
|
|
457
|
+
id: r.id,
|
|
458
|
+
name: r.name,
|
|
459
|
+
agentId: r.agent_id,
|
|
460
|
+
conditions: typeof r.conditions === "string" ? JSON.parse(r.conditions) : r.conditions,
|
|
461
|
+
actions: typeof r.actions === "string" ? JSON.parse(r.actions) : r.actions,
|
|
462
|
+
priority: r.priority,
|
|
463
|
+
enabled: !!r.enabled,
|
|
464
|
+
createdAt: new Date(r.created_at),
|
|
465
|
+
updatedAt: new Date(r.updated_at)
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
mapSettings(r) {
|
|
469
|
+
return {
|
|
470
|
+
id: r.id,
|
|
471
|
+
name: r.name,
|
|
472
|
+
domain: r.domain,
|
|
473
|
+
subdomain: r.subdomain,
|
|
474
|
+
smtpHost: r.smtp_host,
|
|
475
|
+
smtpPort: r.smtp_port,
|
|
476
|
+
smtpUser: r.smtp_user,
|
|
477
|
+
smtpPass: r.smtp_pass,
|
|
478
|
+
dkimPrivateKey: r.dkim_private_key,
|
|
479
|
+
logoUrl: r.logo_url,
|
|
480
|
+
primaryColor: r.primary_color,
|
|
481
|
+
ssoConfig: r.sso_config ? typeof r.sso_config === "string" ? JSON.parse(r.sso_config) : r.sso_config : void 0,
|
|
482
|
+
toolSecurityConfig: r.tool_security_config ? typeof r.tool_security_config === "string" ? JSON.parse(r.tool_security_config) : r.tool_security_config : {},
|
|
483
|
+
firewallConfig: r.firewall_config ? typeof r.firewall_config === "string" ? JSON.parse(r.firewall_config) : r.firewall_config : {},
|
|
484
|
+
modelPricingConfig: r.model_pricing_config ? typeof r.model_pricing_config === "string" ? JSON.parse(r.model_pricing_config) : r.model_pricing_config : {},
|
|
485
|
+
plan: r.plan,
|
|
486
|
+
createdAt: new Date(r.created_at),
|
|
487
|
+
updatedAt: new Date(r.updated_at),
|
|
488
|
+
deploymentKeyHash: r.deployment_key_hash,
|
|
489
|
+
domainRegistrationId: r.domain_registration_id,
|
|
490
|
+
domainDnsChallenge: r.domain_dns_challenge,
|
|
491
|
+
domainVerifiedAt: r.domain_verified_at || void 0,
|
|
492
|
+
domainRegisteredAt: r.domain_registered_at || void 0,
|
|
493
|
+
domainStatus: r.domain_status || "unregistered"
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
export {
|
|
498
|
+
SqliteAdapter
|
|
499
|
+
};
|
package/package.json
CHANGED
package/src/admin/routes.ts
CHANGED
|
@@ -481,6 +481,35 @@ export function createAdminRoutes(db: DatabaseAdapter) {
|
|
|
481
481
|
if (existing) return c.json({ error: 'Email already registered' }, 409);
|
|
482
482
|
|
|
483
483
|
const user = await db.createUser(body);
|
|
484
|
+
|
|
485
|
+
// Mark as must-reset-password (admin-created accounts)
|
|
486
|
+
try {
|
|
487
|
+
await (db as any).pool.query(
|
|
488
|
+
'UPDATE users SET must_reset_password = TRUE WHERE id = $1',
|
|
489
|
+
[user.id]
|
|
490
|
+
);
|
|
491
|
+
} catch {
|
|
492
|
+
try {
|
|
493
|
+
const edb = (db as any).db;
|
|
494
|
+
if (edb?.prepare) edb.prepare('UPDATE users SET must_reset_password = 1 WHERE id = ?').run(user.id);
|
|
495
|
+
} catch { /* ignore */ }
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Set initial permissions if provided
|
|
499
|
+
if (body.permissions && body.permissions !== '*') {
|
|
500
|
+
try {
|
|
501
|
+
await (db as any).pool.query(
|
|
502
|
+
'UPDATE users SET permissions = $1 WHERE id = $2',
|
|
503
|
+
[JSON.stringify(body.permissions), user.id]
|
|
504
|
+
);
|
|
505
|
+
} catch {
|
|
506
|
+
try {
|
|
507
|
+
const edb = (db as any).db;
|
|
508
|
+
if (edb?.prepare) edb.prepare('UPDATE users SET permissions = ? WHERE id = ?').run(JSON.stringify(body.permissions), user.id);
|
|
509
|
+
} catch { /* ignore */ }
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
484
513
|
const { passwordHash, ...safe } = user;
|
|
485
514
|
return c.json(safe, 201);
|
|
486
515
|
});
|