@timmeck/brain-core 2.1.0 → 2.3.0

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.
@@ -0,0 +1,62 @@
1
+ export interface RateLimitConfig {
2
+ windowMs?: number;
3
+ maxRequests?: number;
4
+ keyExtractor?: (req: {
5
+ socket: {
6
+ remoteAddress?: string;
7
+ };
8
+ }) => string;
9
+ }
10
+ export declare class RateLimiter {
11
+ private store;
12
+ private windowMs;
13
+ private maxRequests;
14
+ private keyExtractor;
15
+ private cleanupTimer;
16
+ constructor(config?: RateLimitConfig);
17
+ /**
18
+ * Check if a request is allowed. Returns { allowed, remaining, resetAt }.
19
+ */
20
+ check(req: {
21
+ socket: {
22
+ remoteAddress?: string;
23
+ };
24
+ }): {
25
+ allowed: boolean;
26
+ remaining: number;
27
+ resetAt: number;
28
+ };
29
+ /** Reset a specific key (for testing) */
30
+ reset(key: string): void;
31
+ /** Clear all entries */
32
+ clear(): void;
33
+ /** Stop cleanup timer */
34
+ stop(): void;
35
+ private cleanup;
36
+ }
37
+ export interface SizeLimitConfig {
38
+ maxBodyBytes?: number;
39
+ }
40
+ /**
41
+ * Read request body with size limit enforcement.
42
+ * Returns the body string or null if the limit is exceeded.
43
+ */
44
+ export declare function readBodyWithLimit(req: import('node:http').IncomingMessage, config?: SizeLimitConfig): Promise<{
45
+ body: string;
46
+ error?: undefined;
47
+ } | {
48
+ body?: undefined;
49
+ error: string;
50
+ }>;
51
+ export interface SecurityHeadersConfig {
52
+ cors?: {
53
+ origins?: string[];
54
+ methods?: string[];
55
+ headers?: string[];
56
+ };
57
+ hsts?: boolean;
58
+ }
59
+ /**
60
+ * Apply security headers to an HTTP response.
61
+ */
62
+ export declare function applySecurityHeaders(res: import('node:http').ServerResponse, config?: SecurityHeadersConfig): void;
@@ -0,0 +1,108 @@
1
+ // ── Rate Limiter ────────────────────────────────────────
2
+ export class RateLimiter {
3
+ store = new Map();
4
+ windowMs;
5
+ maxRequests;
6
+ keyExtractor;
7
+ cleanupTimer;
8
+ constructor(config) {
9
+ this.windowMs = config?.windowMs ?? 60_000;
10
+ this.maxRequests = config?.maxRequests ?? 100;
11
+ this.keyExtractor = config?.keyExtractor ?? ((req) => req.socket.remoteAddress ?? 'unknown');
12
+ // Cleanup expired entries every minute
13
+ this.cleanupTimer = setInterval(() => this.cleanup(), 60_000);
14
+ }
15
+ /**
16
+ * Check if a request is allowed. Returns { allowed, remaining, resetAt }.
17
+ */
18
+ check(req) {
19
+ const key = this.keyExtractor(req);
20
+ const now = Date.now();
21
+ const entry = this.store.get(key);
22
+ if (!entry || now >= entry.resetAt) {
23
+ this.store.set(key, { count: 1, resetAt: now + this.windowMs });
24
+ return { allowed: true, remaining: this.maxRequests - 1, resetAt: now + this.windowMs };
25
+ }
26
+ entry.count++;
27
+ if (entry.count > this.maxRequests) {
28
+ return { allowed: false, remaining: 0, resetAt: entry.resetAt };
29
+ }
30
+ return { allowed: true, remaining: this.maxRequests - entry.count, resetAt: entry.resetAt };
31
+ }
32
+ /** Reset a specific key (for testing) */
33
+ reset(key) {
34
+ this.store.delete(key);
35
+ }
36
+ /** Clear all entries */
37
+ clear() {
38
+ this.store.clear();
39
+ }
40
+ /** Stop cleanup timer */
41
+ stop() {
42
+ clearInterval(this.cleanupTimer);
43
+ }
44
+ cleanup() {
45
+ const now = Date.now();
46
+ for (const [key, entry] of this.store) {
47
+ if (now >= entry.resetAt) {
48
+ this.store.delete(key);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ /**
54
+ * Read request body with size limit enforcement.
55
+ * Returns the body string or null if the limit is exceeded.
56
+ */
57
+ export function readBodyWithLimit(req, config) {
58
+ const maxBytes = config?.maxBodyBytes ?? 102_400;
59
+ return new Promise((resolve) => {
60
+ const chunks = [];
61
+ let totalBytes = 0;
62
+ let aborted = false;
63
+ req.on('data', (chunk) => {
64
+ if (aborted)
65
+ return;
66
+ totalBytes += chunk.length;
67
+ if (totalBytes > maxBytes) {
68
+ aborted = true;
69
+ req.destroy();
70
+ resolve({ error: `Request body exceeds limit of ${maxBytes} bytes` });
71
+ return;
72
+ }
73
+ chunks.push(chunk);
74
+ });
75
+ req.on('end', () => {
76
+ if (aborted)
77
+ return;
78
+ resolve({ body: Buffer.concat(chunks).toString('utf8') });
79
+ });
80
+ req.on('error', () => {
81
+ if (aborted)
82
+ return;
83
+ resolve({ error: 'Request read error' });
84
+ });
85
+ });
86
+ }
87
+ /**
88
+ * Apply security headers to an HTTP response.
89
+ */
90
+ export function applySecurityHeaders(res, config) {
91
+ // CORS
92
+ const origins = config?.cors?.origins ?? ['*'];
93
+ const methods = config?.cors?.methods ?? ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'];
94
+ const headers = config?.cors?.headers ?? ['Content-Type', 'Authorization', 'X-API-Key'];
95
+ res.setHeader('Access-Control-Allow-Origin', origins.join(', '));
96
+ res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
97
+ res.setHeader('Access-Control-Allow-Headers', headers.join(', '));
98
+ // Security headers
99
+ res.setHeader('X-Content-Type-Options', 'nosniff');
100
+ res.setHeader('X-Frame-Options', 'DENY');
101
+ res.setHeader('X-XSS-Protection', '1; mode=block');
102
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
103
+ // HSTS (only when explicitly enabled — requires HTTPS)
104
+ if (config?.hsts) {
105
+ res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
106
+ }
107
+ }
108
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/api/middleware.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,15 @@
1
1
  import http from 'node:http';
2
2
  import type { IpcRouter } from '../ipc/server.js';
3
+ import type { RateLimitConfig, SizeLimitConfig, SecurityHeadersConfig } from './middleware.js';
3
4
  export interface ApiServerOptions {
4
5
  port: number;
5
6
  router: IpcRouter;
6
7
  apiKey?: string;
8
+ rateLimit?: RateLimitConfig;
9
+ sizeLimit?: SizeLimitConfig;
10
+ security?: SecurityHeadersConfig;
11
+ /** Callback for deep health checks (DB, engines, etc.) */
12
+ healthCheck?: () => Record<string, unknown>;
7
13
  }
8
14
  export interface RouteDefinition {
9
15
  method: string;
@@ -17,6 +23,7 @@ export declare class BaseApiServer {
17
23
  protected logger: import("winston").Logger;
18
24
  private routes;
19
25
  protected sseClients: Set<http.ServerResponse>;
26
+ private rateLimiter;
20
27
  constructor(options: ApiServerOptions);
21
28
  start(): void;
22
29
  stop(): void;
@@ -1,26 +1,42 @@
1
1
  import http from 'node:http';
2
2
  import { getLogger } from '../utils/logger.js';
3
+ import { RateLimiter, readBodyWithLimit, applySecurityHeaders } from './middleware.js';
4
+ import { validateParams } from '../ipc/validation.js';
5
+ import { ValidationError } from '../ipc/errors.js';
3
6
  export class BaseApiServer {
4
7
  options;
5
8
  server = null;
6
9
  logger = getLogger();
7
10
  routes;
8
11
  sseClients = new Set();
12
+ rateLimiter;
9
13
  constructor(options) {
10
14
  this.options = options;
11
15
  this.routes = this.buildRoutes();
16
+ this.rateLimiter = new RateLimiter(options.rateLimit);
12
17
  }
13
18
  start() {
14
19
  const { port, apiKey } = this.options;
15
20
  this.server = http.createServer((req, res) => {
16
- res.setHeader('Access-Control-Allow-Origin', '*');
17
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
18
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key');
21
+ // Security headers
22
+ applySecurityHeaders(res, this.options.security);
19
23
  if (req.method === 'OPTIONS') {
20
24
  res.writeHead(204);
21
25
  res.end();
22
26
  return;
23
27
  }
28
+ // Rate limiting (skip health check)
29
+ const url = new URL(req.url ?? '/', 'http://localhost');
30
+ if (url.pathname !== '/api/v1/health' && url.pathname !== '/api/v1/ready') {
31
+ const limit = this.rateLimiter.check(req);
32
+ res.setHeader('X-RateLimit-Remaining', String(limit.remaining));
33
+ res.setHeader('X-RateLimit-Reset', String(Math.ceil(limit.resetAt / 1000)));
34
+ if (!limit.allowed) {
35
+ this.json(res, 429, { error: 'Too Many Requests', message: 'Rate limit exceeded' });
36
+ return;
37
+ }
38
+ }
39
+ // API key auth
24
40
  if (apiKey) {
25
41
  const provided = req.headers['x-api-key'] ??
26
42
  req.headers.authorization?.replace('Bearer ', '');
@@ -42,6 +58,7 @@ export class BaseApiServer {
42
58
  });
43
59
  }
44
60
  stop() {
61
+ this.rateLimiter.stop();
45
62
  for (const client of this.sseClients) {
46
63
  try {
47
64
  client.end();
@@ -62,9 +79,18 @@ export class BaseApiServer {
62
79
  const pathname = url.pathname;
63
80
  const method = req.method ?? 'GET';
64
81
  const query = url.searchParams;
65
- // Health check
82
+ // Health check (deep)
66
83
  if (pathname === '/api/v1/health') {
67
- this.json(res, 200, { status: 'ok', timestamp: new Date().toISOString() });
84
+ const base = { status: 'ok', timestamp: new Date().toISOString(), uptime: Math.floor(process.uptime()), memory: Math.floor(process.memoryUsage().rss / 1024 / 1024) };
85
+ const extra = this.options.healthCheck?.() ?? {};
86
+ this.json(res, 200, { ...base, ...extra });
87
+ return;
88
+ }
89
+ // Readiness probe (for Kubernetes)
90
+ if (pathname === '/api/v1/ready') {
91
+ const extra = this.options.healthCheck?.() ?? {};
92
+ const ready = extra.db !== false && extra.ipc !== false;
93
+ this.json(res, ready ? 200 : 503, { ready, timestamp: new Date().toISOString(), ...extra });
68
94
  return;
69
95
  }
70
96
  // SSE event stream
@@ -91,21 +117,27 @@ export class BaseApiServer {
91
117
  }
92
118
  // Generic RPC endpoint
93
119
  if (pathname === '/api/v1/rpc' && method === 'POST') {
94
- const body = await this.readBody(req);
95
- if (!body) {
120
+ const bodyResult = await readBodyWithLimit(req, this.options.sizeLimit);
121
+ if (bodyResult.error) {
122
+ this.json(res, 413, { error: 'Payload Too Large', message: bodyResult.error });
123
+ return;
124
+ }
125
+ if (!bodyResult.body) {
96
126
  this.json(res, 400, { error: 'Bad Request', message: 'Empty request body' });
97
127
  return;
98
128
  }
99
- const parsed = JSON.parse(body);
129
+ const parsed = JSON.parse(bodyResult.body);
100
130
  // Batch RPC support
101
131
  if (Array.isArray(parsed)) {
102
132
  const results = parsed.map((call) => {
103
133
  try {
104
- const result = this.options.router.handle(call.method, call.params ?? {});
134
+ const validated = validateParams(call.params);
135
+ const result = this.options.router.handle(call.method, validated);
105
136
  return { id: call.id, result };
106
137
  }
107
138
  catch (err) {
108
- return { id: call.id, error: err instanceof Error ? err.message : String(err) };
139
+ const code = err instanceof ValidationError ? 'VALIDATION_ERROR' : 'ERROR';
140
+ return { id: call.id, error: err instanceof Error ? err.message : String(err), code };
109
141
  }
110
142
  });
111
143
  this.json(res, 200, results);
@@ -116,11 +148,13 @@ export class BaseApiServer {
116
148
  return;
117
149
  }
118
150
  try {
119
- const result = this.options.router.handle(parsed.method, parsed.params ?? {});
151
+ const validated = validateParams(parsed.params);
152
+ const result = this.options.router.handle(parsed.method, validated);
120
153
  this.json(res, 200, { result });
121
154
  }
122
155
  catch (err) {
123
- this.json(res, 400, { error: err instanceof Error ? err.message : String(err) });
156
+ const status = err instanceof ValidationError ? 400 : 400;
157
+ this.json(res, status, { error: err instanceof Error ? err.message : String(err) });
124
158
  }
125
159
  return;
126
160
  }
@@ -128,8 +162,12 @@ export class BaseApiServer {
128
162
  let body = undefined;
129
163
  if (method === 'POST' || method === 'PUT') {
130
164
  try {
131
- const raw = await this.readBody(req);
132
- body = raw ? JSON.parse(raw) : {};
165
+ const bodyResult = await readBodyWithLimit(req, this.options.sizeLimit);
166
+ if (bodyResult.error) {
167
+ this.json(res, 413, { error: 'Payload Too Large', message: bodyResult.error });
168
+ return;
169
+ }
170
+ body = bodyResult.body ? JSON.parse(bodyResult.body) : {};
133
171
  }
134
172
  catch {
135
173
  this.json(res, 400, { error: 'Bad Request', message: 'Invalid JSON body' });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface BackupConfig {
3
+ backupDir?: string;
4
+ maxBackups?: number;
5
+ compress?: boolean;
6
+ }
7
+ export interface BackupRecord {
8
+ filename: string;
9
+ path: string;
10
+ size: number;
11
+ created: string;
12
+ }
13
+ export interface RestoreResult {
14
+ success: boolean;
15
+ filename: string;
16
+ integrityOk: boolean;
17
+ }
18
+ export declare class BackupService {
19
+ private db;
20
+ private dbPath;
21
+ private logger;
22
+ private backupDir;
23
+ private maxBackups;
24
+ constructor(db: Database.Database, dbPath: string, config?: BackupConfig);
25
+ /** Create a backup of the current database. */
26
+ create(label?: string): BackupRecord;
27
+ /** List all available backups (newest first). */
28
+ list(): BackupRecord[];
29
+ /**
30
+ * Restore a database from a backup.
31
+ * CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
32
+ */
33
+ restore(filename: string): RestoreResult;
34
+ /** Check integrity of a database file. */
35
+ checkIntegrity(dbFilePath?: string): boolean;
36
+ /** Get current database size info. */
37
+ getInfo(): {
38
+ dbSize: number;
39
+ walSize: number;
40
+ backupCount: number;
41
+ backupTotalSize: number;
42
+ };
43
+ /** Delete a specific backup. */
44
+ delete(filename: string): boolean;
45
+ /** Auto-cleanup oldest backups beyond maxBackups. */
46
+ private autoCleanup;
47
+ private formatSize;
48
+ }
@@ -0,0 +1,153 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { getLogger } from '../utils/logger.js';
5
+ const require = createRequire(import.meta.url);
6
+ // ── Service ─────────────────────────────────────────────
7
+ export class BackupService {
8
+ db;
9
+ dbPath;
10
+ logger = getLogger();
11
+ backupDir;
12
+ maxBackups;
13
+ constructor(db, dbPath, config) {
14
+ this.db = db;
15
+ this.dbPath = dbPath;
16
+ this.backupDir = config?.backupDir ?? path.join(path.dirname(dbPath), 'backups');
17
+ this.maxBackups = config?.maxBackups ?? 10;
18
+ fs.mkdirSync(this.backupDir, { recursive: true });
19
+ }
20
+ /** Create a backup of the current database. */
21
+ create(label) {
22
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
23
+ const suffix = label ? `-${label.replace(/[^a-zA-Z0-9-_]/g, '')}` : '';
24
+ const filename = `backup-${timestamp}${suffix}.db`;
25
+ const backupPath = path.join(this.backupDir, filename);
26
+ // Checkpoint WAL before copying for a consistent snapshot
27
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
28
+ // Copy DB file synchronously
29
+ fs.copyFileSync(this.dbPath, backupPath);
30
+ // Verify integrity
31
+ const integrityOk = this.checkIntegrity(backupPath);
32
+ if (!integrityOk) {
33
+ this.logger.warn(`Backup integrity check failed: ${filename}`);
34
+ fs.unlinkSync(backupPath);
35
+ throw new Error('Backup integrity check failed');
36
+ }
37
+ this.logger.info(`Backup created: ${filename} (${this.formatSize(fs.statSync(backupPath).size)})`);
38
+ // Auto-cleanup old backups
39
+ this.autoCleanup();
40
+ const stat = fs.statSync(backupPath);
41
+ return {
42
+ filename,
43
+ path: backupPath,
44
+ size: stat.size,
45
+ created: stat.mtime.toISOString(),
46
+ };
47
+ }
48
+ /** List all available backups (newest first). */
49
+ list() {
50
+ if (!fs.existsSync(this.backupDir))
51
+ return [];
52
+ const files = fs.readdirSync(this.backupDir)
53
+ .filter(f => f.startsWith('backup-') && f.endsWith('.db'))
54
+ .sort()
55
+ .reverse();
56
+ return files.map(filename => {
57
+ const filePath = path.join(this.backupDir, filename);
58
+ const stat = fs.statSync(filePath);
59
+ return {
60
+ filename,
61
+ path: filePath,
62
+ size: stat.size,
63
+ created: stat.mtime.toISOString(),
64
+ };
65
+ });
66
+ }
67
+ /**
68
+ * Restore a database from a backup.
69
+ * CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
70
+ */
71
+ restore(filename) {
72
+ const backupPath = path.join(this.backupDir, filename);
73
+ if (!fs.existsSync(backupPath)) {
74
+ throw new Error(`Backup not found: ${filename}`);
75
+ }
76
+ // Verify backup integrity before restore
77
+ const integrityOk = this.checkIntegrity(backupPath);
78
+ if (!integrityOk) {
79
+ throw new Error(`Backup integrity check failed: ${filename}`);
80
+ }
81
+ // Create a safety backup of current DB before overwriting
82
+ const safetyName = `pre-restore-${new Date().toISOString().replace(/[:.]/g, '-')}.db`;
83
+ const safetyPath = path.join(this.backupDir, safetyName);
84
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
85
+ fs.copyFileSync(this.dbPath, safetyPath);
86
+ this.logger.info(`Safety backup created: ${safetyName}`);
87
+ // Close current DB, copy backup over, caller must reopen
88
+ this.db.close();
89
+ fs.copyFileSync(backupPath, this.dbPath);
90
+ // Clean up WAL/SHM files if they exist
91
+ try {
92
+ fs.unlinkSync(this.dbPath + '-wal');
93
+ }
94
+ catch { /* may not exist */ }
95
+ try {
96
+ fs.unlinkSync(this.dbPath + '-shm');
97
+ }
98
+ catch { /* may not exist */ }
99
+ this.logger.info(`Database restored from: ${filename}`);
100
+ return { success: true, filename, integrityOk };
101
+ }
102
+ /** Check integrity of a database file. */
103
+ checkIntegrity(dbFilePath) {
104
+ const filePath = dbFilePath ?? this.dbPath;
105
+ try {
106
+ const BetterSqlite3 = require('better-sqlite3');
107
+ const testDb = new BetterSqlite3(filePath, { readonly: true });
108
+ const result = testDb.pragma('integrity_check');
109
+ testDb.close();
110
+ return result.length === 1 && result[0].integrity_check === 'ok';
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ /** Get current database size info. */
117
+ getInfo() {
118
+ const dbSize = fs.existsSync(this.dbPath) ? fs.statSync(this.dbPath).size : 0;
119
+ const walPath = this.dbPath + '-wal';
120
+ const walSize = fs.existsSync(walPath) ? fs.statSync(walPath).size : 0;
121
+ const backups = this.list();
122
+ const backupTotalSize = backups.reduce((sum, b) => sum + b.size, 0);
123
+ return { dbSize, walSize, backupCount: backups.length, backupTotalSize };
124
+ }
125
+ /** Delete a specific backup. */
126
+ delete(filename) {
127
+ const filePath = path.join(this.backupDir, filename);
128
+ if (!fs.existsSync(filePath))
129
+ return false;
130
+ fs.unlinkSync(filePath);
131
+ this.logger.info(`Backup deleted: ${filename}`);
132
+ return true;
133
+ }
134
+ /** Auto-cleanup oldest backups beyond maxBackups. */
135
+ autoCleanup() {
136
+ const backups = this.list(); // sorted newest first
137
+ if (backups.length <= this.maxBackups)
138
+ return;
139
+ const toDelete = backups.slice(this.maxBackups);
140
+ for (const backup of toDelete) {
141
+ this.delete(backup.filename);
142
+ }
143
+ this.logger.info(`Auto-cleaned ${toDelete.length} old backup(s)`);
144
+ }
145
+ formatSize(bytes) {
146
+ if (bytes < 1024)
147
+ return `${bytes}B`;
148
+ if (bytes < 1024 * 1024)
149
+ return `${(bytes / 1024).toFixed(1)}KB`;
150
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
151
+ }
152
+ }
153
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/backup/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface ExportOptions {
3
+ table: string;
4
+ format: 'json' | 'csv';
5
+ columns?: string[];
6
+ where?: string;
7
+ orderBy?: string;
8
+ limit?: number;
9
+ dateColumn?: string;
10
+ dateFrom?: string;
11
+ dateTo?: string;
12
+ }
13
+ export interface ExportResult {
14
+ table: string;
15
+ format: 'json' | 'csv';
16
+ rowCount: number;
17
+ data: string;
18
+ }
19
+ export declare class ExportService {
20
+ private db;
21
+ private logger;
22
+ constructor(db: Database.Database);
23
+ /** Get list of all tables in the database. */
24
+ listTables(): string[];
25
+ /** Get column info for a table. */
26
+ getColumns(table: string): {
27
+ name: string;
28
+ type: string;
29
+ }[];
30
+ /** Export data from a table. */
31
+ export(options: ExportOptions): ExportResult;
32
+ /** Export multiple tables at once (JSON only). */
33
+ exportAll(tables?: string[], format?: 'json' | 'csv'): Record<string, ExportResult>;
34
+ /** Get row counts for all tables. */
35
+ getStats(): Record<string, number>;
36
+ /** Convert array of objects to CSV string. */
37
+ private toCsv;
38
+ /** Validate table name to prevent SQL injection. */
39
+ private validateTableName;
40
+ /** Validate column name. */
41
+ private validateColumnName;
42
+ }
@@ -0,0 +1,118 @@
1
+ import { getLogger } from '../utils/logger.js';
2
+ // ── Service ─────────────────────────────────────────────
3
+ export class ExportService {
4
+ db;
5
+ logger = getLogger();
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ /** Get list of all tables in the database. */
10
+ listTables() {
11
+ const rows = this.db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`).all();
12
+ return rows.map(r => r.name);
13
+ }
14
+ /** Get column info for a table. */
15
+ getColumns(table) {
16
+ this.validateTableName(table);
17
+ const rows = this.db.prepare(`PRAGMA table_info("${table}")`).all();
18
+ return rows.map(r => ({ name: r.name, type: r.type }));
19
+ }
20
+ /** Export data from a table. */
21
+ export(options) {
22
+ this.validateTableName(options.table);
23
+ const columns = options.columns?.length
24
+ ? options.columns.map(c => `"${c.replace(/"/g, '""')}"`).join(', ')
25
+ : '*';
26
+ const conditions = [];
27
+ const params = [];
28
+ if (options.where) {
29
+ conditions.push(`(${options.where})`);
30
+ }
31
+ if (options.dateColumn && options.dateFrom) {
32
+ this.validateColumnName(options.dateColumn);
33
+ conditions.push(`"${options.dateColumn}" >= ?`);
34
+ params.push(options.dateFrom);
35
+ }
36
+ if (options.dateColumn && options.dateTo) {
37
+ this.validateColumnName(options.dateColumn);
38
+ conditions.push(`"${options.dateColumn}" <= ?`);
39
+ params.push(options.dateTo);
40
+ }
41
+ let sql = `SELECT ${columns} FROM "${options.table}"`;
42
+ if (conditions.length > 0) {
43
+ sql += ` WHERE ${conditions.join(' AND ')}`;
44
+ }
45
+ if (options.orderBy) {
46
+ sql += ` ORDER BY ${options.orderBy}`;
47
+ }
48
+ if (options.limit) {
49
+ sql += ` LIMIT ?`;
50
+ params.push(options.limit);
51
+ }
52
+ const rows = this.db.prepare(sql).all(...params);
53
+ const data = options.format === 'csv'
54
+ ? this.toCsv(rows)
55
+ : JSON.stringify(rows, null, 2);
56
+ this.logger.info(`Exported ${rows.length} rows from ${options.table} as ${options.format}`);
57
+ return {
58
+ table: options.table,
59
+ format: options.format,
60
+ rowCount: rows.length,
61
+ data,
62
+ };
63
+ }
64
+ /** Export multiple tables at once (JSON only). */
65
+ exportAll(tables, format = 'json') {
66
+ const tableNames = tables ?? this.listTables();
67
+ const results = {};
68
+ for (const table of tableNames) {
69
+ results[table] = this.export({ table, format });
70
+ }
71
+ return results;
72
+ }
73
+ /** Get row counts for all tables. */
74
+ getStats() {
75
+ const tables = this.listTables();
76
+ const stats = {};
77
+ for (const table of tables) {
78
+ const row = this.db.prepare(`SELECT COUNT(*) as count FROM "${table}"`).get();
79
+ stats[table] = row.count;
80
+ }
81
+ return stats;
82
+ }
83
+ /** Convert array of objects to CSV string. */
84
+ toCsv(rows) {
85
+ if (rows.length === 0)
86
+ return '';
87
+ const headers = Object.keys(rows[0]);
88
+ const lines = [headers.join(',')];
89
+ for (const row of rows) {
90
+ const values = headers.map(h => {
91
+ const val = row[h];
92
+ if (val == null)
93
+ return '';
94
+ const str = String(val);
95
+ // Escape CSV: quote if contains comma, quote, or newline
96
+ if (str.includes(',') || str.includes('"') || str.includes('\n')) {
97
+ return `"${str.replace(/"/g, '""')}"`;
98
+ }
99
+ return str;
100
+ });
101
+ lines.push(values.join(','));
102
+ }
103
+ return lines.join('\n');
104
+ }
105
+ /** Validate table name to prevent SQL injection. */
106
+ validateTableName(name) {
107
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
108
+ throw new Error(`Invalid table name: ${name}`);
109
+ }
110
+ }
111
+ /** Validate column name. */
112
+ validateColumnName(name) {
113
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
114
+ throw new Error(`Invalid column name: ${name}`);
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/export/service.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,9 @@ export { encodeMessage, MessageDecoder } from './ipc/protocol.js';
9
9
  export { IpcServer } from './ipc/server.js';
10
10
  export type { IpcRouter } from './ipc/server.js';
11
11
  export { IpcClient } from './ipc/client.js';
12
+ export { validateParams, withValidation } from './ipc/validation.js';
13
+ export type { ValidationOptions } from './ipc/validation.js';
14
+ export { IpcError, ValidationError, NotFoundError, TimeoutError, ServiceUnavailableError } from './ipc/errors.js';
12
15
  export { startMcpServer } from './mcp/server.js';
13
16
  export type { McpServerOptions } from './mcp/server.js';
14
17
  export { McpHttpServer } from './mcp/http-server.js';
@@ -16,6 +19,8 @@ export type { McpHttpServerOptions } from './mcp/http-server.js';
16
19
  export { c, baseIcons, header, keyValue, statusBadge, progressBar, divider, table, stripAnsi } from './cli/colors.js';
17
20
  export { BaseApiServer } from './api/server.js';
18
21
  export type { ApiServerOptions, RouteDefinition } from './api/server.js';
22
+ export { RateLimiter, readBodyWithLimit, applySecurityHeaders } from './api/middleware.js';
23
+ export type { RateLimitConfig, SizeLimitConfig, SecurityHeadersConfig } from './api/middleware.js';
19
24
  export { wilsonScore } from './math/wilson-score.js';
20
25
  export { timeDecayFactor } from './math/time-decay.js';
21
26
  export { deepMerge, loadConfigFile } from './config/loader.js';
@@ -48,3 +53,9 @@ export { CrossBrainCorrelator } from './cross-brain/correlator.js';
48
53
  export type { CorrelatorEvent, Correlation, EcosystemHealth, CorrelatorConfig } from './cross-brain/correlator.js';
49
54
  export { EcosystemService } from './ecosystem/service.js';
50
55
  export type { BrainStatus, EcosystemStatus, AggregatedAnalytics } from './ecosystem/service.js';
56
+ export { WebhookService, runWebhookMigration } from './webhooks/service.js';
57
+ export type { WebhookConfig, WebhookRecord, DeliveryRecord, WebhookDeliveryResult } from './webhooks/service.js';
58
+ export { ExportService } from './export/service.js';
59
+ export type { ExportOptions, ExportResult } from './export/service.js';
60
+ export { BackupService } from './backup/service.js';
61
+ export type { BackupConfig, BackupRecord, RestoreResult } from './backup/service.js';
package/dist/index.js CHANGED
@@ -9,6 +9,8 @@ export { createConnection } from './db/connection.js';
9
9
  export { encodeMessage, MessageDecoder } from './ipc/protocol.js';
10
10
  export { IpcServer } from './ipc/server.js';
11
11
  export { IpcClient } from './ipc/client.js';
12
+ export { validateParams, withValidation } from './ipc/validation.js';
13
+ export { IpcError, ValidationError, NotFoundError, TimeoutError, ServiceUnavailableError } from './ipc/errors.js';
12
14
  // ── MCP ────────────────────────────────────────────────────
13
15
  export { startMcpServer } from './mcp/server.js';
14
16
  export { McpHttpServer } from './mcp/http-server.js';
@@ -16,6 +18,7 @@ export { McpHttpServer } from './mcp/http-server.js';
16
18
  export { c, baseIcons, header, keyValue, statusBadge, progressBar, divider, table, stripAnsi } from './cli/colors.js';
17
19
  // ── API ────────────────────────────────────────────────────
18
20
  export { BaseApiServer } from './api/server.js';
21
+ export { RateLimiter, readBodyWithLimit, applySecurityHeaders } from './api/middleware.js';
19
22
  // ── Math ───────────────────────────────────────────────────
20
23
  export { wilsonScore } from './math/wilson-score.js';
21
24
  export { timeDecayFactor } from './math/time-decay.js';
@@ -42,4 +45,10 @@ export { CrossBrainSubscriptionManager } from './cross-brain/subscription.js';
42
45
  export { CrossBrainCorrelator } from './cross-brain/correlator.js';
43
46
  // ── Ecosystem ──────────────────────────────────────────────
44
47
  export { EcosystemService } from './ecosystem/service.js';
48
+ // ── Webhooks ──────────────────────────────────────────────
49
+ export { WebhookService, runWebhookMigration } from './webhooks/service.js';
50
+ // ── Export ────────────────────────────────────────────────
51
+ export { ExportService } from './export/service.js';
52
+ // ── Backup ────────────────────────────────────────────────
53
+ export { BackupService } from './backup/service.js';
45
54
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,8DAA8D;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,8DAA8D;AAC9D,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEtH,8DAA8D;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,8DAA8D;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,8DAA8D;AAC9D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQ/D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,2DAA2D;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAU7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,4DAA4D;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAG/D,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,+BAA+B,CAAC;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Base error for IPC operations.
3
+ */
4
+ export declare class IpcError extends Error {
5
+ readonly code: string;
6
+ readonly statusCode: number;
7
+ constructor(message: string, code: string, statusCode?: number);
8
+ }
9
+ /**
10
+ * Thrown when IPC params fail validation.
11
+ */
12
+ export declare class ValidationError extends IpcError {
13
+ constructor(message: string);
14
+ }
15
+ /**
16
+ * Thrown when a requested method or resource is not found.
17
+ */
18
+ export declare class NotFoundError extends IpcError {
19
+ constructor(message: string);
20
+ }
21
+ /**
22
+ * Thrown when an IPC operation times out.
23
+ */
24
+ export declare class TimeoutError extends IpcError {
25
+ constructor(message?: string);
26
+ }
27
+ /**
28
+ * Thrown when a required service is not available.
29
+ */
30
+ export declare class ServiceUnavailableError extends IpcError {
31
+ constructor(message?: string);
32
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Base error for IPC operations.
3
+ */
4
+ export class IpcError extends Error {
5
+ code;
6
+ statusCode;
7
+ constructor(message, code, statusCode = 400) {
8
+ super(message);
9
+ this.name = 'IpcError';
10
+ this.code = code;
11
+ this.statusCode = statusCode;
12
+ }
13
+ }
14
+ /**
15
+ * Thrown when IPC params fail validation.
16
+ */
17
+ export class ValidationError extends IpcError {
18
+ constructor(message) {
19
+ super(message, 'VALIDATION_ERROR', 400);
20
+ this.name = 'ValidationError';
21
+ }
22
+ }
23
+ /**
24
+ * Thrown when a requested method or resource is not found.
25
+ */
26
+ export class NotFoundError extends IpcError {
27
+ constructor(message) {
28
+ super(message, 'NOT_FOUND', 404);
29
+ this.name = 'NotFoundError';
30
+ }
31
+ }
32
+ /**
33
+ * Thrown when an IPC operation times out.
34
+ */
35
+ export class TimeoutError extends IpcError {
36
+ constructor(message = 'Operation timed out') {
37
+ super(message, 'TIMEOUT', 408);
38
+ this.name = 'TimeoutError';
39
+ }
40
+ }
41
+ /**
42
+ * Thrown when a required service is not available.
43
+ */
44
+ export class ServiceUnavailableError extends IpcError {
45
+ constructor(message = 'Service unavailable') {
46
+ super(message, 'SERVICE_UNAVAILABLE', 503);
47
+ this.name = 'ServiceUnavailableError';
48
+ }
49
+ }
50
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/ipc/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACb,UAAU,CAAS;IAE5B,YAAY,OAAe,EAAE,IAAY,EAAE,aAAqB,GAAG;QACjE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,QAAQ;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACxC,YAAY,UAAkB,qBAAqB;QACjD,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,uBAAwB,SAAQ,QAAQ;IACnD,YAAY,UAAkB,qBAAqB;QACjD,KAAK,CAAC,OAAO,EAAE,qBAAqB,EAAE,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ export interface ValidationOptions {
2
+ maxStringLength?: number;
3
+ maxArrayLength?: number;
4
+ maxDepth?: number;
5
+ }
6
+ /**
7
+ * Validate IPC method params. Ensures params is a plain object,
8
+ * string fields don't exceed length limits, arrays aren't too large,
9
+ * and nesting isn't too deep. Throws ValidationError on failure.
10
+ */
11
+ export declare function validateParams(params: unknown, options?: ValidationOptions): Record<string, unknown>;
12
+ /**
13
+ * Create a validated IPC handler wrapper. Validates params before passing to the handler.
14
+ */
15
+ export declare function withValidation<T>(handler: (params: Record<string, unknown>) => T, options?: ValidationOptions): (params: unknown) => T;
@@ -0,0 +1,62 @@
1
+ import { ValidationError } from './errors.js';
2
+ const MAX_STRING_LENGTH = 10_240; // 10KB per string field
3
+ const MAX_ARRAY_LENGTH = 1000;
4
+ const MAX_DEPTH = 10;
5
+ /**
6
+ * Validate IPC method params. Ensures params is a plain object,
7
+ * string fields don't exceed length limits, arrays aren't too large,
8
+ * and nesting isn't too deep. Throws ValidationError on failure.
9
+ */
10
+ export function validateParams(params, options) {
11
+ const maxStr = options?.maxStringLength ?? MAX_STRING_LENGTH;
12
+ const maxArr = options?.maxArrayLength ?? MAX_ARRAY_LENGTH;
13
+ const maxDep = options?.maxDepth ?? MAX_DEPTH;
14
+ // Null/undefined → empty object (many methods accept no params)
15
+ if (params == null)
16
+ return {};
17
+ // Must be a plain object
18
+ if (typeof params !== 'object' || Array.isArray(params)) {
19
+ throw new ValidationError('Params must be a plain object');
20
+ }
21
+ // Deep validate
22
+ validateValue(params, 'params', maxStr, maxArr, maxDep, 0);
23
+ return params;
24
+ }
25
+ function validateValue(value, path, maxStr, maxArr, maxDep, depth) {
26
+ if (depth > maxDep) {
27
+ throw new ValidationError(`Maximum nesting depth exceeded at ${path}`);
28
+ }
29
+ if (typeof value === 'string') {
30
+ if (value.length > maxStr) {
31
+ throw new ValidationError(`String field "${path}" exceeds maximum length of ${maxStr}`);
32
+ }
33
+ return;
34
+ }
35
+ if (Array.isArray(value)) {
36
+ if (value.length > maxArr) {
37
+ throw new ValidationError(`Array field "${path}" exceeds maximum length of ${maxArr}`);
38
+ }
39
+ for (let i = 0; i < value.length; i++) {
40
+ validateValue(value[i], `${path}[${i}]`, maxStr, maxArr, maxDep, depth + 1);
41
+ }
42
+ return;
43
+ }
44
+ if (typeof value === 'object' && value !== null) {
45
+ const entries = Object.entries(value);
46
+ for (const [key, val] of entries) {
47
+ validateValue(val, `${path}.${key}`, maxStr, maxArr, maxDep, depth + 1);
48
+ }
49
+ return;
50
+ }
51
+ // Primitives (number, boolean, null) are fine
52
+ }
53
+ /**
54
+ * Create a validated IPC handler wrapper. Validates params before passing to the handler.
55
+ */
56
+ export function withValidation(handler, options) {
57
+ return (params) => {
58
+ const validated = validateParams(params, options);
59
+ return handler(validated);
60
+ };
61
+ }
62
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/ipc/validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface WebhookConfig {
3
+ id?: number;
4
+ url: string;
5
+ events: string[];
6
+ secret?: string;
7
+ active?: boolean;
8
+ name?: string;
9
+ }
10
+ export interface WebhookRecord {
11
+ id: number;
12
+ url: string;
13
+ events: string;
14
+ secret: string | null;
15
+ active: number;
16
+ name: string | null;
17
+ created_at: string;
18
+ }
19
+ export interface DeliveryRecord {
20
+ id: number;
21
+ webhook_id: number;
22
+ event: string;
23
+ payload: string;
24
+ status: number;
25
+ response: string | null;
26
+ attempts: number;
27
+ created_at: string;
28
+ }
29
+ export interface WebhookDeliveryResult {
30
+ webhookId: number;
31
+ url: string;
32
+ status: number;
33
+ success: boolean;
34
+ attempts: number;
35
+ }
36
+ export declare function runWebhookMigration(db: Database.Database): void;
37
+ export declare class WebhookService {
38
+ private db;
39
+ private logger;
40
+ private retryDelays;
41
+ constructor(db: Database.Database);
42
+ /** Register a new webhook endpoint. */
43
+ add(config: WebhookConfig): WebhookRecord;
44
+ /** Remove a webhook by ID. */
45
+ remove(id: number): boolean;
46
+ /** Get a single webhook by ID. */
47
+ get(id: number): WebhookRecord | null;
48
+ /** List all webhooks. */
49
+ list(): WebhookRecord[];
50
+ /** Toggle a webhook active/inactive. */
51
+ toggle(id: number, active: boolean): boolean;
52
+ /** Get delivery history for a webhook (most recent first). */
53
+ history(webhookId?: number, limit?: number): DeliveryRecord[];
54
+ /**
55
+ * Fire an event to all matching webhooks.
56
+ * Returns delivery results (non-blocking — fire and forget with retry).
57
+ */
58
+ fire(event: string, data: unknown): Promise<WebhookDeliveryResult[]>;
59
+ /** Deliver a payload to a single webhook with retries. */
60
+ private deliver;
61
+ private recordDelivery;
62
+ private delay;
63
+ /** Cleanup old delivery records. */
64
+ cleanup(olderThanDays?: number): number;
65
+ }
@@ -0,0 +1,157 @@
1
+ import crypto from 'node:crypto';
2
+ import { getLogger } from '../utils/logger.js';
3
+ // ── Migration ───────────────────────────────────────────
4
+ export function runWebhookMigration(db) {
5
+ db.exec(`
6
+ CREATE TABLE IF NOT EXISTS webhooks (
7
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
8
+ url TEXT NOT NULL,
9
+ events TEXT NOT NULL DEFAULT '[]',
10
+ secret TEXT,
11
+ active INTEGER NOT NULL DEFAULT 1,
12
+ name TEXT,
13
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS webhook_deliveries (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ webhook_id INTEGER NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
19
+ event TEXT NOT NULL,
20
+ payload TEXT NOT NULL,
21
+ status INTEGER NOT NULL DEFAULT 0,
22
+ response TEXT,
23
+ attempts INTEGER NOT NULL DEFAULT 0,
24
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_webhook_id ON webhook_deliveries(webhook_id);
28
+ CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_created_at ON webhook_deliveries(created_at);
29
+ `);
30
+ }
31
+ // ── Service ─────────────────────────────────────────────
32
+ export class WebhookService {
33
+ db;
34
+ logger = getLogger();
35
+ retryDelays = [1000, 3000, 10000]; // 1s, 3s, 10s
36
+ constructor(db) {
37
+ this.db = db;
38
+ runWebhookMigration(db);
39
+ }
40
+ /** Register a new webhook endpoint. */
41
+ add(config) {
42
+ const stmt = this.db.prepare(`
43
+ INSERT INTO webhooks (url, events, secret, active, name)
44
+ VALUES (?, ?, ?, ?, ?)
45
+ `);
46
+ const info = stmt.run(config.url, JSON.stringify(config.events), config.secret ?? null, config.active !== false ? 1 : 0, config.name ?? null);
47
+ this.logger.info(`Webhook #${info.lastInsertRowid} registered: ${config.url}`);
48
+ return this.get(Number(info.lastInsertRowid));
49
+ }
50
+ /** Remove a webhook by ID. */
51
+ remove(id) {
52
+ const info = this.db.prepare('DELETE FROM webhooks WHERE id = ?').run(id);
53
+ return info.changes > 0;
54
+ }
55
+ /** Get a single webhook by ID. */
56
+ get(id) {
57
+ return this.db.prepare('SELECT * FROM webhooks WHERE id = ?').get(id);
58
+ }
59
+ /** List all webhooks. */
60
+ list() {
61
+ return this.db.prepare('SELECT * FROM webhooks ORDER BY created_at DESC').all();
62
+ }
63
+ /** Toggle a webhook active/inactive. */
64
+ toggle(id, active) {
65
+ const info = this.db.prepare('UPDATE webhooks SET active = ? WHERE id = ?').run(active ? 1 : 0, id);
66
+ return info.changes > 0;
67
+ }
68
+ /** Get delivery history for a webhook (most recent first). */
69
+ history(webhookId, limit = 50) {
70
+ if (webhookId) {
71
+ return this.db.prepare('SELECT * FROM webhook_deliveries WHERE webhook_id = ? ORDER BY created_at DESC LIMIT ?').all(webhookId, limit);
72
+ }
73
+ return this.db.prepare('SELECT * FROM webhook_deliveries ORDER BY created_at DESC LIMIT ?').all(limit);
74
+ }
75
+ /**
76
+ * Fire an event to all matching webhooks.
77
+ * Returns delivery results (non-blocking — fire and forget with retry).
78
+ */
79
+ async fire(event, data) {
80
+ const webhooks = this.db.prepare('SELECT * FROM webhooks WHERE active = 1').all();
81
+ const matching = webhooks.filter(wh => {
82
+ const events = JSON.parse(wh.events);
83
+ return events.includes('*') || events.includes(event);
84
+ });
85
+ if (matching.length === 0)
86
+ return [];
87
+ const payload = JSON.stringify({ event, data, timestamp: new Date().toISOString() });
88
+ const results = [];
89
+ await Promise.all(matching.map(async (wh) => {
90
+ const result = await this.deliver(wh, event, payload);
91
+ results.push(result);
92
+ }));
93
+ return results;
94
+ }
95
+ /** Deliver a payload to a single webhook with retries. */
96
+ async deliver(wh, event, payload) {
97
+ let lastStatus = 0;
98
+ let lastResponse = null;
99
+ let attempts = 0;
100
+ for (let i = 0; i <= this.retryDelays.length; i++) {
101
+ attempts++;
102
+ try {
103
+ const headers = {
104
+ 'Content-Type': 'application/json',
105
+ 'X-Webhook-Event': event,
106
+ };
107
+ // HMAC signing
108
+ if (wh.secret) {
109
+ const signature = crypto
110
+ .createHmac('sha256', wh.secret)
111
+ .update(payload)
112
+ .digest('hex');
113
+ headers['X-Webhook-Signature'] = `sha256=${signature}`;
114
+ }
115
+ const response = await fetch(wh.url, {
116
+ method: 'POST',
117
+ headers,
118
+ body: payload,
119
+ signal: AbortSignal.timeout(10_000),
120
+ });
121
+ lastStatus = response.status;
122
+ lastResponse = await response.text().catch(() => null);
123
+ if (response.ok) {
124
+ this.recordDelivery(wh.id, event, payload, lastStatus, lastResponse, attempts);
125
+ return { webhookId: wh.id, url: wh.url, status: lastStatus, success: true, attempts };
126
+ }
127
+ }
128
+ catch (err) {
129
+ lastStatus = 0;
130
+ lastResponse = err instanceof Error ? err.message : String(err);
131
+ }
132
+ // Retry delay (don't delay after last attempt)
133
+ if (i < this.retryDelays.length) {
134
+ await this.delay(this.retryDelays[i]);
135
+ }
136
+ }
137
+ // All retries exhausted
138
+ this.recordDelivery(wh.id, event, payload, lastStatus, lastResponse, attempts);
139
+ this.logger.warn(`Webhook #${wh.id} delivery failed after ${attempts} attempts: ${wh.url}`);
140
+ return { webhookId: wh.id, url: wh.url, status: lastStatus, success: false, attempts };
141
+ }
142
+ recordDelivery(webhookId, event, payload, status, response, attempts) {
143
+ this.db.prepare(`
144
+ INSERT INTO webhook_deliveries (webhook_id, event, payload, status, response, attempts)
145
+ VALUES (?, ?, ?, ?, ?, ?)
146
+ `).run(webhookId, event, payload, status, response, attempts);
147
+ }
148
+ delay(ms) {
149
+ return new Promise(resolve => setTimeout(resolve, ms));
150
+ }
151
+ /** Cleanup old delivery records. */
152
+ cleanup(olderThanDays = 30) {
153
+ const info = this.db.prepare(`DELETE FROM webhook_deliveries WHERE created_at < datetime('now', '-' || ? || ' days')`).run(olderThanDays);
154
+ return info.changes;
155
+ }
156
+ }
157
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/webhooks/service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timmeck/brain-core",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Shared core infrastructure for the Brain ecosystem — IPC, MCP, CLI, DB connection, and utilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,6 +20,9 @@
20
20
  "./mcp/http-server": "./dist/mcp/http-server.js",
21
21
  "./cli/colors": "./dist/cli/colors.js",
22
22
  "./api/server": "./dist/api/server.js",
23
+ "./api/middleware": "./dist/api/middleware.js",
24
+ "./ipc/validation": "./dist/ipc/validation.js",
25
+ "./ipc/errors": "./dist/ipc/errors.js",
23
26
  "./math/wilson-score": "./dist/math/wilson-score.js",
24
27
  "./math/time-decay": "./dist/math/time-decay.js",
25
28
  "./config/loader": "./dist/config/loader.js",
@@ -37,7 +40,10 @@
37
40
  "./ecosystem/service": "./dist/ecosystem/service.js",
38
41
  "./memory/types": "./dist/memory/types.js",
39
42
  "./memory/base-memory-engine": "./dist/memory/base-memory-engine.js",
40
- "./embeddings/engine": "./dist/embeddings/engine.js"
43
+ "./embeddings/engine": "./dist/embeddings/engine.js",
44
+ "./webhooks/service": "./dist/webhooks/service.js",
45
+ "./export/service": "./dist/export/service.js",
46
+ "./backup/service": "./dist/backup/service.js"
41
47
  },
42
48
  "scripts": {
43
49
  "build": "tsc",