@hasna/microservices 0.0.16 → 0.0.18
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/README.md +143 -23
- package/bin/index.js +784 -13987
- package/bin/mcp.js +298 -10973
- package/dist/index.js +251 -10056
- package/package.json +36 -26
- package/microservices/microservice-ads/package.json +0 -28
- package/microservices/microservice-ads/src/cli/index.ts +0 -605
- package/microservices/microservice-ads/src/db/campaigns.ts +0 -797
- package/microservices/microservice-ads/src/db/database.ts +0 -91
- package/microservices/microservice-ads/src/db/migrations.ts +0 -60
- package/microservices/microservice-ads/src/index.ts +0 -39
- package/microservices/microservice-ads/src/mcp/index.ts +0 -480
- package/microservices/microservice-analytics/package.json +0 -28
- package/microservices/microservice-analytics/src/cli/index.ts +0 -373
- package/microservices/microservice-analytics/src/db/analytics.ts +0 -564
- package/microservices/microservice-analytics/src/db/database.ts +0 -91
- package/microservices/microservice-analytics/src/db/migrations.ts +0 -50
- package/microservices/microservice-analytics/src/index.ts +0 -37
- package/microservices/microservice-analytics/src/mcp/index.ts +0 -334
- package/microservices/microservice-assets/package.json +0 -28
- package/microservices/microservice-assets/src/cli/index.ts +0 -375
- package/microservices/microservice-assets/src/db/assets.ts +0 -370
- package/microservices/microservice-assets/src/db/database.ts +0 -91
- package/microservices/microservice-assets/src/db/migrations.ts +0 -51
- package/microservices/microservice-assets/src/index.ts +0 -32
- package/microservices/microservice-assets/src/mcp/index.ts +0 -346
- package/microservices/microservice-bookkeeping/package.json +0 -28
- package/microservices/microservice-bookkeeping/src/cli/index.ts +0 -386
- package/microservices/microservice-bookkeeping/src/db/bookkeeping.ts +0 -591
- package/microservices/microservice-bookkeeping/src/db/database.ts +0 -91
- package/microservices/microservice-bookkeeping/src/db/migrations.ts +0 -52
- package/microservices/microservice-bookkeeping/src/index.ts +0 -32
- package/microservices/microservice-bookkeeping/src/mcp/index.ts +0 -284
- package/microservices/microservice-calendar/package.json +0 -28
- package/microservices/microservice-calendar/src/cli/index.ts +0 -287
- package/microservices/microservice-calendar/src/db/calendar.ts +0 -328
- package/microservices/microservice-calendar/src/db/database.ts +0 -91
- package/microservices/microservice-calendar/src/db/migrations.ts +0 -47
- package/microservices/microservice-calendar/src/index.ts +0 -24
- package/microservices/microservice-calendar/src/mcp/index.ts +0 -226
- package/microservices/microservice-company/package.json +0 -28
- package/microservices/microservice-company/src/cli/index.ts +0 -1126
- package/microservices/microservice-company/src/db/company.ts +0 -854
- package/microservices/microservice-company/src/db/database.ts +0 -91
- package/microservices/microservice-company/src/db/migrations.ts +0 -214
- package/microservices/microservice-company/src/db/workflow-migrations.ts +0 -44
- package/microservices/microservice-company/src/index.ts +0 -60
- package/microservices/microservice-company/src/lib/audit.ts +0 -168
- package/microservices/microservice-company/src/lib/finance.ts +0 -299
- package/microservices/microservice-company/src/lib/settings.ts +0 -85
- package/microservices/microservice-company/src/lib/workflows.ts +0 -698
- package/microservices/microservice-company/src/mcp/index.ts +0 -991
- package/microservices/microservice-compliance/package.json +0 -28
- package/microservices/microservice-compliance/src/cli/index.ts +0 -467
- package/microservices/microservice-compliance/src/db/compliance.ts +0 -633
- package/microservices/microservice-compliance/src/db/database.ts +0 -91
- package/microservices/microservice-compliance/src/db/migrations.ts +0 -63
- package/microservices/microservice-compliance/src/index.ts +0 -46
- package/microservices/microservice-compliance/src/mcp/index.ts +0 -438
- package/microservices/microservice-contacts/package.json +0 -28
- package/microservices/microservice-contacts/src/cli/index.ts +0 -393
- package/microservices/microservice-contacts/src/db/companies.ts +0 -167
- package/microservices/microservice-contacts/src/db/contacts.ts +0 -249
- package/microservices/microservice-contacts/src/db/database.ts +0 -91
- package/microservices/microservice-contacts/src/db/migrations.ts +0 -71
- package/microservices/microservice-contacts/src/db/relationships.ts +0 -53
- package/microservices/microservice-contacts/src/index.ts +0 -42
- package/microservices/microservice-contacts/src/mcp/index.ts +0 -303
- package/microservices/microservice-contracts/package.json +0 -28
- package/microservices/microservice-contracts/src/cli/index.ts +0 -770
- package/microservices/microservice-contracts/src/db/contracts.ts +0 -925
- package/microservices/microservice-contracts/src/db/database.ts +0 -91
- package/microservices/microservice-contracts/src/db/migrations.ts +0 -141
- package/microservices/microservice-contracts/src/index.ts +0 -43
- package/microservices/microservice-contracts/src/mcp/index.ts +0 -617
- package/microservices/microservice-crm/package.json +0 -28
- package/microservices/microservice-crm/src/cli/index.ts +0 -396
- package/microservices/microservice-crm/src/db/database.ts +0 -91
- package/microservices/microservice-crm/src/db/migrations.ts +0 -66
- package/microservices/microservice-crm/src/db/pipeline.ts +0 -397
- package/microservices/microservice-crm/src/index.ts +0 -34
- package/microservices/microservice-crm/src/mcp/index.ts +0 -294
- package/microservices/microservice-documents/package.json +0 -28
- package/microservices/microservice-documents/src/cli/index.ts +0 -246
- package/microservices/microservice-documents/src/db/database.ts +0 -91
- package/microservices/microservice-documents/src/db/documents.ts +0 -316
- package/microservices/microservice-documents/src/db/migrations.ts +0 -49
- package/microservices/microservice-documents/src/index.ts +0 -24
- package/microservices/microservice-documents/src/mcp/index.ts +0 -202
- package/microservices/microservice-domains/package.json +0 -28
- package/microservices/microservice-domains/src/cli/index.ts +0 -1111
- package/microservices/microservice-domains/src/db/database.ts +0 -91
- package/microservices/microservice-domains/src/db/domains.ts +0 -1164
- package/microservices/microservice-domains/src/db/migrations.ts +0 -60
- package/microservices/microservice-domains/src/index.ts +0 -65
- package/microservices/microservice-domains/src/lib/brandsight.ts +0 -350
- package/microservices/microservice-domains/src/lib/godaddy.ts +0 -338
- package/microservices/microservice-domains/src/lib/namecheap.ts +0 -262
- package/microservices/microservice-domains/src/lib/registrar.ts +0 -355
- package/microservices/microservice-domains/src/mcp/index.ts +0 -781
- package/microservices/microservice-expenses/package.json +0 -28
- package/microservices/microservice-expenses/src/cli/index.ts +0 -267
- package/microservices/microservice-expenses/src/db/database.ts +0 -91
- package/microservices/microservice-expenses/src/db/expenses.ts +0 -345
- package/microservices/microservice-expenses/src/db/migrations.ts +0 -45
- package/microservices/microservice-expenses/src/index.ts +0 -25
- package/microservices/microservice-expenses/src/mcp/index.ts +0 -196
- package/microservices/microservice-habits/package.json +0 -28
- package/microservices/microservice-habits/src/cli/index.ts +0 -315
- package/microservices/microservice-habits/src/db/database.ts +0 -91
- package/microservices/microservice-habits/src/db/habits.ts +0 -451
- package/microservices/microservice-habits/src/db/migrations.ts +0 -46
- package/microservices/microservice-habits/src/index.ts +0 -31
- package/microservices/microservice-habits/src/mcp/index.ts +0 -313
- package/microservices/microservice-health/package.json +0 -28
- package/microservices/microservice-health/src/cli/index.ts +0 -484
- package/microservices/microservice-health/src/db/database.ts +0 -91
- package/microservices/microservice-health/src/db/health.ts +0 -708
- package/microservices/microservice-health/src/db/migrations.ts +0 -70
- package/microservices/microservice-health/src/index.ts +0 -63
- package/microservices/microservice-health/src/mcp/index.ts +0 -437
- package/microservices/microservice-hiring/package.json +0 -28
- package/microservices/microservice-hiring/src/cli/index.ts +0 -741
- package/microservices/microservice-hiring/src/db/database.ts +0 -91
- package/microservices/microservice-hiring/src/db/hiring.ts +0 -1085
- package/microservices/microservice-hiring/src/db/migrations.ts +0 -89
- package/microservices/microservice-hiring/src/index.ts +0 -80
- package/microservices/microservice-hiring/src/lib/scoring.ts +0 -206
- package/microservices/microservice-hiring/src/mcp/index.ts +0 -709
- package/microservices/microservice-inventory/package.json +0 -28
- package/microservices/microservice-inventory/src/cli/index.ts +0 -365
- package/microservices/microservice-inventory/src/db/database.ts +0 -91
- package/microservices/microservice-inventory/src/db/inventory.ts +0 -393
- package/microservices/microservice-inventory/src/db/migrations.ts +0 -54
- package/microservices/microservice-inventory/src/index.ts +0 -28
- package/microservices/microservice-inventory/src/mcp/index.ts +0 -250
- package/microservices/microservice-invoices/dashboard/index.html +0 -12
- package/microservices/microservice-invoices/dashboard/package.json +0 -29
- package/microservices/microservice-invoices/dashboard/tsconfig.json +0 -14
- package/microservices/microservice-invoices/dashboard/vite.config.ts +0 -15
- package/microservices/microservice-invoices/package.json +0 -31
- package/microservices/microservice-invoices/src/cli/index.ts +0 -308
- package/microservices/microservice-invoices/src/db/business.ts +0 -241
- package/microservices/microservice-invoices/src/db/clients.ts +0 -127
- package/microservices/microservice-invoices/src/db/database.ts +0 -91
- package/microservices/microservice-invoices/src/db/invoices.ts +0 -345
- package/microservices/microservice-invoices/src/db/migrations.ts +0 -184
- package/microservices/microservice-invoices/src/index.ts +0 -56
- package/microservices/microservice-invoices/src/mcp/index.ts +0 -242
- package/microservices/microservice-invoices/src/server/index.ts +0 -162
- package/microservices/microservice-leads/package.json +0 -28
- package/microservices/microservice-leads/src/cli/index.ts +0 -596
- package/microservices/microservice-leads/src/db/database.ts +0 -91
- package/microservices/microservice-leads/src/db/leads.ts +0 -520
- package/microservices/microservice-leads/src/db/lists.ts +0 -151
- package/microservices/microservice-leads/src/db/migrations.ts +0 -93
- package/microservices/microservice-leads/src/index.ts +0 -65
- package/microservices/microservice-leads/src/lib/enrichment.ts +0 -202
- package/microservices/microservice-leads/src/lib/scoring.ts +0 -134
- package/microservices/microservice-leads/src/mcp/index.ts +0 -533
- package/microservices/microservice-notes/package.json +0 -28
- package/microservices/microservice-notes/src/cli/index.ts +0 -63
- package/microservices/microservice-notes/src/db/database.ts +0 -91
- package/microservices/microservice-notes/src/db/migrations.ts +0 -40
- package/microservices/microservice-notes/src/db/notes.ts +0 -114
- package/microservices/microservice-notes/src/index.ts +0 -2
- package/microservices/microservice-notes/src/mcp/index.ts +0 -37
- package/microservices/microservice-notifications/package.json +0 -28
- package/microservices/microservice-notifications/src/cli/index.ts +0 -349
- package/microservices/microservice-notifications/src/db/database.ts +0 -91
- package/microservices/microservice-notifications/src/db/migrations.ts +0 -62
- package/microservices/microservice-notifications/src/db/notifications.ts +0 -509
- package/microservices/microservice-notifications/src/index.ts +0 -41
- package/microservices/microservice-notifications/src/mcp/index.ts +0 -422
- package/microservices/microservice-payments/package.json +0 -28
- package/microservices/microservice-payments/src/cli/index.ts +0 -609
- package/microservices/microservice-payments/src/db/database.ts +0 -91
- package/microservices/microservice-payments/src/db/migrations.ts +0 -81
- package/microservices/microservice-payments/src/db/payments.ts +0 -1204
- package/microservices/microservice-payments/src/index.ts +0 -51
- package/microservices/microservice-payments/src/mcp/index.ts +0 -683
- package/microservices/microservice-payroll/package.json +0 -28
- package/microservices/microservice-payroll/src/cli/index.ts +0 -643
- package/microservices/microservice-payroll/src/db/database.ts +0 -91
- package/microservices/microservice-payroll/src/db/migrations.ts +0 -95
- package/microservices/microservice-payroll/src/db/payroll.ts +0 -1377
- package/microservices/microservice-payroll/src/index.ts +0 -48
- package/microservices/microservice-payroll/src/mcp/index.ts +0 -666
- package/microservices/microservice-products/package.json +0 -28
- package/microservices/microservice-products/src/cli/index.ts +0 -416
- package/microservices/microservice-products/src/db/categories.ts +0 -154
- package/microservices/microservice-products/src/db/database.ts +0 -91
- package/microservices/microservice-products/src/db/migrations.ts +0 -58
- package/microservices/microservice-products/src/db/pricing-tiers.ts +0 -66
- package/microservices/microservice-products/src/db/products.ts +0 -452
- package/microservices/microservice-products/src/index.ts +0 -53
- package/microservices/microservice-products/src/mcp/index.ts +0 -453
- package/microservices/microservice-projects/package.json +0 -28
- package/microservices/microservice-projects/src/cli/index.ts +0 -480
- package/microservices/microservice-projects/src/db/database.ts +0 -91
- package/microservices/microservice-projects/src/db/migrations.ts +0 -65
- package/microservices/microservice-projects/src/db/projects.ts +0 -715
- package/microservices/microservice-projects/src/index.ts +0 -57
- package/microservices/microservice-projects/src/mcp/index.ts +0 -501
- package/microservices/microservice-proposals/package.json +0 -28
- package/microservices/microservice-proposals/src/cli/index.ts +0 -400
- package/microservices/microservice-proposals/src/db/database.ts +0 -91
- package/microservices/microservice-proposals/src/db/migrations.ts +0 -52
- package/microservices/microservice-proposals/src/db/proposals.ts +0 -532
- package/microservices/microservice-proposals/src/index.ts +0 -37
- package/microservices/microservice-proposals/src/mcp/index.ts +0 -375
- package/microservices/microservice-reading/package.json +0 -28
- package/microservices/microservice-reading/src/cli/index.ts +0 -464
- package/microservices/microservice-reading/src/db/database.ts +0 -91
- package/microservices/microservice-reading/src/db/migrations.ts +0 -59
- package/microservices/microservice-reading/src/db/reading.ts +0 -524
- package/microservices/microservice-reading/src/index.ts +0 -51
- package/microservices/microservice-reading/src/mcp/index.ts +0 -368
- package/microservices/microservice-shipping/package.json +0 -28
- package/microservices/microservice-shipping/src/cli/index.ts +0 -606
- package/microservices/microservice-shipping/src/db/database.ts +0 -91
- package/microservices/microservice-shipping/src/db/migrations.ts +0 -69
- package/microservices/microservice-shipping/src/db/shipping.ts +0 -1093
- package/microservices/microservice-shipping/src/index.ts +0 -53
- package/microservices/microservice-shipping/src/mcp/index.ts +0 -533
- package/microservices/microservice-social/package.json +0 -29
- package/microservices/microservice-social/src/cli/index.ts +0 -1583
- package/microservices/microservice-social/src/db/database.ts +0 -91
- package/microservices/microservice-social/src/db/migrations.ts +0 -160
- package/microservices/microservice-social/src/db/social.ts +0 -1076
- package/microservices/microservice-social/src/index.ts +0 -46
- package/microservices/microservice-social/src/lib/audience.ts +0 -353
- package/microservices/microservice-social/src/lib/content-ai.ts +0 -278
- package/microservices/microservice-social/src/lib/media.ts +0 -311
- package/microservices/microservice-social/src/lib/mentions.ts +0 -434
- package/microservices/microservice-social/src/lib/metrics-sync.ts +0 -264
- package/microservices/microservice-social/src/lib/publisher.ts +0 -377
- package/microservices/microservice-social/src/lib/scheduler.ts +0 -229
- package/microservices/microservice-social/src/lib/sentiment.ts +0 -256
- package/microservices/microservice-social/src/lib/threads.ts +0 -291
- package/microservices/microservice-social/src/mcp/index.ts +0 -1425
- package/microservices/microservice-social/src/server/index.ts +0 -441
- package/microservices/microservice-subscriptions/package.json +0 -28
- package/microservices/microservice-subscriptions/src/cli/index.ts +0 -715
- package/microservices/microservice-subscriptions/src/db/database.ts +0 -91
- package/microservices/microservice-subscriptions/src/db/migrations.ts +0 -125
- package/microservices/microservice-subscriptions/src/db/subscriptions.ts +0 -1256
- package/microservices/microservice-subscriptions/src/index.ts +0 -41
- package/microservices/microservice-subscriptions/src/mcp/index.ts +0 -631
- package/microservices/microservice-timesheets/package.json +0 -28
- package/microservices/microservice-timesheets/src/cli/index.ts +0 -373
- package/microservices/microservice-timesheets/src/db/database.ts +0 -91
- package/microservices/microservice-timesheets/src/db/locale.ts +0 -217
- package/microservices/microservice-timesheets/src/db/migrations.ts +0 -74
- package/microservices/microservice-timesheets/src/db/timesheets.ts +0 -447
- package/microservices/microservice-timesheets/src/index.ts +0 -44
- package/microservices/microservice-timesheets/src/mcp/index.ts +0 -269
- package/microservices/microservice-transcriber/package.json +0 -29
- package/microservices/microservice-transcriber/src/cli/index.ts +0 -1593
- package/microservices/microservice-transcriber/src/db/annotations.ts +0 -37
- package/microservices/microservice-transcriber/src/db/comments.ts +0 -166
- package/microservices/microservice-transcriber/src/db/database.ts +0 -91
- package/microservices/microservice-transcriber/src/db/migrations.ts +0 -118
- package/microservices/microservice-transcriber/src/db/proofread.ts +0 -119
- package/microservices/microservice-transcriber/src/db/transcripts.ts +0 -395
- package/microservices/microservice-transcriber/src/index.ts +0 -43
- package/microservices/microservice-transcriber/src/lib/config.ts +0 -77
- package/microservices/microservice-transcriber/src/lib/diff.ts +0 -91
- package/microservices/microservice-transcriber/src/lib/downloader.ts +0 -638
- package/microservices/microservice-transcriber/src/lib/feeds.ts +0 -62
- package/microservices/microservice-transcriber/src/lib/live.ts +0 -94
- package/microservices/microservice-transcriber/src/lib/notion.ts +0 -129
- package/microservices/microservice-transcriber/src/lib/proofread.ts +0 -296
- package/microservices/microservice-transcriber/src/lib/providers.ts +0 -713
- package/microservices/microservice-transcriber/src/lib/summarizer.ts +0 -147
- package/microservices/microservice-transcriber/src/lib/translator.ts +0 -75
- package/microservices/microservice-transcriber/src/lib/webhook.ts +0 -37
- package/microservices/microservice-transcriber/src/mcp/index.ts +0 -1330
- package/microservices/microservice-transcriber/src/server/index.ts +0 -199
- package/microservices/microservice-travel/package.json +0 -28
- package/microservices/microservice-travel/src/cli/index.ts +0 -505
- package/microservices/microservice-travel/src/db/database.ts +0 -91
- package/microservices/microservice-travel/src/db/migrations.ts +0 -77
- package/microservices/microservice-travel/src/db/travel.ts +0 -802
- package/microservices/microservice-travel/src/index.ts +0 -60
- package/microservices/microservice-travel/src/mcp/index.ts +0 -495
- package/microservices/microservice-wiki/package.json +0 -28
- package/microservices/microservice-wiki/src/cli/index.ts +0 -345
- package/microservices/microservice-wiki/src/db/database.ts +0 -91
- package/microservices/microservice-wiki/src/db/migrations.ts +0 -55
- package/microservices/microservice-wiki/src/db/wiki.ts +0 -395
- package/microservices/microservice-wiki/src/index.ts +0 -32
- package/microservices/microservice-wiki/src/mcp/index.ts +0 -344
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database connection for microservice-notes
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { SqliteAdapter } from "@hasna/cloud";
|
|
6
|
-
import type { Database } from "bun:sqlite";
|
|
7
|
-
import { existsSync, mkdirSync, cpSync } from "node:fs";
|
|
8
|
-
import { dirname, join, resolve } from "node:path";
|
|
9
|
-
import { MIGRATIONS } from "./migrations.js";
|
|
10
|
-
|
|
11
|
-
let _db: Database | null = null;
|
|
12
|
-
|
|
13
|
-
function getDbPath(): string {
|
|
14
|
-
const explicit = process.env["HASNA_MICROSERVICES_DIR"] ?? process.env["MICROSERVICES_DIR"];
|
|
15
|
-
if (explicit) {
|
|
16
|
-
return join(explicit, "microservice-notes", "data.db");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let dir = resolve(process.cwd());
|
|
20
|
-
while (true) {
|
|
21
|
-
const msDir = join(dir, ".microservices");
|
|
22
|
-
if (existsSync(msDir)) return join(msDir, "microservice-notes", "data.db");
|
|
23
|
-
const parent = dirname(dir);
|
|
24
|
-
if (parent === dir) break;
|
|
25
|
-
dir = parent;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
29
|
-
const newDir = join(home, ".hasna", "microservices");
|
|
30
|
-
const oldDir = join(home, ".microservices");
|
|
31
|
-
if (!existsSync(newDir) && existsSync(oldDir)) {
|
|
32
|
-
mkdirSync(join(home, ".hasna"), { recursive: true });
|
|
33
|
-
cpSync(oldDir, newDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
return join(newDir, "microservice-notes", "data.db");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getDatabase(): Database {
|
|
39
|
-
if (_db) return _db;
|
|
40
|
-
|
|
41
|
-
const dbPath = getDbPath();
|
|
42
|
-
const dir = dirname(resolve(dbPath));
|
|
43
|
-
if (!existsSync(dir)) {
|
|
44
|
-
mkdirSync(dir, { recursive: true });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const adapter = new SqliteAdapter(dbPath);
|
|
48
|
-
_db = adapter.raw;
|
|
49
|
-
// SqliteAdapter already sets WAL and foreign_keys
|
|
50
|
-
|
|
51
|
-
_db.exec(`
|
|
52
|
-
CREATE TABLE IF NOT EXISTS _migrations (
|
|
53
|
-
id INTEGER PRIMARY KEY,
|
|
54
|
-
name TEXT NOT NULL,
|
|
55
|
-
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
56
|
-
)
|
|
57
|
-
`);
|
|
58
|
-
|
|
59
|
-
const applied = _db
|
|
60
|
-
.query("SELECT id FROM _migrations ORDER BY id")
|
|
61
|
-
.all() as { id: number }[];
|
|
62
|
-
const appliedIds = new Set(applied.map((r) => r.id));
|
|
63
|
-
|
|
64
|
-
for (const migration of MIGRATIONS) {
|
|
65
|
-
if (appliedIds.has(migration.id)) continue;
|
|
66
|
-
|
|
67
|
-
_db.exec("BEGIN");
|
|
68
|
-
try {
|
|
69
|
-
_db.exec(migration.sql);
|
|
70
|
-
_db.prepare("INSERT INTO _migrations (id, name) VALUES (?, ?)").run(
|
|
71
|
-
migration.id,
|
|
72
|
-
migration.name
|
|
73
|
-
);
|
|
74
|
-
_db.exec("COMMIT");
|
|
75
|
-
} catch (error) {
|
|
76
|
-
_db.exec("ROLLBACK");
|
|
77
|
-
throw new Error(
|
|
78
|
-
`Migration ${migration.id} (${migration.name}) failed: ${error instanceof Error ? error.message : String(error)}`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return _db;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function closeDatabase(): void {
|
|
87
|
-
if (_db) {
|
|
88
|
-
_db.close();
|
|
89
|
-
_db = null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
export interface MigrationEntry { id: number; name: string; sql: string; }
|
|
2
|
-
|
|
3
|
-
export const MIGRATIONS: MigrationEntry[] = [
|
|
4
|
-
{
|
|
5
|
-
id: 1,
|
|
6
|
-
name: "initial_schema",
|
|
7
|
-
sql: `
|
|
8
|
-
CREATE TABLE IF NOT EXISTS folders (
|
|
9
|
-
id TEXT PRIMARY KEY,
|
|
10
|
-
name TEXT NOT NULL,
|
|
11
|
-
parent_id TEXT REFERENCES folders(id) ON DELETE CASCADE,
|
|
12
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
13
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
CREATE TABLE IF NOT EXISTS notes (
|
|
17
|
-
id TEXT PRIMARY KEY,
|
|
18
|
-
title TEXT NOT NULL,
|
|
19
|
-
content TEXT NOT NULL DEFAULT '',
|
|
20
|
-
folder_id TEXT REFERENCES folders(id) ON DELETE SET NULL,
|
|
21
|
-
pinned INTEGER NOT NULL DEFAULT 0,
|
|
22
|
-
tags TEXT NOT NULL DEFAULT '[]',
|
|
23
|
-
metadata TEXT NOT NULL DEFAULT '{}',
|
|
24
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
25
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
CREATE TABLE IF NOT EXISTS note_tags (
|
|
29
|
-
note_id TEXT NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
|
30
|
-
tag TEXT NOT NULL,
|
|
31
|
-
PRIMARY KEY (note_id, tag)
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
CREATE INDEX IF NOT EXISTS idx_notes_folder ON notes(folder_id);
|
|
35
|
-
CREATE INDEX IF NOT EXISTS idx_notes_pinned ON notes(pinned);
|
|
36
|
-
CREATE INDEX IF NOT EXISTS idx_note_tags_tag ON note_tags(tag);
|
|
37
|
-
CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id);
|
|
38
|
-
`,
|
|
39
|
-
},
|
|
40
|
-
];
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { getDatabase } from "./database.js";
|
|
2
|
-
|
|
3
|
-
export interface Note {
|
|
4
|
-
id: string;
|
|
5
|
-
title: string;
|
|
6
|
-
content: string;
|
|
7
|
-
folder_id: string | null;
|
|
8
|
-
pinned: boolean;
|
|
9
|
-
tags: string[];
|
|
10
|
-
metadata: Record<string, unknown>;
|
|
11
|
-
created_at: string;
|
|
12
|
-
updated_at: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface NoteRow {
|
|
16
|
-
id: string; title: string; content: string; folder_id: string | null;
|
|
17
|
-
pinned: number; tags: string; metadata: string; created_at: string; updated_at: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function rowToNote(row: NoteRow): Note {
|
|
21
|
-
return { ...row, pinned: row.pinned === 1, tags: JSON.parse(row.tags || "[]"), metadata: JSON.parse(row.metadata || "{}") };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CreateNoteInput {
|
|
25
|
-
title: string;
|
|
26
|
-
content?: string;
|
|
27
|
-
folder_id?: string;
|
|
28
|
-
pinned?: boolean;
|
|
29
|
-
tags?: string[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function createNote(input: CreateNoteInput): Note {
|
|
33
|
-
const db = getDatabase();
|
|
34
|
-
const id = crypto.randomUUID();
|
|
35
|
-
const tags = JSON.stringify(input.tags || []);
|
|
36
|
-
db.prepare(
|
|
37
|
-
`INSERT INTO notes (id, title, content, folder_id, pinned, tags) VALUES (?, ?, ?, ?, ?, ?)`
|
|
38
|
-
).run(id, input.title, input.content || "", input.folder_id || null, input.pinned ? 1 : 0, tags);
|
|
39
|
-
if (input.tags?.length) {
|
|
40
|
-
const ins = db.prepare("INSERT OR IGNORE INTO note_tags (note_id, tag) VALUES (?, ?)");
|
|
41
|
-
for (const tag of input.tags) ins.run(id, tag);
|
|
42
|
-
}
|
|
43
|
-
return getNote(id)!;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function getNote(id: string): Note | null {
|
|
47
|
-
const db = getDatabase();
|
|
48
|
-
const row = db.prepare("SELECT * FROM notes WHERE id = ?").get(id) as NoteRow | null;
|
|
49
|
-
return row ? rowToNote(row) : null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface ListNotesOptions { search?: string; tag?: string; folder_id?: string; pinned?: boolean; limit?: number; }
|
|
53
|
-
|
|
54
|
-
export function listNotes(options: ListNotesOptions = {}): Note[] {
|
|
55
|
-
const db = getDatabase();
|
|
56
|
-
const conds: string[] = []; const params: unknown[] = [];
|
|
57
|
-
if (options.search) { conds.push("(title LIKE ? OR content LIKE ?)"); const q = `%${options.search}%`; params.push(q, q); }
|
|
58
|
-
if (options.tag) { conds.push("id IN (SELECT note_id FROM note_tags WHERE tag = ?)"); params.push(options.tag); }
|
|
59
|
-
if (options.folder_id) { conds.push("folder_id = ?"); params.push(options.folder_id); }
|
|
60
|
-
if (options.pinned !== undefined) { conds.push("pinned = ?"); params.push(options.pinned ? 1 : 0); }
|
|
61
|
-
let sql = "SELECT * FROM notes";
|
|
62
|
-
if (conds.length) sql += " WHERE " + conds.join(" AND ");
|
|
63
|
-
sql += " ORDER BY pinned DESC, updated_at DESC";
|
|
64
|
-
if (options.limit) { sql += " LIMIT ?"; params.push(options.limit); }
|
|
65
|
-
return (db.prepare(sql).all(...params) as NoteRow[]).map(rowToNote);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function updateNote(id: string, input: Partial<CreateNoteInput>): Note | null {
|
|
69
|
-
const db = getDatabase();
|
|
70
|
-
if (!getNote(id)) return null;
|
|
71
|
-
const sets: string[] = []; const params: unknown[] = [];
|
|
72
|
-
if (input.title !== undefined) { sets.push("title = ?"); params.push(input.title); }
|
|
73
|
-
if (input.content !== undefined) { sets.push("content = ?"); params.push(input.content); }
|
|
74
|
-
if (input.folder_id !== undefined) { sets.push("folder_id = ?"); params.push(input.folder_id); }
|
|
75
|
-
if (input.pinned !== undefined) { sets.push("pinned = ?"); params.push(input.pinned ? 1 : 0); }
|
|
76
|
-
if (input.tags !== undefined) {
|
|
77
|
-
sets.push("tags = ?"); params.push(JSON.stringify(input.tags));
|
|
78
|
-
db.prepare("DELETE FROM note_tags WHERE note_id = ?").run(id);
|
|
79
|
-
const ins = db.prepare("INSERT OR IGNORE INTO note_tags (note_id, tag) VALUES (?, ?)");
|
|
80
|
-
for (const tag of input.tags) ins.run(id, tag);
|
|
81
|
-
}
|
|
82
|
-
if (sets.length === 0) return getNote(id);
|
|
83
|
-
sets.push("updated_at = datetime('now')"); params.push(id);
|
|
84
|
-
db.prepare(`UPDATE notes SET ${sets.join(", ")} WHERE id = ?`).run(...params);
|
|
85
|
-
return getNote(id);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function deleteNote(id: string): boolean {
|
|
89
|
-
return getDatabase().prepare("DELETE FROM notes WHERE id = ?").run(id).changes > 0;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function countNotes(): number {
|
|
93
|
-
return (getDatabase().prepare("SELECT COUNT(*) as count FROM notes").get() as { count: number }).count;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Folders
|
|
97
|
-
export interface Folder { id: string; name: string; parent_id: string | null; created_at: string; updated_at: string; }
|
|
98
|
-
|
|
99
|
-
export function createFolder(name: string, parentId?: string): Folder {
|
|
100
|
-
const db = getDatabase();
|
|
101
|
-
const id = crypto.randomUUID();
|
|
102
|
-
db.prepare("INSERT INTO folders (id, name, parent_id) VALUES (?, ?, ?)").run(id, name, parentId || null);
|
|
103
|
-
return db.prepare("SELECT * FROM folders WHERE id = ?").get(id) as Folder;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function listFolders(parentId?: string): Folder[] {
|
|
107
|
-
const db = getDatabase();
|
|
108
|
-
if (parentId) return db.prepare("SELECT * FROM folders WHERE parent_id = ? ORDER BY name").all(parentId) as Folder[];
|
|
109
|
-
return db.prepare("SELECT * FROM folders ORDER BY name").all() as Folder[];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function deleteFolder(id: string): boolean {
|
|
113
|
-
return getDatabase().prepare("DELETE FROM folders WHERE id = ?").run(id).changes > 0;
|
|
114
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { createNote, getNote, listNotes, updateNote, deleteNote, countNotes, createFolder, listFolders, deleteFolder } from "../db/notes.js";
|
|
6
|
-
|
|
7
|
-
const server = new McpServer({ name: "microservice-notes", version: "0.0.1" });
|
|
8
|
-
|
|
9
|
-
server.registerTool("create_note", { title: "Create Note", description: "Create a new note.", inputSchema: { title: z.string(), content: z.string().optional(), folder_id: z.string().optional(), pinned: z.boolean().optional(), tags: z.array(z.string()).optional() } },
|
|
10
|
-
async (p) => ({ content: [{ type: "text", text: JSON.stringify(createNote(p), null, 2) }] }));
|
|
11
|
-
|
|
12
|
-
server.registerTool("get_note", { title: "Get Note", description: "Get a note by ID.", inputSchema: { id: z.string() } },
|
|
13
|
-
async ({ id }) => { const n = getNote(id); return n ? { content: [{ type: "text", text: JSON.stringify(n, null, 2) }] } : { content: [{ type: "text", text: `Note '${id}' not found.` }], isError: true }; });
|
|
14
|
-
|
|
15
|
-
server.registerTool("list_notes", { title: "List Notes", description: "List notes with filters.", inputSchema: { search: z.string().optional(), tag: z.string().optional(), folder_id: z.string().optional(), pinned: z.boolean().optional(), limit: z.number().optional() } },
|
|
16
|
-
async (p) => { const notes = listNotes(p); return { content: [{ type: "text", text: JSON.stringify({ notes, count: notes.length }, null, 2) }] }; });
|
|
17
|
-
|
|
18
|
-
server.registerTool("update_note", { title: "Update Note", description: "Update a note.", inputSchema: { id: z.string(), title: z.string().optional(), content: z.string().optional(), folder_id: z.string().optional(), pinned: z.boolean().optional(), tags: z.array(z.string()).optional() } },
|
|
19
|
-
async ({ id, ...input }) => { const n = updateNote(id, input); return n ? { content: [{ type: "text", text: JSON.stringify(n, null, 2) }] } : { content: [{ type: "text", text: `Note '${id}' not found.` }], isError: true }; });
|
|
20
|
-
|
|
21
|
-
server.registerTool("delete_note", { title: "Delete Note", description: "Delete a note.", inputSchema: { id: z.string() } },
|
|
22
|
-
async ({ id }) => ({ content: [{ type: "text", text: JSON.stringify({ id, deleted: deleteNote(id) }) }] }));
|
|
23
|
-
|
|
24
|
-
server.registerTool("count_notes", { title: "Count Notes", description: "Count all notes.", inputSchema: {} },
|
|
25
|
-
async () => ({ content: [{ type: "text", text: JSON.stringify({ count: countNotes() }) }] }));
|
|
26
|
-
|
|
27
|
-
server.registerTool("create_folder", { title: "Create Folder", description: "Create a folder.", inputSchema: { name: z.string(), parent_id: z.string().optional() } },
|
|
28
|
-
async ({ name, parent_id }) => ({ content: [{ type: "text", text: JSON.stringify(createFolder(name, parent_id), null, 2) }] }));
|
|
29
|
-
|
|
30
|
-
server.registerTool("list_folders", { title: "List Folders", description: "List folders.", inputSchema: { parent_id: z.string().optional() } },
|
|
31
|
-
async ({ parent_id }) => ({ content: [{ type: "text", text: JSON.stringify(listFolders(parent_id), null, 2) }] }));
|
|
32
|
-
|
|
33
|
-
server.registerTool("delete_folder", { title: "Delete Folder", description: "Delete a folder.", inputSchema: { id: z.string() } },
|
|
34
|
-
async ({ id }) => ({ content: [{ type: "text", text: JSON.stringify({ id, deleted: deleteFolder(id) }) }] }));
|
|
35
|
-
|
|
36
|
-
async function main() { const t = new StdioServerTransport(); await server.connect(t); console.error("microservice-notes MCP running"); }
|
|
37
|
-
main().catch((e) => { console.error("Fatal:", e); process.exit(1); });
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hasna/microservice-notifications",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"description": "Notification management microservice with SQLite — send notifications, define rules, and use templates across channels",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"microservice-notifications": "./src/cli/index.ts",
|
|
8
|
-
"microservice-notifications-mcp": "./src/mcp/index.ts"
|
|
9
|
-
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": "./src/index.ts"
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"dev": "bun run ./src/cli/index.ts",
|
|
15
|
-
"test": "bun test"
|
|
16
|
-
},
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"@hasna/cloud": "^0.1.24",
|
|
19
|
-
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
20
|
-
"commander": "^12.1.0",
|
|
21
|
-
"zod": "^3.24.0"
|
|
22
|
-
},
|
|
23
|
-
"license": "Apache-2.0",
|
|
24
|
-
"publishConfig": {
|
|
25
|
-
"registry": "https://registry.npmjs.org",
|
|
26
|
-
"access": "public"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
import {
|
|
5
|
-
sendNotification,
|
|
6
|
-
listNotifications,
|
|
7
|
-
getNotification,
|
|
8
|
-
markRead,
|
|
9
|
-
markAllRead,
|
|
10
|
-
createRule,
|
|
11
|
-
listRules,
|
|
12
|
-
enableRule,
|
|
13
|
-
disableRule,
|
|
14
|
-
deleteRule,
|
|
15
|
-
createTemplate,
|
|
16
|
-
listTemplates,
|
|
17
|
-
deleteTemplate,
|
|
18
|
-
processEvent,
|
|
19
|
-
getNotificationStats,
|
|
20
|
-
} from "../db/notifications.js";
|
|
21
|
-
|
|
22
|
-
const program = new Command();
|
|
23
|
-
|
|
24
|
-
program
|
|
25
|
-
.name("microservice-notifications")
|
|
26
|
-
.description("Notification management microservice")
|
|
27
|
-
.version("0.0.1");
|
|
28
|
-
|
|
29
|
-
// --- Notifications ---
|
|
30
|
-
|
|
31
|
-
program
|
|
32
|
-
.command("send")
|
|
33
|
-
.description("Send a notification")
|
|
34
|
-
.requiredOption("--channel <channel>", "Channel (email, slack, sms, webhook, in_app)")
|
|
35
|
-
.requiredOption("--to <recipient>", "Recipient")
|
|
36
|
-
.option("--subject <subject>", "Subject")
|
|
37
|
-
.option("--body <body>", "Body")
|
|
38
|
-
.option("--priority <priority>", "Priority (low, normal, high, urgent)", "normal")
|
|
39
|
-
.option("--source-service <service>", "Source service")
|
|
40
|
-
.option("--source-event <event>", "Source event")
|
|
41
|
-
.option("--json", "Output as JSON", false)
|
|
42
|
-
.action((opts) => {
|
|
43
|
-
const notification = sendNotification({
|
|
44
|
-
channel: opts.channel,
|
|
45
|
-
recipient: opts.to,
|
|
46
|
-
subject: opts.subject,
|
|
47
|
-
body: opts.body,
|
|
48
|
-
priority: opts.priority,
|
|
49
|
-
source_service: opts.sourceService,
|
|
50
|
-
source_event: opts.sourceEvent,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (opts.json) {
|
|
54
|
-
console.log(JSON.stringify(notification, null, 2));
|
|
55
|
-
} else {
|
|
56
|
-
console.log(`Sent notification: ${notification.channel} -> ${notification.recipient} (${notification.id})`);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
program
|
|
61
|
-
.command("list")
|
|
62
|
-
.description("List notifications")
|
|
63
|
-
.option("--status <status>", "Filter by status (pending, sent, failed, read)")
|
|
64
|
-
.option("--channel <channel>", "Filter by channel")
|
|
65
|
-
.option("--priority <priority>", "Filter by priority")
|
|
66
|
-
.option("--recipient <recipient>", "Filter by recipient")
|
|
67
|
-
.option("--limit <n>", "Limit results")
|
|
68
|
-
.option("--json", "Output as JSON", false)
|
|
69
|
-
.action((opts) => {
|
|
70
|
-
const notifications = listNotifications({
|
|
71
|
-
status: opts.status,
|
|
72
|
-
channel: opts.channel,
|
|
73
|
-
priority: opts.priority,
|
|
74
|
-
recipient: opts.recipient,
|
|
75
|
-
limit: opts.limit ? parseInt(opts.limit) : undefined,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
if (opts.json) {
|
|
79
|
-
console.log(JSON.stringify(notifications, null, 2));
|
|
80
|
-
} else {
|
|
81
|
-
if (notifications.length === 0) {
|
|
82
|
-
console.log("No notifications found.");
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
for (const n of notifications) {
|
|
86
|
-
const subject = n.subject ? ` — ${n.subject}` : "";
|
|
87
|
-
console.log(` [${n.status}] ${n.channel} -> ${n.recipient}${subject} (${n.priority})`);
|
|
88
|
-
}
|
|
89
|
-
console.log(`\n${notifications.length} notification(s)`);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
program
|
|
94
|
-
.command("read")
|
|
95
|
-
.description("Mark a notification as read")
|
|
96
|
-
.argument("<id>", "Notification ID")
|
|
97
|
-
.option("--json", "Output as JSON", false)
|
|
98
|
-
.action((id, opts) => {
|
|
99
|
-
const notification = markRead(id);
|
|
100
|
-
if (!notification) {
|
|
101
|
-
console.error(`Notification '${id}' not found.`);
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (opts.json) {
|
|
106
|
-
console.log(JSON.stringify(notification, null, 2));
|
|
107
|
-
} else {
|
|
108
|
-
console.log(`Marked as read: ${notification.id}`);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
program
|
|
113
|
-
.command("read-all")
|
|
114
|
-
.description("Mark all notifications as read for a recipient")
|
|
115
|
-
.requiredOption("--recipient <recipient>", "Recipient")
|
|
116
|
-
.option("--json", "Output as JSON", false)
|
|
117
|
-
.action((opts) => {
|
|
118
|
-
const count = markAllRead(opts.recipient);
|
|
119
|
-
|
|
120
|
-
if (opts.json) {
|
|
121
|
-
console.log(JSON.stringify({ recipient: opts.recipient, marked_read: count }));
|
|
122
|
-
} else {
|
|
123
|
-
console.log(`Marked ${count} notification(s) as read for ${opts.recipient}`);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// --- Rules ---
|
|
128
|
-
|
|
129
|
-
const ruleCmd = program
|
|
130
|
-
.command("rule")
|
|
131
|
-
.description("Notification rule management");
|
|
132
|
-
|
|
133
|
-
ruleCmd
|
|
134
|
-
.command("create")
|
|
135
|
-
.description("Create a notification rule")
|
|
136
|
-
.requiredOption("--event <event>", "Trigger event")
|
|
137
|
-
.requiredOption("--channel <channel>", "Notification channel")
|
|
138
|
-
.requiredOption("--to <recipient>", "Recipient")
|
|
139
|
-
.option("--name <name>", "Rule name")
|
|
140
|
-
.option("--template <id>", "Template ID")
|
|
141
|
-
.option("--json", "Output as JSON", false)
|
|
142
|
-
.action((opts) => {
|
|
143
|
-
const rule = createRule({
|
|
144
|
-
name: opts.name,
|
|
145
|
-
trigger_event: opts.event,
|
|
146
|
-
channel: opts.channel,
|
|
147
|
-
recipient: opts.to,
|
|
148
|
-
template_id: opts.template,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (opts.json) {
|
|
152
|
-
console.log(JSON.stringify(rule, null, 2));
|
|
153
|
-
} else {
|
|
154
|
-
console.log(`Created rule: ${rule.trigger_event} -> ${rule.channel}:${rule.recipient} (${rule.id})`);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
ruleCmd
|
|
159
|
-
.command("list")
|
|
160
|
-
.description("List notification rules")
|
|
161
|
-
.option("--json", "Output as JSON", false)
|
|
162
|
-
.action((opts) => {
|
|
163
|
-
const rules = listRules();
|
|
164
|
-
|
|
165
|
-
if (opts.json) {
|
|
166
|
-
console.log(JSON.stringify(rules, null, 2));
|
|
167
|
-
} else {
|
|
168
|
-
if (rules.length === 0) {
|
|
169
|
-
console.log("No rules found.");
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
for (const r of rules) {
|
|
173
|
-
const name = r.name ? ` (${r.name})` : "";
|
|
174
|
-
const status = r.enabled ? "enabled" : "disabled";
|
|
175
|
-
console.log(` [${status}] ${r.trigger_event} -> ${r.channel}:${r.recipient}${name}`);
|
|
176
|
-
}
|
|
177
|
-
console.log(`\n${rules.length} rule(s)`);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
ruleCmd
|
|
182
|
-
.command("enable")
|
|
183
|
-
.description("Enable a rule")
|
|
184
|
-
.argument("<id>", "Rule ID")
|
|
185
|
-
.action((id) => {
|
|
186
|
-
const rule = enableRule(id);
|
|
187
|
-
if (!rule) {
|
|
188
|
-
console.error(`Rule '${id}' not found.`);
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
console.log(`Enabled rule ${id}`);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
ruleCmd
|
|
195
|
-
.command("disable")
|
|
196
|
-
.description("Disable a rule")
|
|
197
|
-
.argument("<id>", "Rule ID")
|
|
198
|
-
.action((id) => {
|
|
199
|
-
const rule = disableRule(id);
|
|
200
|
-
if (!rule) {
|
|
201
|
-
console.error(`Rule '${id}' not found.`);
|
|
202
|
-
process.exit(1);
|
|
203
|
-
}
|
|
204
|
-
console.log(`Disabled rule ${id}`);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
ruleCmd
|
|
208
|
-
.command("delete")
|
|
209
|
-
.description("Delete a rule")
|
|
210
|
-
.argument("<id>", "Rule ID")
|
|
211
|
-
.action((id) => {
|
|
212
|
-
const deleted = deleteRule(id);
|
|
213
|
-
if (deleted) {
|
|
214
|
-
console.log(`Deleted rule ${id}`);
|
|
215
|
-
} else {
|
|
216
|
-
console.error(`Rule '${id}' not found.`);
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// --- Templates ---
|
|
222
|
-
|
|
223
|
-
const templateCmd = program
|
|
224
|
-
.command("template")
|
|
225
|
-
.description("Notification template management");
|
|
226
|
-
|
|
227
|
-
templateCmd
|
|
228
|
-
.command("create")
|
|
229
|
-
.description("Create a notification template")
|
|
230
|
-
.requiredOption("--name <name>", "Template name")
|
|
231
|
-
.option("--channel <channel>", "Channel")
|
|
232
|
-
.option("--subject <subject>", "Subject template")
|
|
233
|
-
.option("--body <body>", "Body template")
|
|
234
|
-
.option("--variables <vars>", "Comma-separated variable names")
|
|
235
|
-
.option("--json", "Output as JSON", false)
|
|
236
|
-
.action((opts) => {
|
|
237
|
-
const template = createTemplate({
|
|
238
|
-
name: opts.name,
|
|
239
|
-
channel: opts.channel,
|
|
240
|
-
subject_template: opts.subject,
|
|
241
|
-
body_template: opts.body,
|
|
242
|
-
variables: opts.variables ? opts.variables.split(",").map((v: string) => v.trim()) : undefined,
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
if (opts.json) {
|
|
246
|
-
console.log(JSON.stringify(template, null, 2));
|
|
247
|
-
} else {
|
|
248
|
-
console.log(`Created template: ${template.name} (${template.id})`);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
templateCmd
|
|
253
|
-
.command("list")
|
|
254
|
-
.description("List notification templates")
|
|
255
|
-
.option("--json", "Output as JSON", false)
|
|
256
|
-
.action((opts) => {
|
|
257
|
-
const templates = listTemplates();
|
|
258
|
-
|
|
259
|
-
if (opts.json) {
|
|
260
|
-
console.log(JSON.stringify(templates, null, 2));
|
|
261
|
-
} else {
|
|
262
|
-
if (templates.length === 0) {
|
|
263
|
-
console.log("No templates found.");
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
for (const t of templates) {
|
|
267
|
-
const channel = t.channel ? ` [${t.channel}]` : "";
|
|
268
|
-
console.log(` ${t.name}${channel} (${t.id})`);
|
|
269
|
-
}
|
|
270
|
-
console.log(`\n${templates.length} template(s)`);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
templateCmd
|
|
275
|
-
.command("delete")
|
|
276
|
-
.description("Delete a template")
|
|
277
|
-
.argument("<id>", "Template ID")
|
|
278
|
-
.action((id) => {
|
|
279
|
-
const deleted = deleteTemplate(id);
|
|
280
|
-
if (deleted) {
|
|
281
|
-
console.log(`Deleted template ${id}`);
|
|
282
|
-
} else {
|
|
283
|
-
console.error(`Template '${id}' not found.`);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// --- Event Processing ---
|
|
289
|
-
|
|
290
|
-
program
|
|
291
|
-
.command("process")
|
|
292
|
-
.description("Process an event and trigger matching rules")
|
|
293
|
-
.requiredOption("--event <event>", "Event name")
|
|
294
|
-
.option("--data <json>", "Event data as JSON", "{}")
|
|
295
|
-
.option("--json", "Output as JSON", false)
|
|
296
|
-
.action((opts) => {
|
|
297
|
-
let data: Record<string, string>;
|
|
298
|
-
try {
|
|
299
|
-
data = JSON.parse(opts.data);
|
|
300
|
-
} catch {
|
|
301
|
-
console.error("Invalid JSON data");
|
|
302
|
-
process.exit(1);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const notifications = processEvent(opts.event, data);
|
|
306
|
-
|
|
307
|
-
if (opts.json) {
|
|
308
|
-
console.log(JSON.stringify(notifications, null, 2));
|
|
309
|
-
} else {
|
|
310
|
-
if (notifications.length === 0) {
|
|
311
|
-
console.log(`No rules matched event '${opts.event}'.`);
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
console.log(`Processed event '${opts.event}' — created ${notifications.length} notification(s):`);
|
|
315
|
-
for (const n of notifications) {
|
|
316
|
-
console.log(` ${n.channel} -> ${n.recipient}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
// --- Stats ---
|
|
322
|
-
|
|
323
|
-
program
|
|
324
|
-
.command("stats")
|
|
325
|
-
.description("Show notification statistics")
|
|
326
|
-
.option("--json", "Output as JSON", false)
|
|
327
|
-
.action((opts) => {
|
|
328
|
-
const stats = getNotificationStats();
|
|
329
|
-
|
|
330
|
-
if (opts.json) {
|
|
331
|
-
console.log(JSON.stringify(stats, null, 2));
|
|
332
|
-
} else {
|
|
333
|
-
console.log(`Total notifications: ${stats.total}`);
|
|
334
|
-
console.log("\nBy channel:");
|
|
335
|
-
for (const [channel, count] of Object.entries(stats.by_channel)) {
|
|
336
|
-
console.log(` ${channel}: ${count}`);
|
|
337
|
-
}
|
|
338
|
-
console.log("\nBy status:");
|
|
339
|
-
for (const [status, count] of Object.entries(stats.by_status)) {
|
|
340
|
-
console.log(` ${status}: ${count}`);
|
|
341
|
-
}
|
|
342
|
-
console.log("\nBy priority:");
|
|
343
|
-
for (const [priority, count] of Object.entries(stats.by_priority)) {
|
|
344
|
-
console.log(` ${priority}: ${count}`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
program.parse(process.argv);
|