@hasna/microservices 0.0.2 → 0.0.4
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/bin/index.js +70 -0
- package/bin/mcp.js +71 -1
- package/dist/index.js +70 -0
- package/microservices/microservice-ads/package.json +27 -0
- package/microservices/microservice-ads/src/cli/index.ts +407 -0
- package/microservices/microservice-ads/src/db/campaigns.ts +493 -0
- package/microservices/microservice-ads/src/db/database.ts +93 -0
- package/microservices/microservice-ads/src/db/migrations.ts +60 -0
- package/microservices/microservice-ads/src/index.ts +39 -0
- package/microservices/microservice-ads/src/mcp/index.ts +320 -0
- package/microservices/microservice-contracts/package.json +27 -0
- package/microservices/microservice-contracts/src/cli/index.ts +383 -0
- package/microservices/microservice-contracts/src/db/contracts.ts +496 -0
- package/microservices/microservice-contracts/src/db/database.ts +93 -0
- package/microservices/microservice-contracts/src/db/migrations.ts +58 -0
- package/microservices/microservice-contracts/src/index.ts +43 -0
- package/microservices/microservice-contracts/src/mcp/index.ts +308 -0
- package/microservices/microservice-domains/package.json +27 -0
- package/microservices/microservice-domains/src/cli/index.ts +438 -0
- package/microservices/microservice-domains/src/db/database.ts +93 -0
- package/microservices/microservice-domains/src/db/domains.ts +551 -0
- package/microservices/microservice-domains/src/db/migrations.ts +60 -0
- package/microservices/microservice-domains/src/index.ts +44 -0
- package/microservices/microservice-domains/src/mcp/index.ts +368 -0
- package/microservices/microservice-hiring/package.json +27 -0
- package/microservices/microservice-hiring/src/cli/index.ts +431 -0
- package/microservices/microservice-hiring/src/db/database.ts +93 -0
- package/microservices/microservice-hiring/src/db/hiring.ts +582 -0
- package/microservices/microservice-hiring/src/db/migrations.ts +68 -0
- package/microservices/microservice-hiring/src/index.ts +51 -0
- package/microservices/microservice-hiring/src/mcp/index.ts +464 -0
- package/microservices/microservice-payments/package.json +27 -0
- package/microservices/microservice-payments/src/cli/index.ts +357 -0
- package/microservices/microservice-payments/src/db/database.ts +93 -0
- package/microservices/microservice-payments/src/db/migrations.ts +63 -0
- package/microservices/microservice-payments/src/db/payments.ts +652 -0
- package/microservices/microservice-payments/src/index.ts +51 -0
- package/microservices/microservice-payments/src/mcp/index.ts +460 -0
- package/microservices/microservice-payroll/package.json +27 -0
- package/microservices/microservice-payroll/src/cli/index.ts +374 -0
- package/microservices/microservice-payroll/src/db/database.ts +93 -0
- package/microservices/microservice-payroll/src/db/migrations.ts +69 -0
- package/microservices/microservice-payroll/src/db/payroll.ts +741 -0
- package/microservices/microservice-payroll/src/index.ts +48 -0
- package/microservices/microservice-payroll/src/mcp/index.ts +420 -0
- package/microservices/microservice-shipping/package.json +27 -0
- package/microservices/microservice-shipping/src/cli/index.ts +398 -0
- package/microservices/microservice-shipping/src/db/database.ts +93 -0
- package/microservices/microservice-shipping/src/db/migrations.ts +61 -0
- package/microservices/microservice-shipping/src/db/shipping.ts +643 -0
- package/microservices/microservice-shipping/src/index.ts +53 -0
- package/microservices/microservice-shipping/src/mcp/index.ts +385 -0
- package/microservices/microservice-social/package.json +27 -0
- package/microservices/microservice-social/src/cli/index.ts +447 -0
- package/microservices/microservice-social/src/db/database.ts +93 -0
- package/microservices/microservice-social/src/db/migrations.ts +55 -0
- package/microservices/microservice-social/src/db/social.ts +672 -0
- package/microservices/microservice-social/src/index.ts +46 -0
- package/microservices/microservice-social/src/mcp/index.ts +435 -0
- package/microservices/microservice-subscriptions/package.json +27 -0
- package/microservices/microservice-subscriptions/src/cli/index.ts +400 -0
- package/microservices/microservice-subscriptions/src/db/database.ts +93 -0
- package/microservices/microservice-subscriptions/src/db/migrations.ts +57 -0
- package/microservices/microservice-subscriptions/src/db/subscriptions.ts +692 -0
- package/microservices/microservice-subscriptions/src/index.ts +41 -0
- package/microservices/microservice-subscriptions/src/mcp/index.ts +365 -0
- package/microservices/microservice-transcriber/package.json +28 -0
- package/microservices/microservice-transcriber/src/cli/index.ts +1347 -0
- package/microservices/microservice-transcriber/src/db/annotations.ts +37 -0
- package/microservices/microservice-transcriber/src/db/database.ts +82 -0
- package/microservices/microservice-transcriber/src/db/migrations.ts +72 -0
- package/microservices/microservice-transcriber/src/db/transcripts.ts +395 -0
- package/microservices/microservice-transcriber/src/index.ts +43 -0
- package/microservices/microservice-transcriber/src/lib/config.ts +77 -0
- package/microservices/microservice-transcriber/src/lib/diff.ts +91 -0
- package/microservices/microservice-transcriber/src/lib/downloader.ts +570 -0
- package/microservices/microservice-transcriber/src/lib/feeds.ts +62 -0
- package/microservices/microservice-transcriber/src/lib/live.ts +94 -0
- package/microservices/microservice-transcriber/src/lib/notion.ts +129 -0
- package/microservices/microservice-transcriber/src/lib/providers.ts +713 -0
- package/microservices/microservice-transcriber/src/lib/summarizer.ts +147 -0
- package/microservices/microservice-transcriber/src/lib/translator.ts +75 -0
- package/microservices/microservice-transcriber/src/lib/webhook.ts +37 -0
- package/microservices/microservice-transcriber/src/mcp/index.ts +1070 -0
- package/microservices/microservice-transcriber/src/server/index.ts +199 -0
- package/package.json +1 -1
- package/microservices/microservice-invoices/dashboard/dist/assets/index-Bngq7FNM.css +0 -1
- package/microservices/microservice-invoices/dashboard/dist/assets/index-aHW4ARZR.js +0 -124
- package/microservices/microservice-invoices/dashboard/dist/index.html +0 -13
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import {
|
|
5
|
+
createDomain,
|
|
6
|
+
getDomain,
|
|
7
|
+
listDomains,
|
|
8
|
+
updateDomain,
|
|
9
|
+
deleteDomain,
|
|
10
|
+
searchDomains,
|
|
11
|
+
listExpiring,
|
|
12
|
+
listSslExpiring,
|
|
13
|
+
getDomainStats,
|
|
14
|
+
getByRegistrar,
|
|
15
|
+
createDnsRecord,
|
|
16
|
+
listDnsRecords,
|
|
17
|
+
updateDnsRecord,
|
|
18
|
+
deleteDnsRecord,
|
|
19
|
+
createAlert,
|
|
20
|
+
listAlerts,
|
|
21
|
+
deleteAlert,
|
|
22
|
+
} from "../db/domains.js";
|
|
23
|
+
|
|
24
|
+
const program = new Command();
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.name("microservice-domains")
|
|
28
|
+
.description("Domain portfolio and DNS management microservice")
|
|
29
|
+
.version("0.0.1");
|
|
30
|
+
|
|
31
|
+
// --- Domains ---
|
|
32
|
+
|
|
33
|
+
program
|
|
34
|
+
.command("add")
|
|
35
|
+
.description("Add a new domain")
|
|
36
|
+
.requiredOption("--name <name>", "Domain name (e.g. example.com)")
|
|
37
|
+
.option("--registrar <registrar>", "Domain registrar")
|
|
38
|
+
.option("--status <status>", "Status (active/expired/transferring/redemption)", "active")
|
|
39
|
+
.option("--registered-at <date>", "Registration date (ISO)")
|
|
40
|
+
.option("--expires-at <date>", "Expiration date (ISO)")
|
|
41
|
+
.option("--no-auto-renew", "Disable auto-renew")
|
|
42
|
+
.option("--nameservers <ns>", "Comma-separated nameservers")
|
|
43
|
+
.option("--ssl-expires-at <date>", "SSL expiration date (ISO)")
|
|
44
|
+
.option("--ssl-issuer <issuer>", "SSL certificate issuer")
|
|
45
|
+
.option("--notes <notes>", "Notes")
|
|
46
|
+
.option("--json", "Output as JSON", false)
|
|
47
|
+
.action((opts) => {
|
|
48
|
+
const domain = createDomain({
|
|
49
|
+
name: opts.name,
|
|
50
|
+
registrar: opts.registrar,
|
|
51
|
+
status: opts.status,
|
|
52
|
+
registered_at: opts.registeredAt,
|
|
53
|
+
expires_at: opts.expiresAt,
|
|
54
|
+
auto_renew: opts.autoRenew,
|
|
55
|
+
nameservers: opts.nameservers
|
|
56
|
+
? opts.nameservers.split(",").map((s: string) => s.trim())
|
|
57
|
+
: undefined,
|
|
58
|
+
ssl_expires_at: opts.sslExpiresAt,
|
|
59
|
+
ssl_issuer: opts.sslIssuer,
|
|
60
|
+
notes: opts.notes,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (opts.json) {
|
|
64
|
+
console.log(JSON.stringify(domain, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
console.log(`Created domain: ${domain.name} (${domain.id})`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
program
|
|
71
|
+
.command("get")
|
|
72
|
+
.description("Get a domain by ID")
|
|
73
|
+
.argument("<id>", "Domain ID")
|
|
74
|
+
.option("--json", "Output as JSON", false)
|
|
75
|
+
.action((id, opts) => {
|
|
76
|
+
const domain = getDomain(id);
|
|
77
|
+
if (!domain) {
|
|
78
|
+
console.error(`Domain '${id}' not found.`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (opts.json) {
|
|
83
|
+
console.log(JSON.stringify(domain, null, 2));
|
|
84
|
+
} else {
|
|
85
|
+
console.log(`${domain.name} [${domain.status}]`);
|
|
86
|
+
if (domain.registrar) console.log(` Registrar: ${domain.registrar}`);
|
|
87
|
+
if (domain.expires_at) console.log(` Expires: ${domain.expires_at}`);
|
|
88
|
+
if (domain.ssl_expires_at) console.log(` SSL Expires: ${domain.ssl_expires_at}`);
|
|
89
|
+
if (domain.ssl_issuer) console.log(` SSL Issuer: ${domain.ssl_issuer}`);
|
|
90
|
+
console.log(` Auto-renew: ${domain.auto_renew ? "yes" : "no"}`);
|
|
91
|
+
if (domain.nameservers.length) console.log(` Nameservers: ${domain.nameservers.join(", ")}`);
|
|
92
|
+
if (domain.notes) console.log(` Notes: ${domain.notes}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
program
|
|
97
|
+
.command("list")
|
|
98
|
+
.description("List domains")
|
|
99
|
+
.option("--search <query>", "Search by name, registrar, or notes")
|
|
100
|
+
.option("--status <status>", "Filter by status")
|
|
101
|
+
.option("--registrar <registrar>", "Filter by registrar")
|
|
102
|
+
.option("--limit <n>", "Limit results")
|
|
103
|
+
.option("--json", "Output as JSON", false)
|
|
104
|
+
.action((opts) => {
|
|
105
|
+
const domains = listDomains({
|
|
106
|
+
search: opts.search,
|
|
107
|
+
status: opts.status,
|
|
108
|
+
registrar: opts.registrar,
|
|
109
|
+
limit: opts.limit ? parseInt(opts.limit) : undefined,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (opts.json) {
|
|
113
|
+
console.log(JSON.stringify(domains, null, 2));
|
|
114
|
+
} else {
|
|
115
|
+
if (domains.length === 0) {
|
|
116
|
+
console.log("No domains found.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const d of domains) {
|
|
120
|
+
const expires = d.expires_at ? ` (expires ${d.expires_at})` : "";
|
|
121
|
+
console.log(` ${d.name} [${d.status}]${expires}`);
|
|
122
|
+
}
|
|
123
|
+
console.log(`\n${domains.length} domain(s)`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
program
|
|
128
|
+
.command("update")
|
|
129
|
+
.description("Update a domain")
|
|
130
|
+
.argument("<id>", "Domain ID")
|
|
131
|
+
.option("--name <name>", "Domain name")
|
|
132
|
+
.option("--registrar <registrar>", "Registrar")
|
|
133
|
+
.option("--status <status>", "Status")
|
|
134
|
+
.option("--registered-at <date>", "Registration date")
|
|
135
|
+
.option("--expires-at <date>", "Expiration date")
|
|
136
|
+
.option("--auto-renew <bool>", "Auto-renew (true/false)")
|
|
137
|
+
.option("--nameservers <ns>", "Comma-separated nameservers")
|
|
138
|
+
.option("--ssl-expires-at <date>", "SSL expiration date")
|
|
139
|
+
.option("--ssl-issuer <issuer>", "SSL issuer")
|
|
140
|
+
.option("--notes <notes>", "Notes")
|
|
141
|
+
.option("--json", "Output as JSON", false)
|
|
142
|
+
.action((id, opts) => {
|
|
143
|
+
const input: Record<string, unknown> = {};
|
|
144
|
+
if (opts.name !== undefined) input.name = opts.name;
|
|
145
|
+
if (opts.registrar !== undefined) input.registrar = opts.registrar;
|
|
146
|
+
if (opts.status !== undefined) input.status = opts.status;
|
|
147
|
+
if (opts.registeredAt !== undefined) input.registered_at = opts.registeredAt;
|
|
148
|
+
if (opts.expiresAt !== undefined) input.expires_at = opts.expiresAt;
|
|
149
|
+
if (opts.autoRenew !== undefined) input.auto_renew = opts.autoRenew === "true";
|
|
150
|
+
if (opts.nameservers !== undefined)
|
|
151
|
+
input.nameservers = opts.nameservers.split(",").map((s: string) => s.trim());
|
|
152
|
+
if (opts.sslExpiresAt !== undefined) input.ssl_expires_at = opts.sslExpiresAt;
|
|
153
|
+
if (opts.sslIssuer !== undefined) input.ssl_issuer = opts.sslIssuer;
|
|
154
|
+
if (opts.notes !== undefined) input.notes = opts.notes;
|
|
155
|
+
|
|
156
|
+
const domain = updateDomain(id, input);
|
|
157
|
+
if (!domain) {
|
|
158
|
+
console.error(`Domain '${id}' not found.`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (opts.json) {
|
|
163
|
+
console.log(JSON.stringify(domain, null, 2));
|
|
164
|
+
} else {
|
|
165
|
+
console.log(`Updated: ${domain.name}`);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
program
|
|
170
|
+
.command("delete")
|
|
171
|
+
.description("Delete a domain")
|
|
172
|
+
.argument("<id>", "Domain ID")
|
|
173
|
+
.action((id) => {
|
|
174
|
+
const deleted = deleteDomain(id);
|
|
175
|
+
if (deleted) {
|
|
176
|
+
console.log(`Deleted domain ${id}`);
|
|
177
|
+
} else {
|
|
178
|
+
console.error(`Domain '${id}' not found.`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
program
|
|
184
|
+
.command("search")
|
|
185
|
+
.description("Search domains")
|
|
186
|
+
.argument("<query>", "Search term")
|
|
187
|
+
.option("--json", "Output as JSON", false)
|
|
188
|
+
.action((query, opts) => {
|
|
189
|
+
const results = searchDomains(query);
|
|
190
|
+
|
|
191
|
+
if (opts.json) {
|
|
192
|
+
console.log(JSON.stringify(results, null, 2));
|
|
193
|
+
} else {
|
|
194
|
+
if (results.length === 0) {
|
|
195
|
+
console.log(`No domains matching "${query}".`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
for (const d of results) {
|
|
199
|
+
console.log(` ${d.name} [${d.status}]`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
program
|
|
205
|
+
.command("expiring")
|
|
206
|
+
.description("List domains expiring within N days")
|
|
207
|
+
.option("--days <n>", "Number of days ahead", "30")
|
|
208
|
+
.option("--json", "Output as JSON", false)
|
|
209
|
+
.action((opts) => {
|
|
210
|
+
const days = parseInt(opts.days);
|
|
211
|
+
const domains = listExpiring(days);
|
|
212
|
+
|
|
213
|
+
if (opts.json) {
|
|
214
|
+
console.log(JSON.stringify(domains, null, 2));
|
|
215
|
+
} else {
|
|
216
|
+
if (domains.length === 0) {
|
|
217
|
+
console.log(`No domains expiring within ${days} days.`);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
console.log(`Domains expiring within ${days} days:`);
|
|
221
|
+
for (const d of domains) {
|
|
222
|
+
console.log(` ${d.name} — expires ${d.expires_at}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
program
|
|
228
|
+
.command("ssl")
|
|
229
|
+
.description("List domains with SSL expiring within N days")
|
|
230
|
+
.option("--days <n>", "Number of days ahead", "30")
|
|
231
|
+
.option("--json", "Output as JSON", false)
|
|
232
|
+
.action((opts) => {
|
|
233
|
+
const days = parseInt(opts.days);
|
|
234
|
+
const domains = listSslExpiring(days);
|
|
235
|
+
|
|
236
|
+
if (opts.json) {
|
|
237
|
+
console.log(JSON.stringify(domains, null, 2));
|
|
238
|
+
} else {
|
|
239
|
+
if (domains.length === 0) {
|
|
240
|
+
console.log(`No SSL certificates expiring within ${days} days.`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
console.log(`SSL certificates expiring within ${days} days:`);
|
|
244
|
+
for (const d of domains) {
|
|
245
|
+
console.log(` ${d.name} — SSL expires ${d.ssl_expires_at} (${d.ssl_issuer || "unknown issuer"})`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
program
|
|
251
|
+
.command("stats")
|
|
252
|
+
.description("Show domain portfolio statistics")
|
|
253
|
+
.option("--json", "Output as JSON", false)
|
|
254
|
+
.action((opts) => {
|
|
255
|
+
const stats = getDomainStats();
|
|
256
|
+
|
|
257
|
+
if (opts.json) {
|
|
258
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
259
|
+
} else {
|
|
260
|
+
console.log("Domain Portfolio Stats:");
|
|
261
|
+
console.log(` Total: ${stats.total}`);
|
|
262
|
+
console.log(` Active: ${stats.active}`);
|
|
263
|
+
console.log(` Expired: ${stats.expired}`);
|
|
264
|
+
console.log(` Transferring: ${stats.transferring}`);
|
|
265
|
+
console.log(` Redemption: ${stats.redemption}`);
|
|
266
|
+
console.log(` Auto-renew enabled: ${stats.auto_renew_enabled}`);
|
|
267
|
+
console.log(` Expiring (30 days): ${stats.expiring_30_days}`);
|
|
268
|
+
console.log(` SSL expiring (30 days): ${stats.ssl_expiring_30_days}`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// --- DNS Records ---
|
|
273
|
+
|
|
274
|
+
const dnsCmd = program
|
|
275
|
+
.command("dns")
|
|
276
|
+
.description("DNS record management");
|
|
277
|
+
|
|
278
|
+
dnsCmd
|
|
279
|
+
.command("list")
|
|
280
|
+
.description("List DNS records for a domain")
|
|
281
|
+
.argument("<domain-id>", "Domain ID")
|
|
282
|
+
.option("--type <type>", "Filter by record type (A/AAAA/CNAME/MX/TXT/NS/SRV)")
|
|
283
|
+
.option("--json", "Output as JSON", false)
|
|
284
|
+
.action((domainId, opts) => {
|
|
285
|
+
const records = listDnsRecords(domainId, opts.type);
|
|
286
|
+
|
|
287
|
+
if (opts.json) {
|
|
288
|
+
console.log(JSON.stringify(records, null, 2));
|
|
289
|
+
} else {
|
|
290
|
+
if (records.length === 0) {
|
|
291
|
+
console.log("No DNS records found.");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
for (const r of records) {
|
|
295
|
+
const priority = r.priority !== null ? ` (priority: ${r.priority})` : "";
|
|
296
|
+
console.log(` ${r.type}\t${r.name}\t${r.value}\tTTL:${r.ttl}${priority}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
dnsCmd
|
|
302
|
+
.command("add")
|
|
303
|
+
.description("Add a DNS record")
|
|
304
|
+
.requiredOption("--domain <id>", "Domain ID")
|
|
305
|
+
.requiredOption("--type <type>", "Record type (A/AAAA/CNAME/MX/TXT/NS/SRV)")
|
|
306
|
+
.requiredOption("--name <name>", "Record name")
|
|
307
|
+
.requiredOption("--value <value>", "Record value")
|
|
308
|
+
.option("--ttl <ttl>", "TTL in seconds", "3600")
|
|
309
|
+
.option("--priority <n>", "Priority (for MX/SRV)")
|
|
310
|
+
.option("--json", "Output as JSON", false)
|
|
311
|
+
.action((opts) => {
|
|
312
|
+
const record = createDnsRecord({
|
|
313
|
+
domain_id: opts.domain,
|
|
314
|
+
type: opts.type,
|
|
315
|
+
name: opts.name,
|
|
316
|
+
value: opts.value,
|
|
317
|
+
ttl: parseInt(opts.ttl),
|
|
318
|
+
priority: opts.priority ? parseInt(opts.priority) : undefined,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (opts.json) {
|
|
322
|
+
console.log(JSON.stringify(record, null, 2));
|
|
323
|
+
} else {
|
|
324
|
+
console.log(`Created DNS record: ${record.type} ${record.name} -> ${record.value} (${record.id})`);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
dnsCmd
|
|
329
|
+
.command("update")
|
|
330
|
+
.description("Update a DNS record")
|
|
331
|
+
.argument("<id>", "Record ID")
|
|
332
|
+
.option("--type <type>", "Record type")
|
|
333
|
+
.option("--name <name>", "Record name")
|
|
334
|
+
.option("--value <value>", "Record value")
|
|
335
|
+
.option("--ttl <ttl>", "TTL in seconds")
|
|
336
|
+
.option("--priority <n>", "Priority")
|
|
337
|
+
.option("--json", "Output as JSON", false)
|
|
338
|
+
.action((id, opts) => {
|
|
339
|
+
const input: Record<string, unknown> = {};
|
|
340
|
+
if (opts.type !== undefined) input.type = opts.type;
|
|
341
|
+
if (opts.name !== undefined) input.name = opts.name;
|
|
342
|
+
if (opts.value !== undefined) input.value = opts.value;
|
|
343
|
+
if (opts.ttl !== undefined) input.ttl = parseInt(opts.ttl);
|
|
344
|
+
if (opts.priority !== undefined) input.priority = parseInt(opts.priority);
|
|
345
|
+
|
|
346
|
+
const record = updateDnsRecord(id, input);
|
|
347
|
+
if (!record) {
|
|
348
|
+
console.error(`DNS record '${id}' not found.`);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (opts.json) {
|
|
353
|
+
console.log(JSON.stringify(record, null, 2));
|
|
354
|
+
} else {
|
|
355
|
+
console.log(`Updated DNS record: ${record.type} ${record.name} -> ${record.value}`);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
dnsCmd
|
|
360
|
+
.command("remove")
|
|
361
|
+
.description("Remove a DNS record")
|
|
362
|
+
.argument("<id>", "Record ID")
|
|
363
|
+
.action((id) => {
|
|
364
|
+
const deleted = deleteDnsRecord(id);
|
|
365
|
+
if (deleted) {
|
|
366
|
+
console.log(`Deleted DNS record ${id}`);
|
|
367
|
+
} else {
|
|
368
|
+
console.error(`DNS record '${id}' not found.`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// --- Alerts ---
|
|
374
|
+
|
|
375
|
+
const alertCmd = program
|
|
376
|
+
.command("alert")
|
|
377
|
+
.description("Alert management");
|
|
378
|
+
|
|
379
|
+
alertCmd
|
|
380
|
+
.command("set")
|
|
381
|
+
.description("Set an alert for a domain")
|
|
382
|
+
.requiredOption("--domain <id>", "Domain ID")
|
|
383
|
+
.requiredOption("--type <type>", "Alert type (expiry/ssl_expiry/dns_change)")
|
|
384
|
+
.option("--days-before <n>", "Trigger N days before")
|
|
385
|
+
.option("--json", "Output as JSON", false)
|
|
386
|
+
.action((opts) => {
|
|
387
|
+
const alert = createAlert({
|
|
388
|
+
domain_id: opts.domain,
|
|
389
|
+
type: opts.type,
|
|
390
|
+
trigger_days_before: opts.daysBefore ? parseInt(opts.daysBefore) : undefined,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (opts.json) {
|
|
394
|
+
console.log(JSON.stringify(alert, null, 2));
|
|
395
|
+
} else {
|
|
396
|
+
const daysBefore = alert.trigger_days_before ? ` (${alert.trigger_days_before} days before)` : "";
|
|
397
|
+
console.log(`Created alert: ${alert.type}${daysBefore} for domain ${alert.domain_id} (${alert.id})`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
alertCmd
|
|
402
|
+
.command("list")
|
|
403
|
+
.description("List alerts for a domain")
|
|
404
|
+
.argument("<domain-id>", "Domain ID")
|
|
405
|
+
.option("--json", "Output as JSON", false)
|
|
406
|
+
.action((domainId, opts) => {
|
|
407
|
+
const alerts = listAlerts(domainId);
|
|
408
|
+
|
|
409
|
+
if (opts.json) {
|
|
410
|
+
console.log(JSON.stringify(alerts, null, 2));
|
|
411
|
+
} else {
|
|
412
|
+
if (alerts.length === 0) {
|
|
413
|
+
console.log("No alerts set.");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
for (const a of alerts) {
|
|
417
|
+
const daysBefore = a.trigger_days_before ? ` (${a.trigger_days_before} days before)` : "";
|
|
418
|
+
const sent = a.sent_at ? ` — sent ${a.sent_at}` : "";
|
|
419
|
+
console.log(` ${a.type}${daysBefore}${sent}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
alertCmd
|
|
425
|
+
.command("remove")
|
|
426
|
+
.description("Remove an alert")
|
|
427
|
+
.argument("<id>", "Alert ID")
|
|
428
|
+
.action((id) => {
|
|
429
|
+
const deleted = deleteAlert(id);
|
|
430
|
+
if (deleted) {
|
|
431
|
+
console.log(`Deleted alert ${id}`);
|
|
432
|
+
} else {
|
|
433
|
+
console.error(`Alert '${id}' not found.`);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database connection for microservice-domains
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Database } from "bun:sqlite";
|
|
6
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { MIGRATIONS } from "./migrations.js";
|
|
9
|
+
|
|
10
|
+
let _db: Database | null = null;
|
|
11
|
+
|
|
12
|
+
function getDbPath(): string {
|
|
13
|
+
// Environment variable override
|
|
14
|
+
if (process.env["MICROSERVICES_DIR"]) {
|
|
15
|
+
return join(process.env["MICROSERVICES_DIR"], "microservice-domains", "data.db");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check for .microservices in current or parent directories
|
|
19
|
+
let dir = resolve(process.cwd());
|
|
20
|
+
while (true) {
|
|
21
|
+
const candidate = join(dir, ".microservices", "microservice-domains", "data.db");
|
|
22
|
+
const msDir = join(dir, ".microservices");
|
|
23
|
+
if (existsSync(msDir)) return candidate;
|
|
24
|
+
const parent = dirname(dir);
|
|
25
|
+
if (parent === dir) break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Global fallback
|
|
30
|
+
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
31
|
+
return join(home, ".microservices", "microservice-domains", "data.db");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ensureDir(filePath: string): void {
|
|
35
|
+
const dir = dirname(resolve(filePath));
|
|
36
|
+
if (!existsSync(dir)) {
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getDatabase(): Database {
|
|
42
|
+
if (_db) return _db;
|
|
43
|
+
|
|
44
|
+
const dbPath = getDbPath();
|
|
45
|
+
ensureDir(dbPath);
|
|
46
|
+
|
|
47
|
+
_db = new Database(dbPath);
|
|
48
|
+
_db.exec("PRAGMA journal_mode = WAL");
|
|
49
|
+
_db.exec("PRAGMA foreign_keys = ON");
|
|
50
|
+
|
|
51
|
+
// Create migrations table
|
|
52
|
+
_db.exec(`
|
|
53
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
54
|
+
id INTEGER PRIMARY KEY,
|
|
55
|
+
name TEXT NOT NULL,
|
|
56
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
57
|
+
)
|
|
58
|
+
`);
|
|
59
|
+
|
|
60
|
+
// Apply pending migrations
|
|
61
|
+
const applied = _db
|
|
62
|
+
.query("SELECT id FROM _migrations ORDER BY id")
|
|
63
|
+
.all() as { id: number }[];
|
|
64
|
+
const appliedIds = new Set(applied.map((r) => r.id));
|
|
65
|
+
|
|
66
|
+
for (const migration of MIGRATIONS) {
|
|
67
|
+
if (appliedIds.has(migration.id)) continue;
|
|
68
|
+
|
|
69
|
+
_db.exec("BEGIN");
|
|
70
|
+
try {
|
|
71
|
+
_db.exec(migration.sql);
|
|
72
|
+
_db.prepare("INSERT INTO _migrations (id, name) VALUES (?, ?)").run(
|
|
73
|
+
migration.id,
|
|
74
|
+
migration.name
|
|
75
|
+
);
|
|
76
|
+
_db.exec("COMMIT");
|
|
77
|
+
} catch (error) {
|
|
78
|
+
_db.exec("ROLLBACK");
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Migration ${migration.id} (${migration.name}) failed: ${error instanceof Error ? error.message : String(error)}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return _db;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function closeDatabase(): void {
|
|
89
|
+
if (_db) {
|
|
90
|
+
_db.close();
|
|
91
|
+
_db = null;
|
|
92
|
+
}
|
|
93
|
+
}
|