@jango-blockchained/hoox-cli 0.5.1 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/bin/hoox.js +3 -4
  2. package/dist/index.js +32545 -0
  3. package/package.json +10 -15
  4. package/src/commands/check/check-command.test.ts +0 -476
  5. package/src/commands/check/check-command.ts +0 -1144
  6. package/src/commands/check/index.ts +0 -10
  7. package/src/commands/check/prerequisites-command.test.ts +0 -19
  8. package/src/commands/check/prerequisites-command.ts +0 -96
  9. package/src/commands/check/types.ts +0 -103
  10. package/src/commands/clone/clone-command.test.ts +0 -442
  11. package/src/commands/clone/clone-command.ts +0 -431
  12. package/src/commands/clone/index.ts +0 -1
  13. package/src/commands/config/config-command.test.ts +0 -583
  14. package/src/commands/config/config-command.ts +0 -897
  15. package/src/commands/config/env-command.test.ts +0 -43
  16. package/src/commands/config/env-command.ts +0 -295
  17. package/src/commands/config/index.ts +0 -3
  18. package/src/commands/config/kv-command.test.ts +0 -14
  19. package/src/commands/config/kv-command.ts +0 -290
  20. package/src/commands/dashboard/dashboard-command.test.ts +0 -47
  21. package/src/commands/dashboard/dashboard-command.ts +0 -128
  22. package/src/commands/dashboard/index.ts +0 -1
  23. package/src/commands/db/db-command.test.ts +0 -21
  24. package/src/commands/db/db-command.ts +0 -312
  25. package/src/commands/db/index.ts +0 -1
  26. package/src/commands/deploy/deploy-command.test.ts +0 -304
  27. package/src/commands/deploy/deploy-command.ts +0 -1135
  28. package/src/commands/deploy/index.ts +0 -2
  29. package/src/commands/deploy/telegram-service.ts +0 -76
  30. package/src/commands/deploy/types.ts +0 -34
  31. package/src/commands/dev/dev-command.test.ts +0 -383
  32. package/src/commands/dev/dev-command.ts +0 -398
  33. package/src/commands/dev/index.ts +0 -1
  34. package/src/commands/infra/index.ts +0 -5
  35. package/src/commands/infra/infra-command.test.ts +0 -719
  36. package/src/commands/infra/infra-command.ts +0 -974
  37. package/src/commands/infra/types.ts +0 -23
  38. package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
  39. package/src/commands/init/cli-provisioner.ts +0 -71
  40. package/src/commands/init/index.ts +0 -1
  41. package/src/commands/init/init-command.test.ts +0 -876
  42. package/src/commands/init/init-command.ts +0 -593
  43. package/src/commands/init/types.ts +0 -23
  44. package/src/commands/logs/index.ts +0 -1
  45. package/src/commands/logs/logs-command.test.ts +0 -294
  46. package/src/commands/logs/logs-command.ts +0 -452
  47. package/src/commands/monitor/index.ts +0 -2
  48. package/src/commands/monitor/monitor-command.test.ts +0 -263
  49. package/src/commands/monitor/monitor-command.ts +0 -269
  50. package/src/commands/monitor/monitor-service.ts +0 -65
  51. package/src/commands/monitor/types.ts +0 -13
  52. package/src/commands/repair/index.ts +0 -2
  53. package/src/commands/repair/repair-command.test.ts +0 -224
  54. package/src/commands/repair/repair-command.ts +0 -220
  55. package/src/commands/repair/repair-service.ts +0 -138
  56. package/src/commands/repair/types.ts +0 -13
  57. package/src/commands/test/index.ts +0 -2
  58. package/src/commands/test/test-command.test.ts +0 -319
  59. package/src/commands/test/test-command.ts +0 -406
  60. package/src/commands/tui/index.ts +0 -1
  61. package/src/commands/tui/tui-command.ts +0 -87
  62. package/src/commands/update/index.ts +0 -1
  63. package/src/commands/update/update-command.ts +0 -168
  64. package/src/commands/waf/index.ts +0 -2
  65. package/src/commands/waf/types.ts +0 -48
  66. package/src/commands/waf/waf-command.test.ts +0 -506
  67. package/src/commands/waf/waf-command.ts +0 -548
  68. package/src/index.ts +0 -269
  69. package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
  70. package/src/services/cloudflare/cloudflare-service.ts +0 -433
  71. package/src/services/cloudflare/index.ts +0 -2
  72. package/src/services/cloudflare/types.ts +0 -30
  73. package/src/services/config/config-service.test.ts +0 -395
  74. package/src/services/config/config-service.ts +0 -207
  75. package/src/services/config/index.ts +0 -9
  76. package/src/services/config/types.ts +0 -66
  77. package/src/services/db/db-service.test.ts +0 -51
  78. package/src/services/db/db-service.ts +0 -131
  79. package/src/services/db/index.ts +0 -1
  80. package/src/services/docker/docker-service.ts +0 -155
  81. package/src/services/docker/index.ts +0 -1
  82. package/src/services/env/env-service.test.ts +0 -236
  83. package/src/services/env/env-service.ts +0 -386
  84. package/src/services/env/index.ts +0 -1
  85. package/src/services/kv/index.ts +0 -1
  86. package/src/services/kv/kv-sync-service.test.ts +0 -38
  87. package/src/services/kv/kv-sync-service.ts +0 -234
  88. package/src/services/prerequisites/index.ts +0 -1
  89. package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
  90. package/src/services/prerequisites/prerequisites-service.ts +0 -377
  91. package/src/services/prerequisites/types.ts +0 -48
  92. package/src/services/secrets/index.ts +0 -11
  93. package/src/services/secrets/secrets-service.test.ts +0 -495
  94. package/src/services/secrets/secrets-service.ts +0 -287
  95. package/src/services/secrets/types.ts +0 -50
  96. package/src/services/update/index.ts +0 -2
  97. package/src/services/update/update-service.test.ts +0 -76
  98. package/src/services/update/update-service.ts +0 -193
  99. package/src/ui/banner.ts +0 -141
  100. package/src/ui/index.ts +0 -8
  101. package/src/ui/menu.ts +0 -490
  102. package/src/utils/errors.test.ts +0 -69
  103. package/src/utils/errors.ts +0 -23
  104. package/src/utils/formatters.test.ts +0 -180
  105. package/src/utils/formatters.ts +0 -269
  106. package/src/utils/theme.ts +0 -94
