@agentgazer/server 0.1.1

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.
Files changed (59) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +86 -0
  3. package/dist/alerts/evaluator.d.ts +9 -0
  4. package/dist/alerts/evaluator.d.ts.map +1 -0
  5. package/dist/alerts/evaluator.js +323 -0
  6. package/dist/alerts/evaluator.js.map +1 -0
  7. package/dist/db.d.ts +121 -0
  8. package/dist/db.d.ts.map +1 -0
  9. package/dist/db.js +425 -0
  10. package/dist/db.js.map +1 -0
  11. package/dist/index.d.ts +5 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +15 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/middleware/auth.d.ts +3 -0
  16. package/dist/middleware/auth.d.ts.map +1 -0
  17. package/dist/middleware/auth.js +65 -0
  18. package/dist/middleware/auth.js.map +1 -0
  19. package/dist/middleware/rate-limit.d.ts +8 -0
  20. package/dist/middleware/rate-limit.d.ts.map +1 -0
  21. package/dist/middleware/rate-limit.js +72 -0
  22. package/dist/middleware/rate-limit.js.map +1 -0
  23. package/dist/routes/agents.d.ts +3 -0
  24. package/dist/routes/agents.d.ts.map +1 -0
  25. package/dist/routes/agents.js +149 -0
  26. package/dist/routes/agents.js.map +1 -0
  27. package/dist/routes/alerts.d.ts +3 -0
  28. package/dist/routes/alerts.d.ts.map +1 -0
  29. package/dist/routes/alerts.js +286 -0
  30. package/dist/routes/alerts.js.map +1 -0
  31. package/dist/routes/auth.d.ts +3 -0
  32. package/dist/routes/auth.d.ts.map +1 -0
  33. package/dist/routes/auth.js +20 -0
  34. package/dist/routes/auth.js.map +1 -0
  35. package/dist/routes/events.d.ts +3 -0
  36. package/dist/routes/events.d.ts.map +1 -0
  37. package/dist/routes/events.js +304 -0
  38. package/dist/routes/events.js.map +1 -0
  39. package/dist/routes/health.d.ts +3 -0
  40. package/dist/routes/health.d.ts.map +1 -0
  41. package/dist/routes/health.js +13 -0
  42. package/dist/routes/health.js.map +1 -0
  43. package/dist/routes/model-rules.d.ts +3 -0
  44. package/dist/routes/model-rules.d.ts.map +1 -0
  45. package/dist/routes/model-rules.js +71 -0
  46. package/dist/routes/model-rules.js.map +1 -0
  47. package/dist/routes/rate-limits.d.ts +3 -0
  48. package/dist/routes/rate-limits.d.ts.map +1 -0
  49. package/dist/routes/rate-limits.js +58 -0
  50. package/dist/routes/rate-limits.js.map +1 -0
  51. package/dist/routes/stats.d.ts +3 -0
  52. package/dist/routes/stats.d.ts.map +1 -0
  53. package/dist/routes/stats.js +233 -0
  54. package/dist/routes/stats.js.map +1 -0
  55. package/dist/server.d.ts +20 -0
  56. package/dist/server.d.ts.map +1 -0
  57. package/dist/server.js +155 -0
  58. package/dist/server.js.map +1 -0
  59. package/package.json +65 -0
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authMiddleware = authMiddleware;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const PUBLIC_PATHS = ["/api/health", "/api/auth/verify"];
6
+ // Simple rate limiting for public endpoints (e.g. token brute-force on /api/auth/verify)
7
+ const publicRateMap = new Map();
8
+ const PUBLIC_RATE_MAX = 20;
9
+ const PUBLIC_RATE_WINDOW_MS = 60_000;
10
+ setInterval(() => {
11
+ const now = Date.now();
12
+ for (const [key, entry] of publicRateMap) {
13
+ if (now >= entry.resetAt)
14
+ publicRateMap.delete(key);
15
+ }
16
+ }, 5 * 60_000).unref();
17
+ function authMiddleware(req, res, next) {
18
+ // Skip auth for non-API paths (static files, dashboard)
19
+ if (!req.path.startsWith("/api/")) {
20
+ next();
21
+ return;
22
+ }
23
+ // Skip auth for public API paths (with rate limiting)
24
+ if (PUBLIC_PATHS.includes(req.path)) {
25
+ const ip = req.ip ?? req.socket.remoteAddress ?? "unknown";
26
+ const now = Date.now();
27
+ const entry = publicRateMap.get(ip);
28
+ if (entry && now < entry.resetAt && entry.count >= PUBLIC_RATE_MAX) {
29
+ res.status(429).json({ error: "Too many requests" });
30
+ return;
31
+ }
32
+ if (!entry || now >= entry.resetAt) {
33
+ publicRateMap.set(ip, { count: 1, resetAt: now + PUBLIC_RATE_WINDOW_MS });
34
+ }
35
+ else {
36
+ entry.count++;
37
+ }
38
+ next();
39
+ return;
40
+ }
41
+ // Extract token from Authorization header or x-api-key header
42
+ let token;
43
+ const authHeader = req.headers.authorization;
44
+ if (authHeader && authHeader.startsWith("Bearer ")) {
45
+ token = authHeader.slice(7);
46
+ }
47
+ if (!token) {
48
+ const apiKey = req.headers["x-api-key"];
49
+ if (typeof apiKey === "string") {
50
+ token = apiKey;
51
+ }
52
+ }
53
+ if (!token) {
54
+ res.status(401).json({ error: "Authentication required" });
55
+ return;
56
+ }
57
+ const expectedToken = req.app.locals.token;
58
+ if (token.length !== expectedToken.length ||
59
+ !(0, node_crypto_1.timingSafeEqual)(Buffer.from(token), Buffer.from(expectedToken))) {
60
+ res.status(401).json({ error: "Invalid token" });
61
+ return;
62
+ }
63
+ next();
64
+ }
65
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ /**
3
+ * Rate-limiting middleware for the event ingestion endpoint.
4
+ * Allows up to 1000 events per minute per API key.
5
+ * Counts each event in a batch individually.
6
+ */
7
+ export declare function rateLimitEvents(req: Request, res: Response, next: NextFunction): void;
8
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAyD/D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CA8BrF"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rateLimitEvents = rateLimitEvents;
4
+ const DEFAULT_MAX_TOKENS = 1000; // events per window
5
+ const DEFAULT_REFILL_INTERVAL_MS = 60_000; // 1 minute
6
+ const MAX_BUCKETS = 10_000; // Cap to prevent memory exhaustion from unique key flooding
7
+ const buckets = new Map();
8
+ function getOrCreateBucket(key) {
9
+ let bucket = buckets.get(key);
10
+ if (!bucket) {
11
+ // Evict oldest entries if at capacity
12
+ if (buckets.size >= MAX_BUCKETS) {
13
+ const firstKey = buckets.keys().next().value;
14
+ if (firstKey !== undefined)
15
+ buckets.delete(firstKey);
16
+ }
17
+ bucket = { tokens: DEFAULT_MAX_TOKENS, lastRefill: Date.now() };
18
+ buckets.set(key, bucket);
19
+ }
20
+ return bucket;
21
+ }
22
+ function refill(bucket) {
23
+ const now = Date.now();
24
+ const elapsed = now - bucket.lastRefill;
25
+ if (elapsed > 0) {
26
+ const tokensToAdd = Math.floor((elapsed / DEFAULT_REFILL_INTERVAL_MS) * DEFAULT_MAX_TOKENS);
27
+ if (tokensToAdd > 0) {
28
+ bucket.tokens = Math.min(DEFAULT_MAX_TOKENS, bucket.tokens + tokensToAdd);
29
+ bucket.lastRefill = now;
30
+ }
31
+ }
32
+ }
33
+ // Cleanup stale buckets every 5 minutes to prevent unbounded Map growth
34
+ setInterval(() => {
35
+ const now = Date.now();
36
+ for (const [key, bucket] of buckets) {
37
+ if (now - bucket.lastRefill > 5 * 60_000) {
38
+ buckets.delete(key);
39
+ }
40
+ }
41
+ }, 5 * 60_000).unref();
42
+ /**
43
+ * Rate-limiting middleware for the event ingestion endpoint.
44
+ * Allows up to 1000 events per minute per API key.
45
+ * Counts each event in a batch individually.
46
+ */
47
+ function rateLimitEvents(req, res, next) {
48
+ // Extract the API key used for auth
49
+ const authHeader = req.headers["authorization"];
50
+ const apiKeyHeader = req.headers["x-api-key"];
51
+ const key = (typeof authHeader === "string" && authHeader.startsWith("Bearer ")
52
+ ? authHeader.slice(7)
53
+ : null) ??
54
+ (typeof apiKeyHeader === "string" ? apiKeyHeader : "anonymous");
55
+ const bucket = getOrCreateBucket(key);
56
+ refill(bucket);
57
+ // Count how many events are in this request
58
+ const body = req.body;
59
+ const eventCount = body && "events" in body && Array.isArray(body.events)
60
+ ? body.events.length
61
+ : 1;
62
+ if (bucket.tokens < eventCount) {
63
+ res.status(429).json({
64
+ error: "Rate limit exceeded. Maximum 1000 events per minute.",
65
+ retry_after_ms: DEFAULT_REFILL_INTERVAL_MS,
66
+ });
67
+ return;
68
+ }
69
+ bucket.tokens -= eventCount;
70
+ next();
71
+ }
72
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
3
+ //# sourceMappingURL=agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAwLxB,eAAe,MAAM,CAAC"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const db_js_1 = require("../db.js");
5
+ const router = (0, express_1.Router)();
6
+ router.get("/api/agents", (req, res) => {
7
+ const db = req.app.locals.db;
8
+ const limitStr = req.query.limit;
9
+ const offsetStr = req.query.offset;
10
+ const search = req.query.search;
11
+ // Get today's UTC midnight for today_cost calculation
12
+ const todayUTC = new Date();
13
+ todayUTC.setUTCHours(0, 0, 0, 0);
14
+ const todayStart = todayUTC.toISOString();
15
+ const hasPagination = limitStr || offsetStr || search;
16
+ if (!hasPagination) {
17
+ // Backwards compatible: return all agents with providers
18
+ const agents = (0, db_js_1.getAllAgents)(db);
19
+ const agentsWithProviders = agents.map((agent) => {
20
+ const providerRows = db.prepare(`
21
+ SELECT DISTINCT provider FROM agent_events
22
+ WHERE agent_id = ? AND provider IS NOT NULL
23
+ ORDER BY provider
24
+ `).all(agent.agent_id);
25
+ const modelRules = (0, db_js_1.getModelRulesForAgent)(db, agent.agent_id);
26
+ const overrideProviders = new Set(modelRules.filter(r => r.model_override).map(r => r.provider));
27
+ return {
28
+ ...agent,
29
+ providers: providerRows.map(r => ({
30
+ provider: r.provider,
31
+ has_override: overrideProviders.has(r.provider),
32
+ })),
33
+ };
34
+ });
35
+ res.json({ agents: agentsWithProviders });
36
+ return;
37
+ }
38
+ const limit = limitStr ? Math.min(Math.max(1, parseInt(limitStr, 10) || 20), 100) : 20;
39
+ const offset = offsetStr ? Math.max(0, parseInt(offsetStr, 10) || 0) : 0;
40
+ const conditions = [];
41
+ const params = [todayStart];
42
+ if (search) {
43
+ conditions.push("(a.agent_id LIKE ? OR a.name LIKE ?)");
44
+ const term = `%${search}%`;
45
+ params.push(term, term);
46
+ }
47
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
48
+ const countRow = db
49
+ .prepare(`SELECT COUNT(*) AS total FROM agents a ${where}`)
50
+ .get(...params.slice(1));
51
+ const rows = db
52
+ .prepare(`SELECT
53
+ a.id, a.agent_id, a.name, a.active, a.budget_limit,
54
+ a.created_at, a.updated_at,
55
+ COALESCE(SUM(e.tokens_total), 0) AS total_tokens,
56
+ COALESCE(SUM(e.cost_usd), 0) AS total_cost,
57
+ COALESCE(SUM(CASE WHEN e.timestamp >= ? THEN e.cost_usd ELSE 0 END), 0) AS today_cost
58
+ FROM agents a
59
+ LEFT JOIN agent_events e ON e.agent_id = a.agent_id
60
+ ${where}
61
+ GROUP BY a.id
62
+ ORDER BY a.updated_at DESC
63
+ LIMIT ? OFFSET ?`)
64
+ .all(...params, limit, offset);
65
+ // Add providers to each agent
66
+ const agentsWithProviders = rows.map((agent) => {
67
+ const providerRows = db.prepare(`
68
+ SELECT DISTINCT provider FROM agent_events
69
+ WHERE agent_id = ? AND provider IS NOT NULL
70
+ ORDER BY provider
71
+ `).all(agent.agent_id);
72
+ const modelRules = (0, db_js_1.getModelRulesForAgent)(db, agent.agent_id);
73
+ const overrideProviders = new Set(modelRules.filter(r => r.model_override).map(r => r.provider));
74
+ return {
75
+ ...agent,
76
+ providers: providerRows.map(r => ({
77
+ provider: r.provider,
78
+ has_override: overrideProviders.has(r.provider),
79
+ })),
80
+ };
81
+ });
82
+ res.json({ agents: agentsWithProviders, total: countRow.total });
83
+ });
84
+ router.get("/api/agents/:agentId", (req, res) => {
85
+ const db = req.app.locals.db;
86
+ const { agentId } = req.params;
87
+ const agent = (0, db_js_1.getAgentByAgentId)(db, agentId);
88
+ if (!agent) {
89
+ res.status(404).json({ error: "Agent not found" });
90
+ return;
91
+ }
92
+ res.json(agent);
93
+ });
94
+ router.get("/api/agents/:agentId/policy", (req, res) => {
95
+ const db = req.app.locals.db;
96
+ const { agentId } = req.params;
97
+ const policy = (0, db_js_1.getAgentPolicy)(db, agentId);
98
+ if (!policy) {
99
+ res.status(404).json({ error: "Agent not found" });
100
+ return;
101
+ }
102
+ const dailySpend = (0, db_js_1.getDailySpend)(db, agentId);
103
+ res.json({
104
+ ...policy,
105
+ daily_spend: dailySpend,
106
+ });
107
+ });
108
+ router.put("/api/agents/:agentId/policy", (req, res) => {
109
+ const db = req.app.locals.db;
110
+ const { agentId } = req.params;
111
+ const body = req.body;
112
+ // Check agent exists
113
+ const existing = (0, db_js_1.getAgentPolicy)(db, agentId);
114
+ if (!existing) {
115
+ res.status(404).json({ error: "Agent not found" });
116
+ return;
117
+ }
118
+ // Validate allowed_hours range
119
+ if (body.allowed_hours_start !== undefined && body.allowed_hours_start !== null) {
120
+ if (body.allowed_hours_start < 0 || body.allowed_hours_start > 23) {
121
+ res.status(400).json({ error: "allowed_hours_start must be between 0 and 23" });
122
+ return;
123
+ }
124
+ }
125
+ if (body.allowed_hours_end !== undefined && body.allowed_hours_end !== null) {
126
+ if (body.allowed_hours_end < 0 || body.allowed_hours_end > 23) {
127
+ res.status(400).json({ error: "allowed_hours_end must be between 0 and 23" });
128
+ return;
129
+ }
130
+ }
131
+ const updated = (0, db_js_1.updateAgentPolicy)(db, agentId, {
132
+ active: body.active,
133
+ budget_limit: body.budget_limit,
134
+ allowed_hours_start: body.allowed_hours_start,
135
+ allowed_hours_end: body.allowed_hours_end,
136
+ });
137
+ if (!updated) {
138
+ res.status(400).json({ error: "No changes provided" });
139
+ return;
140
+ }
141
+ const newPolicy = (0, db_js_1.getAgentPolicy)(db, agentId);
142
+ const dailySpend = (0, db_js_1.getDailySpend)(db, agentId);
143
+ res.json({
144
+ ...newPolicy,
145
+ daily_spend: dailySpend,
146
+ });
147
+ });
148
+ exports.default = router;
149
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
3
+ //# sourceMappingURL=alerts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/routes/alerts.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuYxB,eAAe,MAAM,CAAC"}
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const express_1 = require("express");
4
+ const node_crypto_1 = require("node:crypto");
5
+ const router = (0, express_1.Router)();
6
+ function parseAlertRule(row) {
7
+ let config = row.config;
8
+ if (typeof row.config === "string") {
9
+ try {
10
+ config = JSON.parse(row.config);
11
+ }
12
+ catch { /* keep as string */ }
13
+ }
14
+ let smtpConfig = row.smtp_config;
15
+ if (row.smtp_config && typeof row.smtp_config === "string") {
16
+ try {
17
+ smtpConfig = JSON.parse(row.smtp_config);
18
+ }
19
+ catch { /* keep as string */ }
20
+ }
21
+ let telegramConfig = row.telegram_config;
22
+ if (row.telegram_config && typeof row.telegram_config === "string") {
23
+ try {
24
+ telegramConfig = JSON.parse(row.telegram_config);
25
+ }
26
+ catch { /* keep as string */ }
27
+ }
28
+ return {
29
+ id: row.id,
30
+ agent_id: row.agent_id,
31
+ rule_type: row.rule_type,
32
+ config,
33
+ enabled: row.enabled === 1,
34
+ notification_type: row.notification_type || "webhook",
35
+ webhook_url: row.webhook_url,
36
+ email: row.email,
37
+ smtp_config: smtpConfig,
38
+ telegram_config: telegramConfig,
39
+ created_at: row.created_at,
40
+ updated_at: row.updated_at,
41
+ };
42
+ }
43
+ function parseAlertHistory(row) {
44
+ let data = row.data;
45
+ if (row.data && typeof row.data === "string") {
46
+ try {
47
+ data = JSON.parse(row.data);
48
+ }
49
+ catch { /* keep as string */ }
50
+ }
51
+ return {
52
+ ...row,
53
+ data,
54
+ };
55
+ }
56
+ // GET /api/alerts - List all alert rules (with optional pagination + filters)
57
+ router.get("/api/alerts", (req, res) => {
58
+ const db = req.app.locals.db;
59
+ const limitStr = req.query.limit;
60
+ const offsetStr = req.query.offset;
61
+ const agentId = req.query.agent_id;
62
+ const ruleType = req.query.rule_type;
63
+ const hasPagination = limitStr || offsetStr || agentId || ruleType;
64
+ if (!hasPagination) {
65
+ // Backwards compatible
66
+ const rows = db
67
+ .prepare("SELECT * FROM alert_rules ORDER BY created_at DESC")
68
+ .all();
69
+ const alerts = rows.map(parseAlertRule);
70
+ res.json({ alerts });
71
+ return;
72
+ }
73
+ const limit = limitStr ? Math.min(Math.max(1, parseInt(limitStr, 10) || 20), 100) : 20;
74
+ const offset = offsetStr ? Math.max(0, parseInt(offsetStr, 10) || 0) : 0;
75
+ const conditions = [];
76
+ const params = [];
77
+ if (agentId) {
78
+ conditions.push("agent_id = ?");
79
+ params.push(agentId);
80
+ }
81
+ if (ruleType) {
82
+ conditions.push("rule_type = ?");
83
+ params.push(ruleType);
84
+ }
85
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
86
+ const countRow = db
87
+ .prepare(`SELECT COUNT(*) AS total FROM alert_rules ${where}`)
88
+ .get(...params);
89
+ const rows = db
90
+ .prepare(`SELECT * FROM alert_rules ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`)
91
+ .all(...params, limit, offset);
92
+ const alerts = rows.map(parseAlertRule);
93
+ res.json({ alerts, total: countRow.total });
94
+ });
95
+ // POST /api/alerts - Create a new alert rule
96
+ router.post("/api/alerts", (req, res) => {
97
+ const db = req.app.locals.db;
98
+ const body = req.body;
99
+ // Validate required fields
100
+ if (!body.agent_id || typeof body.agent_id !== "string") {
101
+ res.status(400).json({ error: "agent_id is required" });
102
+ return;
103
+ }
104
+ if (!body.rule_type || typeof body.rule_type !== "string") {
105
+ res.status(400).json({ error: "rule_type is required" });
106
+ return;
107
+ }
108
+ const validRuleTypes = new Set(["agent_down", "error_rate", "budget"]);
109
+ if (!validRuleTypes.has(body.rule_type)) {
110
+ res.status(400).json({ error: `rule_type must be one of: ${[...validRuleTypes].join(", ")}` });
111
+ return;
112
+ }
113
+ if (body.config === undefined || body.config === null || typeof body.config !== "object") {
114
+ res.status(400).json({ error: "config is required and must be an object" });
115
+ return;
116
+ }
117
+ const notificationType = body.notification_type || "webhook";
118
+ const validNotificationTypes = new Set(["webhook", "email", "telegram"]);
119
+ if (!validNotificationTypes.has(notificationType)) {
120
+ res.status(400).json({ error: `notification_type must be one of: ${[...validNotificationTypes].join(", ")}` });
121
+ return;
122
+ }
123
+ // Validate notification-specific fields
124
+ if (notificationType === "webhook") {
125
+ if (!body.webhook_url) {
126
+ res.status(400).json({ error: "webhook_url is required for webhook notification" });
127
+ return;
128
+ }
129
+ try {
130
+ const parsed = new URL(body.webhook_url);
131
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
132
+ res.status(400).json({ error: "webhook_url must use http or https protocol" });
133
+ return;
134
+ }
135
+ }
136
+ catch {
137
+ res.status(400).json({ error: "webhook_url must be a valid URL" });
138
+ return;
139
+ }
140
+ }
141
+ else if (notificationType === "email") {
142
+ if (!body.smtp_config) {
143
+ res.status(400).json({ error: "smtp_config is required for email notification" });
144
+ return;
145
+ }
146
+ const smtp = body.smtp_config;
147
+ if (!smtp.host || !smtp.from || !smtp.to) {
148
+ res.status(400).json({ error: "smtp_config requires host, from, and to fields" });
149
+ return;
150
+ }
151
+ }
152
+ else if (notificationType === "telegram") {
153
+ if (!body.telegram_config) {
154
+ res.status(400).json({ error: "telegram_config is required for telegram notification" });
155
+ return;
156
+ }
157
+ const tg = body.telegram_config;
158
+ if (!tg.bot_token || !tg.chat_id) {
159
+ res.status(400).json({ error: "telegram_config requires bot_token and chat_id fields" });
160
+ return;
161
+ }
162
+ }
163
+ const id = (0, node_crypto_1.randomUUID)();
164
+ const now = new Date().toISOString();
165
+ const enabled = body.enabled !== undefined ? (body.enabled ? 1 : 0) : 1;
166
+ db.prepare(`INSERT INTO alert_rules (id, agent_id, rule_type, config, enabled, notification_type, webhook_url, email, smtp_config, telegram_config, created_at, updated_at)
167
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, body.agent_id, body.rule_type, JSON.stringify(body.config), enabled, notificationType, body.webhook_url ?? null, body.email ?? null, body.smtp_config ? JSON.stringify(body.smtp_config) : null, body.telegram_config ? JSON.stringify(body.telegram_config) : null, now, now);
168
+ const row = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
169
+ res.status(201).json(parseAlertRule(row));
170
+ });
171
+ // PUT /api/alerts/:id - Update an alert rule
172
+ router.put("/api/alerts/:id", (req, res) => {
173
+ const db = req.app.locals.db;
174
+ const { id } = req.params;
175
+ const body = req.body;
176
+ const existing = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
177
+ if (!existing) {
178
+ res.status(404).json({ error: "Alert rule not found" });
179
+ return;
180
+ }
181
+ const now = new Date().toISOString();
182
+ const agentId = body.agent_id ?? existing.agent_id;
183
+ const ruleType = body.rule_type ?? existing.rule_type;
184
+ const config = body.config !== undefined ? JSON.stringify(body.config) : existing.config;
185
+ const enabled = body.enabled !== undefined ? (body.enabled ? 1 : 0) : existing.enabled;
186
+ const notificationType = body.notification_type ?? existing.notification_type ?? "webhook";
187
+ // Validate webhook_url if provided
188
+ if (body.webhook_url !== undefined && body.webhook_url !== null && body.webhook_url !== "") {
189
+ try {
190
+ const parsed = new URL(body.webhook_url);
191
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
192
+ res.status(400).json({ error: "webhook_url must use http or https protocol" });
193
+ return;
194
+ }
195
+ }
196
+ catch {
197
+ res.status(400).json({ error: "webhook_url must be a valid URL" });
198
+ return;
199
+ }
200
+ }
201
+ const webhookUrl = body.webhook_url !== undefined ? (body.webhook_url || null) : existing.webhook_url;
202
+ const email = body.email !== undefined ? body.email : existing.email;
203
+ const smtpConfig = body.smtp_config !== undefined
204
+ ? (body.smtp_config ? JSON.stringify(body.smtp_config) : null)
205
+ : existing.smtp_config;
206
+ const telegramConfig = body.telegram_config !== undefined
207
+ ? (body.telegram_config ? JSON.stringify(body.telegram_config) : null)
208
+ : existing.telegram_config;
209
+ db.prepare(`UPDATE alert_rules
210
+ SET agent_id = ?, rule_type = ?, config = ?, enabled = ?, notification_type = ?, webhook_url = ?, email = ?, smtp_config = ?, telegram_config = ?, updated_at = ?
211
+ WHERE id = ?`).run(agentId, ruleType, config, enabled, notificationType, webhookUrl, email, smtpConfig, telegramConfig, now, id);
212
+ const row = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
213
+ res.json(parseAlertRule(row));
214
+ });
215
+ // DELETE /api/alerts/:id - Delete an alert rule
216
+ router.delete("/api/alerts/:id", (req, res) => {
217
+ const db = req.app.locals.db;
218
+ const { id } = req.params;
219
+ const existing = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
220
+ if (!existing) {
221
+ res.status(404).json({ error: "Alert rule not found" });
222
+ return;
223
+ }
224
+ db.prepare("DELETE FROM alert_rules WHERE id = ?").run(id);
225
+ res.status(204).send();
226
+ });
227
+ // PATCH /api/alerts/:id/toggle - Toggle alert rule enabled status
228
+ router.patch("/api/alerts/:id/toggle", (req, res) => {
229
+ const db = req.app.locals.db;
230
+ const { id } = req.params;
231
+ const body = req.body;
232
+ const existing = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
233
+ if (!existing) {
234
+ res.status(404).json({ error: "Alert rule not found" });
235
+ return;
236
+ }
237
+ if (body.enabled === undefined || typeof body.enabled !== "boolean") {
238
+ res.status(400).json({ error: "enabled field (boolean) is required" });
239
+ return;
240
+ }
241
+ const now = new Date().toISOString();
242
+ db.prepare("UPDATE alert_rules SET enabled = ?, updated_at = ? WHERE id = ?").run(body.enabled ? 1 : 0, now, id);
243
+ const row = db.prepare("SELECT * FROM alert_rules WHERE id = ?").get(id);
244
+ res.json(parseAlertRule(row));
245
+ });
246
+ // GET /api/alert-history - List alert history (with optional pagination + filters)
247
+ router.get("/api/alert-history", (req, res) => {
248
+ const db = req.app.locals.db;
249
+ const limitStr = req.query.limit;
250
+ const offsetStr = req.query.offset;
251
+ const agentId = req.query.agent_id;
252
+ const ruleType = req.query.rule_type;
253
+ const limit = limitStr ? Math.min(Math.max(1, parseInt(limitStr, 10) || 100), 10_000) : 100;
254
+ const hasPagination = offsetStr || agentId || ruleType;
255
+ if (!hasPagination) {
256
+ // Backwards compatible
257
+ const rows = db
258
+ .prepare("SELECT * FROM alert_history ORDER BY delivered_at DESC LIMIT ?")
259
+ .all(limit);
260
+ const history = rows.map(parseAlertHistory);
261
+ res.json({ history });
262
+ return;
263
+ }
264
+ const offset = offsetStr ? Math.max(0, parseInt(offsetStr, 10) || 0) : 0;
265
+ const conditions = [];
266
+ const params = [];
267
+ if (agentId) {
268
+ conditions.push("agent_id = ?");
269
+ params.push(agentId);
270
+ }
271
+ if (ruleType) {
272
+ conditions.push("rule_type = ?");
273
+ params.push(ruleType);
274
+ }
275
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
276
+ const countRow = db
277
+ .prepare(`SELECT COUNT(*) AS total FROM alert_history ${where}`)
278
+ .get(...params);
279
+ const rows = db
280
+ .prepare(`SELECT * FROM alert_history ${where} ORDER BY delivered_at DESC LIMIT ? OFFSET ?`)
281
+ .all(...params, limit, offset);
282
+ const history = rows.map(parseAlertHistory);
283
+ res.json({ history, total: countRow.total });
284
+ });
285
+ exports.default = router;
286
+ //# sourceMappingURL=alerts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/routes/alerts.ts"],"names":[],"mappings":"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"}