@@ -1,548 +0,0 @@
1
- /**
2
- * `hoox waf` command — Cloudflare WAF (Web Application Firewall) management.
3
- *
4
- * Subcommands:
5
- * waf status Show WAF status for the zone
6
- * waf rules list List all active firewall rules
7
- * waf rules add <type> <value> Add a WAF rule
8
- * waf rules remove <id> Remove a WAF rule by ID
9
- * waf mode enable Enable WAF protection
10
- * waf mode disable Disable WAF protection
11
- */
12
-
13
- import type { Command } from "commander";
14
- import { CloudflareService } from "../../services/cloudflare/cloudflare-service.js";
15
- import type { WranglerResult } from "../../services/cloudflare/types.js";
16
- import { CLIError, ExitCode } from "../../utils/errors.js";
17
- import {
18
- formatSuccess,
19
- formatError,
20
- formatTable,
21
- } from "../../utils/formatters.js";
22
- import { theme, icons } from "../../utils/theme.js";
23
- import type { FormatOptions } from "../../utils/formatters.js";
24
- import type { WafStatus, WafRule, WafRuleInput } from "./types.js";
25
-
26
- // ---------------------------------------------------------------------------
27
- // Cloudflare API helpers
28
- // ---------------------------------------------------------------------------
29
-
30
- const CF_API_BASE = "https://api.cloudflare.com/client/v4";
31
-
32
- /**
33
- * Reads the Cloudflare API token and account ID from environment variables.
34
- * Required for direct REST API calls (wrangler doesn't have native WAF commands).
35
- */
36
- function getCredentials(): { token: string; accountId: string } {
37
- const token = process.env.CLOUDFLARE_API_TOKEN;
38
- const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
39
- if (!token) {
40
- throw new CLIError(
41
- "CLOUDFLARE_API_TOKEN environment variable is not set. Set it or run `wrangler login`.",
42
- ExitCode.ERROR
43
- );
44
- }
45
- if (!accountId) {
46
- throw new CLIError(
47
- "CLOUDFLARE_ACCOUNT_ID environment variable is not set.",
48
- ExitCode.ERROR
49
- );
50
- }
51
- return { token, accountId };
52
- }
53
-
54
- /**
55
- * Makes an authenticated request to the Cloudflare REST API.
56
- * Returns a WranglerResult-style discriminated union for consistent error handling.
57
- */
58
- async function cfApi<T>(
59
- method: "GET" | "POST" | "PATCH" | "DELETE",
60
- path: string,
61
- body?: unknown
62
- ): Promise<WranglerResult<T>> {
63
- const { token } = getCredentials();
64
- const url = `${CF_API_BASE}${path}`;
65
-
66
- try {
67
- const response = await fetch(url, {
68
- method,
69
- headers: {
70
- Authorization: `Bearer ${token}`,
71
- "Content-Type": "application/json",
72
- },
73
- body: body !== undefined ? JSON.stringify(body) : undefined,
74
- });
75
-
76
- const json = (await response.json()) as {
77
- success: boolean;
78
- result: T;
79
- errors: Array<{ message: string }>;
80
- };
81
-
82
- if (!response.ok || !json.success) {
83
- const errorMsg =
84
- json.errors?.map((e) => e.message).join("; ") ||
85
- `HTTP ${response.status}`;
86
- return { ok: false, error: errorMsg };
87
- }
88
-
89
- return { ok: true, value: json.result };
90
- } catch (err) {
91
- const message = err instanceof Error ? err.message : String(err);
92
- return { ok: false, error: `Cloudflare API request failed: ${message}` };
93
- }
94
- }
95
-
96
- // ---------------------------------------------------------------------------
97
- // WAF API operations
98
- // ---------------------------------------------------------------------------
99
-
100
- interface CfWafSetting {
101
- id: string;
102
- value: string;
103
- }
104
-
105
- interface CfFirewallRule {
106
- id: string;
107
- description: string;
108
- action: string;
109
- filter: { id: string; expression: string };
110
- created_on: string;
111
- modified_on: string;
112
- }
113
-
114
- interface CfAnalyticsOverview {
115
- totals: {
116
- threats: number;
117
- };
118
- }
119
-
120
- /**
121
- * Builds expression from rule type and value.
122
- * ip-allowlist → (ip.src eq {value})
123
- * ip-blocklist → (ip.src eq {value})
124
- * rate-limit → handled as custom with rate-limit expression
125
- * custom → uses value as raw expression
126
- */
127
- function buildExpression(type: WafRuleInput["type"], value: string): string {
128
- switch (type) {
129
- case "ip-allowlist":
130
- return `(ip.src eq ${value})`;
131
- case "ip-blocklist":
132
- return `(ip.src eq ${value})`;
133
- case "rate-limit":
134
- // Rate limiting is configured via a separate API; here we build a
135
- // basic rate-limit expression that can be extended.
136
- return `(http.request.uri.path contains "/")`;
137
- case "custom":
138
- return value;
139
- }
140
- }
141
-
142
- /**
143
- * Maps rule type and value to a Cloudflare firewall rule action.
144
- */
145
- function mapAction(type: WafRuleInput["type"]): string {
146
- switch (type) {
147
- case "ip-allowlist":
148
- return "allow";
149
- case "ip-blocklist":
150
- return "block";
151
- case "rate-limit":
152
- return "managed_challenge";
153
- case "custom":
154
- return "block";
155
- }
156
- }
157
-
158
- /**
159
- * Fetches the Cloudflare zone to operate on. Uses CloudflareService.zonesList()
160
- * and picks the first zone. Falls back to env CLOUDFLARE_ZONE_ID.
161
- */
162
- async function resolveZone(
163
- cf: CloudflareService
164
- ): Promise<{ id: string; name: string }> {
165
- // Try explicit env var first
166
- if (process.env.CLOUDFLARE_ZONE_ID) {
167
- return {
168
- id: process.env.CLOUDFLARE_ZONE_ID,
169
- name: process.env.CLOUDFLARE_ZONE_ID,
170
- };
171
- }
172
-
173
- const result = await cf.zonesList();
174
- if (!result.ok) {
175
- throw new CLIError(`Failed to list zones: ${result.error}`, ExitCode.ERROR);
176
- }
177
-
178
- // wrangler zones list outputs lines like "zone-name (zone-id)"
179
- const lines = result.value.split("\n").filter(Boolean);
180
- if (lines.length === 0) {
181
- throw new CLIError(
182
- "No zones found. Set CLOUDFLARE_ZONE_ID environment variable.",
183
- ExitCode.ERROR
184
- );
185
- }
186
-
187
- // Parse first zone: "domain.com (abc123...)"
188
- const match = lines[0].match(/^(.+?)\s+\((.+?)\)\s*$/);
189
- if (match) {
190
- return { name: match[1], id: match[2] };
191
- }
192
-
193
- // Fallback: use the first line as ID
194
- return { name: lines[0], id: lines[0] };
195
- }
196
-
197
- // ---------------------------------------------------------------------------
198
- // Command action: waf status
199
- // ---------------------------------------------------------------------------
200
-
201
- async function handleStatus(opts: FormatOptions): Promise<void> {
202
- const cf = new CloudflareService();
203
-
204
- const zone = await resolveZone(cf);
205
-
206
- // Fetch WAF setting
207
- const wafResult = await cfApi<CfWafSetting>(
208
- "GET",
209
- `/zones/${zone.id}/settings/waf`
210
- );
211
- if (!wafResult.ok) {
212
- throw new CLIError(
213
- `Failed to get WAF status: ${wafResult.error}`,
214
- ExitCode.ERROR
215
- );
216
- }
217
-
218
- // Fetch firewall rules count
219
- const rulesResult = await cfApi<CfFirewallRule[]>(
220
- "GET",
221
- `/zones/${zone.id}/firewall/rules?per_page=50`
222
- );
223
- const activeRulesCount = rulesResult.ok ? rulesResult.value.length : 0;
224
-
225
- // Fetch recent blocks (last 24h analytics overview)
226
- let recentBlocks = 0;
227
- const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
228
- const analyticsResult = await cfApi<CfAnalyticsOverview>(
229
- "GET",
230
- `/zones/${zone.id}/analytics/dashboard?since=${encodeURIComponent(since)}&continuous=true`
231
- );
232
- if (analyticsResult.ok) {
233
- recentBlocks = analyticsResult.value.totals?.threats ?? 0;
234
- }
235
-
236
- const status: WafStatus = {
237
- enabled: wafResult.value.value === "on",
238
- mode: wafResult.value.value,
239
- activeRulesCount,
240
- recentBlocks,
241
- zoneId: zone.id,
242
- zoneName: zone.name,
243
- };
244
-
245
- if (opts.json) {
246
- process.stdout.write(JSON.stringify(status, null, 2) + "\n");
247
- return;
248
- }
249
-
250
- process.stdout.write(theme.heading(`\nWAF status for ${zone.name}\n\n`));
251
-
252
- const colorFn = status.enabled ? theme.success : theme.error;
253
- process.stdout.write(
254
- ` Status: ${colorFn(status.enabled ? `${icons.success} ENABLED` : `${icons.error} DISABLED`)}\n`
255
- );
256
- process.stdout.write(` Mode: ${status.mode}\n`);
257
- process.stdout.write(` Rules: ${status.activeRulesCount} active\n`);
258
- process.stdout.write(` Blocks: ${status.recentBlocks} (last 24h)\n`);
259
- process.stdout.write(` Zone ID: ${theme.dim(zone.id)}\n\n`);
260
- }
261
-
262
- // ---------------------------------------------------------------------------
263
- // Command action: waf rules list
264
- // ---------------------------------------------------------------------------
265
-
266
- async function handleRulesList(opts: FormatOptions): Promise<void> {
267
- const cf = new CloudflareService();
268
- const zone = await resolveZone(cf);
269
-
270
- const result = await cfApi<CfFirewallRule[]>(
271
- "GET",
272
- `/zones/${zone.id}/firewall/rules?per_page=50`
273
- );
274
- if (!result.ok) {
275
- throw new CLIError(
276
- `Failed to list firewall rules: ${result.error}`,
277
- ExitCode.ERROR
278
- );
279
- }
280
-
281
- const rules: WafRule[] = result.value.map((r) => ({
282
- id: r.id,
283
- description: r.description,
284
- mode: r.action,
285
- expression: r.filter?.expression ?? "(unknown)",
286
- created_at: r.created_on,
287
- updated_at: r.modified_on,
288
- }));
289
-
290
- if (opts.json) {
291
- process.stdout.write(JSON.stringify(rules, null, 2) + "\n");
292
- return;
293
- }
294
-
295
- if (rules.length === 0) {
296
- process.stdout.write(
297
- `${theme.dim(`${icons.info} No firewall rules configured.\n`)}`
298
- );
299
- return;
300
- }
301
-
302
- process.stdout.write(theme.heading(`\nWAF rules for ${zone.name}\n\n`));
303
-
304
- const rows = rules.map((r) => ({
305
- ID: r.id.substring(0, 8) + "...",
306
- Description: r.description || "(no description)",
307
- Mode: r.mode,
308
- Expression:
309
- r.expression.length > 40
310
- ? r.expression.substring(0, 37) + "..."
311
- : r.expression,
312
- }));
313
-
314
- formatTable(rows, opts);
315
- }
316
-
317
- // ---------------------------------------------------------------------------
318
- // Command action: waf rules add
319
- // ---------------------------------------------------------------------------
320
-
321
- async function handleRulesAdd(
322
- type: string,
323
- value: string,
324
- opts: FormatOptions
325
- ): Promise<void> {
326
- const validTypes = ["ip-allowlist", "ip-blocklist", "rate-limit", "custom"];
327
- if (!validTypes.includes(type)) {
328
- throw new CLIError(
329
- `Invalid rule type "${type}". Must be one of: ${validTypes.join(", ")}`,
330
- ExitCode.INVALID_USAGE
331
- );
332
- }
333
-
334
- const ruleInput: WafRuleInput = {
335
- type: type as WafRuleInput["type"],
336
- value,
337
- };
338
-
339
- const cf = new CloudflareService();
340
- const zone = await resolveZone(cf);
341
-
342
- const expression = buildExpression(ruleInput.type, value);
343
- const action = mapAction(ruleInput.type);
344
-
345
- const body = {
346
- description: ruleInput.description ?? `${ruleInput.type} rule for ${value}`,
347
- action,
348
- filter: { expression },
349
- };
350
-
351
- // Create filter first, then rule referencing the filter
352
- const filterResult = await cfApi<{ id: string }>(
353
- "POST",
354
- `/zones/${zone.id}/filters`,
355
- { expression }
356
- );
357
- if (!filterResult.ok) {
358
- throw new CLIError(
359
- `Failed to create filter: ${filterResult.error}`,
360
- ExitCode.ERROR
361
- );
362
- }
363
-
364
- const ruleBody = {
365
- description: body.description,
366
- action,
367
- filter: { id: filterResult.value.id },
368
- };
369
-
370
- const ruleResult = await cfApi<CfFirewallRule>(
371
- "POST",
372
- `/zones/${zone.id}/firewall/rules`,
373
- [ruleBody] // Cloudflare expects an array of rules
374
- );
375
- if (!ruleResult.ok) {
376
- throw new CLIError(
377
- `Failed to create firewall rule: ${ruleResult.error}`,
378
- ExitCode.ERROR
379
- );
380
- }
381
-
382
- // Cloudflare returns an array of created rules
383
- const created = Array.isArray(ruleResult.value)
384
- ? (ruleResult.value[0] as unknown as CfFirewallRule)
385
- : (ruleResult.value as unknown as CfFirewallRule);
386
-
387
- formatSuccess(
388
- `WAF rule added (${created.id}) — ${created.action} for "${value}"`,
389
- opts
390
- );
391
- }
392
-
393
- // ---------------------------------------------------------------------------
394
- // Command action: waf rules remove
395
- // ---------------------------------------------------------------------------
396
-
397
- async function handleRulesRemove(
398
- ruleId: string,
399
- opts: FormatOptions
400
- ): Promise<void> {
401
- const cf = new CloudflareService();
402
- const zone = await resolveZone(cf);
403
-
404
- const result = await cfApi<{ id: string }>(
405
- "DELETE",
406
- `/zones/${zone.id}/firewall/rules/${ruleId}`
407
- );
408
- if (!result.ok) {
409
- throw new CLIError(
410
- `Failed to remove firewall rule: ${result.error}`,
411
- ExitCode.ERROR
412
- );
413
- }
414
-
415
- formatSuccess(`WAF rule ${ruleId} removed`, opts);
416
- }
417
-
418
- // ---------------------------------------------------------------------------
419
- // Command action: waf mode enable / disable
420
- // ---------------------------------------------------------------------------
421
-
422
- async function handleMode(
423
- mode: "on" | "off",
424
- opts: FormatOptions
425
- ): Promise<void> {
426
- const cf = new CloudflareService();
427
- const zone = await resolveZone(cf);
428
-
429
- const result = await cfApi<CfWafSetting>(
430
- "PATCH",
431
- `/zones/${zone.id}/settings/waf`,
432
- { value: mode }
433
- );
434
- if (!result.ok) {
435
- throw new CLIError(
436
- `Failed to ${mode === "on" ? "enable" : "disable"} WAF: ${result.error}`,
437
- ExitCode.ERROR
438
- );
439
- }
440
-
441
- const label = mode === "on" ? "enabled" : "disabled";
442
- formatSuccess(`WAF ${label} on zone ${zone.name}`, opts);
443
- }
444
-
445
- // ---------------------------------------------------------------------------
446
- // Command registration
447
- // ---------------------------------------------------------------------------
448
-
449
- /**
450
- * Registers the `hoox waf` command and all subcommands on the given
451
- * Commander.js program instance.
452
- */
453
- export function registerWafCommand(program: Command): void {
454
- const waf = program
455
- .command("waf")
456
- .description("Manage Cloudflare WAF (Web Application Firewall)");
457
-
458
- // -- waf status ------------------------------------------------------------
459
- waf
460
- .command("status")
461
- .description(
462
- "Show WAF status (enabled/disabled, active rules, recent blocks)"
463
- )
464
- .action(async (_, cmd: Command) => {
465
- const opts: FormatOptions = cmd.parent!.parent!.opts();
466
- try {
467
- await handleStatus(opts);
468
- } catch (err) {
469
- formatError(err instanceof Error ? err : String(err), opts);
470
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
471
- }
472
- });
473
-
474
- // -- waf rules -------------------------------------------------------------
475
- const rules = waf.command("rules").description("Manage WAF firewall rules");
476
-
477
- rules
478
- .command("list")
479
- .description("List all WAF firewall rules")
480
- .action(async (_, cmd: Command) => {
481
- const opts: FormatOptions = cmd.parent!.parent!.parent!.opts();
482
- try {
483
- await handleRulesList(opts);
484
- } catch (err) {
485
- formatError(err instanceof Error ? err : String(err), opts);
486
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
487
- }
488
- });
489
-
490
- rules
491
- .command("add <type> <value>")
492
- .description(
493
- "Add a WAF rule. Types: ip-allowlist, ip-blocklist, rate-limit, custom"
494
- )
495
- .action(async (type: string, value: string, _, cmd: Command) => {
496
- const opts: FormatOptions = cmd.parent!.parent!.parent!.opts();
497
- try {
498
- await handleRulesAdd(type, value, opts);
499
- } catch (err) {
500
- formatError(err instanceof Error ? err : String(err), opts);
501
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
502
- }
503
- });
504
-
505
- rules
506
- .command("remove <ruleId>")
507
- .description("Remove a WAF rule by ID")
508
- .action(async (ruleId: string, _, cmd: Command) => {
509
- const opts: FormatOptions = cmd.parent!.parent!.parent!.opts();
510
- try {
511
- await handleRulesRemove(ruleId, opts);
512
- } catch (err) {
513
- formatError(err instanceof Error ? err : String(err), opts);
514
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
515
- }
516
- });
517
-
518
- // -- waf mode --------------------------------------------------------------
519
- const mode = waf
520
- .command("mode")
521
- .description("Enable or disable WAF protection");
522
-
523
- mode
524
- .command("enable")
525
- .description("Enable WAF protection on the zone")
526
- .action(async (_, cmd: Command) => {
527
- const opts: FormatOptions = cmd.parent!.parent!.parent!.opts();
528
- try {
529
- await handleMode("on", opts);
530
- } catch (err) {
531
- formatError(err instanceof Error ? err : String(err), opts);
532
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
533
- }
534
- });
535
-
536
- mode
537
- .command("disable")
538
- .description("Disable WAF protection on the zone")
539
- .action(async (_, cmd: Command) => {
540
- const opts: FormatOptions = cmd.parent!.parent!.parent!.opts();
541
- try {
542
- await handleMode("off", opts);
543
- } catch (err) {
544
- formatError(err instanceof Error ? err : String(err), opts);
545
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
546
- }
547
- });
548
- }