@lamalibre/create-lamaste 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +118 -0
- package/README.md +114 -0
- package/bin/create-lamaste.js +15 -0
- package/package.json +54 -0
- package/scripts/bundle-vendor.js +110 -0
- package/scripts/lamaste-cert-info +62 -0
- package/scripts/lamaste-certbot +96 -0
- package/scripts/lamaste-priv +693 -0
- package/src/index.js +646 -0
- package/src/lib/accounts.js +86 -0
- package/src/lib/binaries.js +137 -0
- package/src/lib/cert-help-page.js +160 -0
- package/src/lib/env.js +132 -0
- package/src/lib/http-redirect.js +38 -0
- package/src/lib/ownership.js +231 -0
- package/src/lib/relay-services.js +103 -0
- package/src/lib/secrets.js +19 -0
- package/src/lib/service-config.js +311 -0
- package/src/lib/summary.js +131 -0
- package/src/lib/wrappers.js +63 -0
- package/src/tasks/gatekeeper.js +233 -0
- package/src/tasks/harden.js +293 -0
- package/src/tasks/mtls.js +193 -0
- package/src/tasks/nginx.js +265 -0
- package/src/tasks/node.js +110 -0
- package/src/tasks/panel.js +408 -0
- package/src/tasks/redeploy.js +438 -0
- package/vendor/docs/00-introduction/how-it-works.md +447 -0
- package/vendor/docs/00-introduction/quickstart.md +472 -0
- package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
- package/vendor/docs/01-concepts/authentication.md +464 -0
- package/vendor/docs/01-concepts/authorization.md +242 -0
- package/vendor/docs/01-concepts/certificates.md +452 -0
- package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
- package/vendor/docs/01-concepts/mtls.md +490 -0
- package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
- package/vendor/docs/01-concepts/security-model.md +600 -0
- package/vendor/docs/01-concepts/tickets.md +414 -0
- package/vendor/docs/01-concepts/tunneling.md +367 -0
- package/vendor/docs/02-guides/agent-setup.md +194 -0
- package/vendor/docs/02-guides/certificate-management.md +341 -0
- package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
- package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
- package/vendor/docs/02-guides/disaster-recovery.md +464 -0
- package/vendor/docs/02-guides/first-tunnel.md +230 -0
- package/vendor/docs/02-guides/installation.md +393 -0
- package/vendor/docs/02-guides/managing-users.md +255 -0
- package/vendor/docs/02-guides/onboarding.md +244 -0
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
- package/vendor/docs/02-guides/static-sites.md +364 -0
- package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
- package/vendor/docs/03-architecture/installer-flow.md +151 -0
- package/vendor/docs/03-architecture/installer.md +445 -0
- package/vendor/docs/03-architecture/management-flow.md +288 -0
- package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
- package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
- package/vendor/docs/03-architecture/overview.md +541 -0
- package/vendor/docs/03-architecture/panel-client.md +416 -0
- package/vendor/docs/03-architecture/panel-server.md +695 -0
- package/vendor/docs/03-architecture/state-management.md +529 -0
- package/vendor/docs/03-architecture/system-overview.md +167 -0
- package/vendor/docs/04-api-reference/certificates.md +1141 -0
- package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
- package/vendor/docs/04-api-reference/onboarding.md +473 -0
- package/vendor/docs/04-api-reference/overview.md +404 -0
- package/vendor/docs/04-api-reference/services.md +267 -0
- package/vendor/docs/04-api-reference/settings.md +343 -0
- package/vendor/docs/04-api-reference/sites.md +716 -0
- package/vendor/docs/04-api-reference/system.md +268 -0
- package/vendor/docs/04-api-reference/tickets.md +618 -0
- package/vendor/docs/04-api-reference/tunnels.md +417 -0
- package/vendor/docs/04-api-reference/users.md +333 -0
- package/vendor/docs/05-operations/backup-and-restore.md +342 -0
- package/vendor/docs/05-operations/monitoring.md +285 -0
- package/vendor/docs/05-operations/uninstalling.md +344 -0
- package/vendor/docs/05-operations/upgrades.md +270 -0
- package/vendor/docs/06-reference/config-files.md +1080 -0
- package/vendor/docs/06-reference/glossary.md +528 -0
- package/vendor/docs/06-reference/installer-flags.md +260 -0
- package/vendor/docs/06-reference/ports-and-services.md +244 -0
- package/vendor/docs/06-reference/troubleshooting.md +665 -0
- package/vendor/docs/_index.json +233 -0
- package/vendor/docs/gatekeeper-design.md +953 -0
- package/vendor/docs/package.json +15 -0
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
- package/vendor/gatekeeper/dist/boot.d.ts +1 -0
- package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/boot.js +17 -0
- package/vendor/gatekeeper/dist/boot.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
- package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
- package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
- package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
- package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/cli/index.js +45 -0
- package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
- package/vendor/gatekeeper/dist/index.d.ts +9 -0
- package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/index.js +13 -0
- package/vendor/gatekeeper/dist/index.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/authz.js +42 -0
- package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
- package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
- package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/constants.js +79 -0
- package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
- package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/grants.js +314 -0
- package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
- package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/groups.js +247 -0
- package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
- package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/migration.js +79 -0
- package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
- package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
- package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
- package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
- package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/templates.js +151 -0
- package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
- package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/lib/types.js +2 -0
- package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
- package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
- package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/index.js +251 -0
- package/vendor/gatekeeper/dist/server/index.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
- package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
- package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
- package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
- package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
- package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
- package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
- package/vendor/gatekeeper/package.json +49 -0
- package/vendor/server/bin/lamaste-server.js +204 -0
- package/vendor/server/package.json +31 -0
- package/vendor/server/src/commands/certs.js +224 -0
- package/vendor/server/src/commands/chisel.js +132 -0
- package/vendor/server/src/commands/logs.js +49 -0
- package/vendor/server/src/commands/plugins.js +321 -0
- package/vendor/server/src/commands/reset-admin.js +478 -0
- package/vendor/server/src/commands/restart.js +94 -0
- package/vendor/server/src/commands/sites.js +257 -0
- package/vendor/server/src/commands/status.js +158 -0
- package/vendor/server/src/commands/tunnels.js +365 -0
- package/vendor/server/src/commands/uninstall.js +326 -0
- package/vendor/server/src/config.js +38 -0
- package/vendor/server/src/exec.js +26 -0
- package/vendor/server/src/logger.js +39 -0
- package/vendor/server/src/ndjson.js +56 -0
- package/vendor/server/src/panel-api.js +85 -0
- package/vendor/server/src/state.js +45 -0
- package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
- package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
- package/vendor/server-ui/dist/index.html +13 -0
- package/vendor/serverd/package.json +44 -0
- package/vendor/serverd/src/boot.js +13 -0
- package/vendor/serverd/src/index.js +191 -0
- package/vendor/serverd/src/lib/app-error.js +14 -0
- package/vendor/serverd/src/lib/authelia.js +85 -0
- package/vendor/serverd/src/lib/certbot.js +74 -0
- package/vendor/serverd/src/lib/chisel-args.js +6 -0
- package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
- package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
- package/vendor/serverd/src/lib/chisel-users.js +109 -0
- package/vendor/serverd/src/lib/chisel.js +81 -0
- package/vendor/serverd/src/lib/config.js +190 -0
- package/vendor/serverd/src/lib/constants.js +8 -0
- package/vendor/serverd/src/lib/csr-signing.js +331 -0
- package/vendor/serverd/src/lib/enrollment.js +302 -0
- package/vendor/serverd/src/lib/files.js +19 -0
- package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
- package/vendor/serverd/src/lib/invite-page.js +281 -0
- package/vendor/serverd/src/lib/ip-utils.js +79 -0
- package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
- package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
- package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
- package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
- package/vendor/serverd/src/lib/mtls.js +795 -0
- package/vendor/serverd/src/lib/nginx.js +1090 -0
- package/vendor/serverd/src/lib/pki-paths.js +41 -0
- package/vendor/serverd/src/lib/pki-sign.js +103 -0
- package/vendor/serverd/src/lib/plugins.js +538 -0
- package/vendor/serverd/src/lib/push-install.js +390 -0
- package/vendor/serverd/src/lib/revocation.js +112 -0
- package/vendor/serverd/src/lib/services.js +131 -0
- package/vendor/serverd/src/lib/session.js +151 -0
- package/vendor/serverd/src/lib/state-db.js +130 -0
- package/vendor/serverd/src/lib/state.js +140 -0
- package/vendor/serverd/src/lib/storage.js +63 -0
- package/vendor/serverd/src/lib/system-stats.js +58 -0
- package/vendor/serverd/src/lib/tickets.js +1556 -0
- package/vendor/serverd/src/lib/totp.js +76 -0
- package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
- package/vendor/serverd/src/lib/user-access-session.js +142 -0
- package/vendor/serverd/src/lib/user-access.js +52 -0
- package/vendor/serverd/src/middleware/errors.js +63 -0
- package/vendor/serverd/src/middleware/mtls.js +120 -0
- package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
- package/vendor/serverd/src/middleware/role-guard.js +58 -0
- package/vendor/serverd/src/middleware/twofa-session.js +65 -0
- package/vendor/serverd/src/middleware/user-access-session.js +52 -0
- package/vendor/serverd/src/routes/enrollment.js +120 -0
- package/vendor/serverd/src/routes/health.js +22 -0
- package/vendor/serverd/src/routes/invite.js +163 -0
- package/vendor/serverd/src/routes/management/agents.js +182 -0
- package/vendor/serverd/src/routes/management/certs.js +911 -0
- package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
- package/vendor/serverd/src/routes/management/identity.js +107 -0
- package/vendor/serverd/src/routes/management/invitations.js +166 -0
- package/vendor/serverd/src/routes/management/logs.js +218 -0
- package/vendor/serverd/src/routes/management/plugins.js +560 -0
- package/vendor/serverd/src/routes/management/services.js +63 -0
- package/vendor/serverd/src/routes/management/settings.js +325 -0
- package/vendor/serverd/src/routes/management/sites.js +522 -0
- package/vendor/serverd/src/routes/management/storage.js +192 -0
- package/vendor/serverd/src/routes/management/system.js +201 -0
- package/vendor/serverd/src/routes/management/tickets.js +457 -0
- package/vendor/serverd/src/routes/management/tunnels.js +709 -0
- package/vendor/serverd/src/routes/management/users.js +377 -0
- package/vendor/serverd/src/routes/management.js +36 -0
- package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
- package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
- package/vendor/serverd/src/routes/onboarding/index.js +18 -0
- package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
- package/vendor/serverd/src/routes/onboarding/status.js +12 -0
- package/vendor/serverd/src/routes/plugin-router.js +79 -0
- package/vendor/serverd/src/routes/user-access.js +389 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
readPlugins,
|
|
4
|
+
installPlugin,
|
|
5
|
+
uninstallPlugin,
|
|
6
|
+
enablePlugin,
|
|
7
|
+
disablePlugin,
|
|
8
|
+
getPluginCapabilities,
|
|
9
|
+
} from '../../lib/plugins.js';
|
|
10
|
+
import { setPluginCapabilities, loadAgentRegistry } from '../../lib/mtls.js';
|
|
11
|
+
import {
|
|
12
|
+
readPushInstallConfig,
|
|
13
|
+
readPushInstallSessions,
|
|
14
|
+
updatePushInstallConfigFields,
|
|
15
|
+
createPushInstallPolicy,
|
|
16
|
+
updatePushInstallPolicy,
|
|
17
|
+
deletePushInstallPolicy,
|
|
18
|
+
enableAgentPushInstall,
|
|
19
|
+
disableAgentPushInstall,
|
|
20
|
+
logPushInstallSession,
|
|
21
|
+
validatePushInstallAccess,
|
|
22
|
+
} from '../../lib/push-install.js';
|
|
23
|
+
|
|
24
|
+
// --- Zod schemas ---
|
|
25
|
+
|
|
26
|
+
const PluginNameParamSchema = z.object({
|
|
27
|
+
name: z
|
|
28
|
+
.string()
|
|
29
|
+
.min(1)
|
|
30
|
+
.max(100)
|
|
31
|
+
.regex(/^[a-z0-9-]+$/, 'Plugin name must contain only lowercase letters, numbers, and hyphens'),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const InstallBodySchema = z.object({
|
|
35
|
+
packageName: z
|
|
36
|
+
.string()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(200)
|
|
39
|
+
.regex(/^@lamalibre\//, 'Package must be in the @lamalibre/ scope'),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// --- Push install schemas ---
|
|
43
|
+
|
|
44
|
+
const IpEntrySchema = z.string().refine(
|
|
45
|
+
(val) => {
|
|
46
|
+
// Accept bare IPv4 or IPv4/CIDR
|
|
47
|
+
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
48
|
+
const cidr = /^(\d{1,3}\.){3}\d{1,3}\/(\d{1,2})$/;
|
|
49
|
+
|
|
50
|
+
if (ipv4.test(val)) return true;
|
|
51
|
+
|
|
52
|
+
if (cidr.test(val)) {
|
|
53
|
+
const prefix = parseInt(val.split('/')[1], 10);
|
|
54
|
+
return prefix >= 0 && prefix <= 32;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
},
|
|
59
|
+
{ message: 'Must be a valid IPv4 address or CIDR (e.g. 192.168.1.0/24)' },
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const PolicyIdSchema = z
|
|
63
|
+
.string()
|
|
64
|
+
.min(1)
|
|
65
|
+
.max(100)
|
|
66
|
+
.regex(/^[a-z0-9-]+$/, 'Policy ID must contain only lowercase letters, numbers, and hyphens');
|
|
67
|
+
|
|
68
|
+
const ScopedPackageSchema = z
|
|
69
|
+
.string()
|
|
70
|
+
.min(1)
|
|
71
|
+
.max(200)
|
|
72
|
+
.regex(/^@lamalibre\//, 'Package must be in the @lamalibre/ scope');
|
|
73
|
+
|
|
74
|
+
const CreatePolicySchema = z.object({
|
|
75
|
+
name: z.string().min(1).max(100),
|
|
76
|
+
id: PolicyIdSchema.optional(),
|
|
77
|
+
description: z.string().max(500).optional().default(''),
|
|
78
|
+
allowedIps: z.array(IpEntrySchema).optional().default([]),
|
|
79
|
+
deniedIps: z.array(IpEntrySchema).optional().default([]),
|
|
80
|
+
allowedPlugins: z.array(ScopedPackageSchema).optional().default([]),
|
|
81
|
+
allowedActions: z
|
|
82
|
+
.array(z.enum(['install', 'update', 'uninstall', 'check-prerequisites']))
|
|
83
|
+
.optional()
|
|
84
|
+
.default(['install', 'update', 'check-prerequisites']),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const UpdatePolicySchema = z.object({
|
|
88
|
+
name: z.string().min(1).max(100).optional(),
|
|
89
|
+
description: z.string().max(500).optional(),
|
|
90
|
+
allowedIps: z.array(IpEntrySchema).optional(),
|
|
91
|
+
deniedIps: z.array(IpEntrySchema).optional(),
|
|
92
|
+
allowedPlugins: z.array(ScopedPackageSchema).optional(),
|
|
93
|
+
allowedActions: z
|
|
94
|
+
.array(z.enum(['install', 'update', 'uninstall', 'check-prerequisites']))
|
|
95
|
+
.optional(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const UpdatePushInstallConfigSchema = z.object({
|
|
99
|
+
enabled: z.boolean().optional(),
|
|
100
|
+
defaultPolicy: PolicyIdSchema.optional(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const EnablePushInstallSchema = z.object({
|
|
104
|
+
durationMinutes: z.number().int().min(5).max(480),
|
|
105
|
+
policyId: PolicyIdSchema.optional(),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const AgentLabelParamSchema = z.object({
|
|
109
|
+
label: z
|
|
110
|
+
.string()
|
|
111
|
+
.min(1)
|
|
112
|
+
.max(50)
|
|
113
|
+
.regex(/^[a-z0-9-]+$/, 'Invalid agent label'),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const PushInstallCommandSchema = z
|
|
117
|
+
.object({
|
|
118
|
+
action: z.enum(['install', 'update', 'uninstall', 'check-prerequisites']),
|
|
119
|
+
packageName: z
|
|
120
|
+
.string()
|
|
121
|
+
.min(1)
|
|
122
|
+
.max(200)
|
|
123
|
+
.regex(/^@lamalibre\//, 'Package must be in the @lamalibre/ scope')
|
|
124
|
+
.optional(),
|
|
125
|
+
})
|
|
126
|
+
.refine(
|
|
127
|
+
(data) => {
|
|
128
|
+
if (data.action === 'install' || data.action === 'update' || data.action === 'uninstall') {
|
|
129
|
+
return !!data.packageName;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
message: 'packageName is required for install, update, and uninstall actions',
|
|
135
|
+
path: ['packageName'],
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
export default async function pluginRoutes(fastify, _opts) {
|
|
140
|
+
// ===========================================================================
|
|
141
|
+
// Plugin management routes
|
|
142
|
+
// ===========================================================================
|
|
143
|
+
|
|
144
|
+
// ------------------------------------------------------------------
|
|
145
|
+
// GET /plugins — list all plugins
|
|
146
|
+
// ------------------------------------------------------------------
|
|
147
|
+
fastify.get(
|
|
148
|
+
'/plugins',
|
|
149
|
+
{
|
|
150
|
+
preHandler: fastify.requireRole(['admin']),
|
|
151
|
+
},
|
|
152
|
+
async (_request, _reply) => {
|
|
153
|
+
const registry = await readPlugins();
|
|
154
|
+
return { plugins: registry.plugins };
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// ------------------------------------------------------------------
|
|
159
|
+
// GET /plugins/:name — get plugin details
|
|
160
|
+
// ------------------------------------------------------------------
|
|
161
|
+
fastify.get(
|
|
162
|
+
'/plugins/:name',
|
|
163
|
+
{
|
|
164
|
+
preHandler: fastify.requireRole(['admin']),
|
|
165
|
+
},
|
|
166
|
+
async (request, reply) => {
|
|
167
|
+
const params = PluginNameParamSchema.parse(request.params);
|
|
168
|
+
const registry = await readPlugins();
|
|
169
|
+
const plugin = registry.plugins.find((p) => p.name === params.name);
|
|
170
|
+
|
|
171
|
+
if (!plugin) {
|
|
172
|
+
return reply.code(404).send({ error: `Plugin "${params.name}" not found` });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return { plugin };
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// ------------------------------------------------------------------
|
|
180
|
+
// POST /plugins/install — install a plugin
|
|
181
|
+
// ------------------------------------------------------------------
|
|
182
|
+
fastify.post(
|
|
183
|
+
'/plugins/install',
|
|
184
|
+
{
|
|
185
|
+
preHandler: fastify.requireRole(['admin']),
|
|
186
|
+
},
|
|
187
|
+
async (request, reply) => {
|
|
188
|
+
const body = InstallBodySchema.parse(request.body);
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const plugin = await installPlugin(body.packageName, request.log);
|
|
192
|
+
// Refresh in-memory capability list so new plugin caps are valid immediately
|
|
193
|
+
setPluginCapabilities(await getPluginCapabilities());
|
|
194
|
+
return { ok: true, plugin };
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const statusCode = err.statusCode || 500;
|
|
197
|
+
return reply.code(statusCode).send({
|
|
198
|
+
error: err.message || 'Plugin installation failed',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// ------------------------------------------------------------------
|
|
205
|
+
// POST /plugins/:name/enable — enable a plugin
|
|
206
|
+
// ------------------------------------------------------------------
|
|
207
|
+
fastify.post(
|
|
208
|
+
'/plugins/:name/enable',
|
|
209
|
+
{
|
|
210
|
+
preHandler: fastify.requireRole(['admin']),
|
|
211
|
+
},
|
|
212
|
+
async (request, reply) => {
|
|
213
|
+
const params = PluginNameParamSchema.parse(request.params);
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const result = await enablePlugin(params.name, request.log);
|
|
217
|
+
setPluginCapabilities(await getPluginCapabilities());
|
|
218
|
+
return result;
|
|
219
|
+
} catch (err) {
|
|
220
|
+
const statusCode = err.statusCode || 500;
|
|
221
|
+
return reply.code(statusCode).send({
|
|
222
|
+
error: err.message || 'Failed to enable plugin',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// ------------------------------------------------------------------
|
|
229
|
+
// POST /plugins/:name/disable — disable a plugin
|
|
230
|
+
// ------------------------------------------------------------------
|
|
231
|
+
fastify.post(
|
|
232
|
+
'/plugins/:name/disable',
|
|
233
|
+
{
|
|
234
|
+
preHandler: fastify.requireRole(['admin']),
|
|
235
|
+
},
|
|
236
|
+
async (request, reply) => {
|
|
237
|
+
const params = PluginNameParamSchema.parse(request.params);
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const result = await disablePlugin(params.name, request.log);
|
|
241
|
+
setPluginCapabilities(await getPluginCapabilities());
|
|
242
|
+
return result;
|
|
243
|
+
} catch (err) {
|
|
244
|
+
const statusCode = err.statusCode || 500;
|
|
245
|
+
return reply.code(statusCode).send({
|
|
246
|
+
error: err.message || 'Failed to disable plugin',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// ------------------------------------------------------------------
|
|
253
|
+
// DELETE /plugins/:name — uninstall a plugin (must be disabled)
|
|
254
|
+
// ------------------------------------------------------------------
|
|
255
|
+
fastify.delete(
|
|
256
|
+
'/plugins/:name',
|
|
257
|
+
{
|
|
258
|
+
preHandler: fastify.requireRole(['admin']),
|
|
259
|
+
},
|
|
260
|
+
async (request, reply) => {
|
|
261
|
+
const params = PluginNameParamSchema.parse(request.params);
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
const result = await uninstallPlugin(params.name, request.log);
|
|
265
|
+
setPluginCapabilities(await getPluginCapabilities());
|
|
266
|
+
return result;
|
|
267
|
+
} catch (err) {
|
|
268
|
+
const statusCode = err.statusCode || 500;
|
|
269
|
+
return reply.code(statusCode).send({
|
|
270
|
+
error: err.message || 'Plugin uninstall failed',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
// ===========================================================================
|
|
277
|
+
// Push install routes
|
|
278
|
+
// ===========================================================================
|
|
279
|
+
|
|
280
|
+
// ------------------------------------------------------------------
|
|
281
|
+
// GET /plugins/push-install/config — get push install config
|
|
282
|
+
// ------------------------------------------------------------------
|
|
283
|
+
fastify.get(
|
|
284
|
+
'/plugins/push-install/config',
|
|
285
|
+
{
|
|
286
|
+
preHandler: fastify.requireRole(['admin']),
|
|
287
|
+
},
|
|
288
|
+
async (_request, _reply) => {
|
|
289
|
+
const config = await readPushInstallConfig();
|
|
290
|
+
return config;
|
|
291
|
+
},
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// ------------------------------------------------------------------
|
|
295
|
+
// PATCH /plugins/push-install/config — update push install config
|
|
296
|
+
// ------------------------------------------------------------------
|
|
297
|
+
fastify.patch(
|
|
298
|
+
'/plugins/push-install/config',
|
|
299
|
+
{
|
|
300
|
+
preHandler: fastify.requireRole(['admin']),
|
|
301
|
+
},
|
|
302
|
+
async (request, reply) => {
|
|
303
|
+
const body = UpdatePushInstallConfigSchema.parse(request.body);
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
const config = await updatePushInstallConfigFields(body);
|
|
307
|
+
return { ok: true, ...config };
|
|
308
|
+
} catch (err) {
|
|
309
|
+
const statusCode = err.statusCode || 500;
|
|
310
|
+
return reply.code(statusCode).send({
|
|
311
|
+
error: err.message || 'Failed to update push install config',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// ------------------------------------------------------------------
|
|
318
|
+
// GET /plugins/push-install/policies — list push install policies
|
|
319
|
+
// ------------------------------------------------------------------
|
|
320
|
+
fastify.get(
|
|
321
|
+
'/plugins/push-install/policies',
|
|
322
|
+
{
|
|
323
|
+
preHandler: fastify.requireRole(['admin']),
|
|
324
|
+
},
|
|
325
|
+
async (_request, _reply) => {
|
|
326
|
+
const config = await readPushInstallConfig();
|
|
327
|
+
return { policies: config.policies };
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
// ------------------------------------------------------------------
|
|
332
|
+
// POST /plugins/push-install/policies — create push install policy
|
|
333
|
+
// ------------------------------------------------------------------
|
|
334
|
+
fastify.post(
|
|
335
|
+
'/plugins/push-install/policies',
|
|
336
|
+
{
|
|
337
|
+
preHandler: fastify.requireRole(['admin']),
|
|
338
|
+
},
|
|
339
|
+
async (request, reply) => {
|
|
340
|
+
const body = CreatePolicySchema.parse(request.body);
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const policy = await createPushInstallPolicy(body);
|
|
344
|
+
return { ok: true, policy };
|
|
345
|
+
} catch (err) {
|
|
346
|
+
const statusCode = err.statusCode || 500;
|
|
347
|
+
return reply.code(statusCode).send({
|
|
348
|
+
error: err.message || 'Failed to create policy',
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// ------------------------------------------------------------------
|
|
355
|
+
// PATCH /plugins/push-install/policies/:id — update push install policy
|
|
356
|
+
// ------------------------------------------------------------------
|
|
357
|
+
fastify.patch(
|
|
358
|
+
'/plugins/push-install/policies/:id',
|
|
359
|
+
{
|
|
360
|
+
preHandler: fastify.requireRole(['admin']),
|
|
361
|
+
},
|
|
362
|
+
async (request, reply) => {
|
|
363
|
+
const { id } = z.object({ id: PolicyIdSchema }).parse(request.params);
|
|
364
|
+
const body = UpdatePolicySchema.parse(request.body);
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
const policy = await updatePushInstallPolicy(id, body);
|
|
368
|
+
return { ok: true, policy };
|
|
369
|
+
} catch (err) {
|
|
370
|
+
const statusCode = err.statusCode || 500;
|
|
371
|
+
return reply.code(statusCode).send({
|
|
372
|
+
error: err.message || 'Failed to update policy',
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// ------------------------------------------------------------------
|
|
379
|
+
// DELETE /plugins/push-install/policies/:id — delete push install policy
|
|
380
|
+
// ------------------------------------------------------------------
|
|
381
|
+
fastify.delete(
|
|
382
|
+
'/plugins/push-install/policies/:id',
|
|
383
|
+
{
|
|
384
|
+
preHandler: fastify.requireRole(['admin']),
|
|
385
|
+
},
|
|
386
|
+
async (request, reply) => {
|
|
387
|
+
const { id } = z.object({ id: PolicyIdSchema }).parse(request.params);
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const result = await deletePushInstallPolicy(id);
|
|
391
|
+
return result;
|
|
392
|
+
} catch (err) {
|
|
393
|
+
const statusCode = err.statusCode || 500;
|
|
394
|
+
return reply.code(statusCode).send({
|
|
395
|
+
error: err.message || 'Failed to delete policy',
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// ------------------------------------------------------------------
|
|
402
|
+
// POST /plugins/push-install/enable/:label — enable push install for agent
|
|
403
|
+
// ------------------------------------------------------------------
|
|
404
|
+
fastify.post(
|
|
405
|
+
'/plugins/push-install/enable/:label',
|
|
406
|
+
{
|
|
407
|
+
preHandler: fastify.requireRole(['admin']),
|
|
408
|
+
},
|
|
409
|
+
async (request, reply) => {
|
|
410
|
+
const params = AgentLabelParamSchema.parse(request.params);
|
|
411
|
+
const body = EnablePushInstallSchema.parse(request.body);
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const result = await enableAgentPushInstall(
|
|
415
|
+
params.label,
|
|
416
|
+
body.durationMinutes,
|
|
417
|
+
body.policyId,
|
|
418
|
+
);
|
|
419
|
+
return result;
|
|
420
|
+
} catch (err) {
|
|
421
|
+
const statusCode = err.statusCode || 500;
|
|
422
|
+
return reply.code(statusCode).send({
|
|
423
|
+
error: err.message || 'Failed to enable push install',
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// ------------------------------------------------------------------
|
|
430
|
+
// DELETE /plugins/push-install/enable/:label — disable push install for agent
|
|
431
|
+
// ------------------------------------------------------------------
|
|
432
|
+
fastify.delete(
|
|
433
|
+
'/plugins/push-install/enable/:label',
|
|
434
|
+
{
|
|
435
|
+
preHandler: fastify.requireRole(['admin']),
|
|
436
|
+
},
|
|
437
|
+
async (request, reply) => {
|
|
438
|
+
const params = AgentLabelParamSchema.parse(request.params);
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
const result = await disableAgentPushInstall(params.label);
|
|
442
|
+
return result;
|
|
443
|
+
} catch (err) {
|
|
444
|
+
const statusCode = err.statusCode || 500;
|
|
445
|
+
return reply.code(statusCode).send({
|
|
446
|
+
error: err.message || 'Failed to disable push install',
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// ------------------------------------------------------------------
|
|
453
|
+
// POST /plugins/push-install/:label — send push install command
|
|
454
|
+
// ------------------------------------------------------------------
|
|
455
|
+
fastify.post(
|
|
456
|
+
'/plugins/push-install/:label',
|
|
457
|
+
{
|
|
458
|
+
preHandler: fastify.requireRole(['admin']),
|
|
459
|
+
},
|
|
460
|
+
async (request, reply) => {
|
|
461
|
+
const params = AgentLabelParamSchema.parse(request.params);
|
|
462
|
+
const body = PushInstallCommandSchema.parse(request.body);
|
|
463
|
+
|
|
464
|
+
// Validate push install access (5-gate auth)
|
|
465
|
+
const access = await validatePushInstallAccess(params.label, request.ip);
|
|
466
|
+
if (!access.ok) {
|
|
467
|
+
return reply.code(access.statusCode).send({ error: access.error });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Check if the action is allowed by the policy
|
|
471
|
+
if (
|
|
472
|
+
access.policy.allowedActions &&
|
|
473
|
+
access.policy.allowedActions.length > 0 &&
|
|
474
|
+
!access.policy.allowedActions.includes(body.action)
|
|
475
|
+
) {
|
|
476
|
+
return reply.code(403).send({
|
|
477
|
+
error: `Action "${body.action}" is not allowed by the assigned policy`,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Check if the package is allowed by the policy
|
|
482
|
+
if (
|
|
483
|
+
body.packageName &&
|
|
484
|
+
access.policy.allowedPlugins &&
|
|
485
|
+
access.policy.allowedPlugins.length > 0 &&
|
|
486
|
+
!access.policy.allowedPlugins.includes(body.packageName)
|
|
487
|
+
) {
|
|
488
|
+
return reply.code(403).send({
|
|
489
|
+
error: `Package "${body.packageName}" is not allowed by the assigned policy`,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Log the session
|
|
494
|
+
await logPushInstallSession({
|
|
495
|
+
agentLabel: params.label,
|
|
496
|
+
action: body.action,
|
|
497
|
+
packageName: body.packageName || null,
|
|
498
|
+
sourceIp: request.ip,
|
|
499
|
+
policyId: access.policy.id,
|
|
500
|
+
status: 'sent',
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// The actual command execution happens on the agent side.
|
|
504
|
+
// This endpoint signals intent; the agent polls or receives via WebSocket.
|
|
505
|
+
return {
|
|
506
|
+
ok: true,
|
|
507
|
+
label: params.label,
|
|
508
|
+
action: body.action,
|
|
509
|
+
packageName: body.packageName || null,
|
|
510
|
+
message: `Push install command "${body.action}" sent to agent "${params.label}"`,
|
|
511
|
+
};
|
|
512
|
+
},
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
// ------------------------------------------------------------------
|
|
516
|
+
// GET /plugins/push-install/sessions — audit log
|
|
517
|
+
// ------------------------------------------------------------------
|
|
518
|
+
fastify.get(
|
|
519
|
+
'/plugins/push-install/sessions',
|
|
520
|
+
{
|
|
521
|
+
preHandler: fastify.requireRole(['admin']),
|
|
522
|
+
},
|
|
523
|
+
async (_request, _reply) => {
|
|
524
|
+
const sessions = await readPushInstallSessions();
|
|
525
|
+
return { sessions };
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
// ------------------------------------------------------------------
|
|
530
|
+
// GET /plugins/push-install/agent-status — agent self-check
|
|
531
|
+
// ------------------------------------------------------------------
|
|
532
|
+
fastify.get(
|
|
533
|
+
'/plugins/push-install/agent-status',
|
|
534
|
+
{
|
|
535
|
+
preHandler: fastify.requireRole(['admin', 'agent']),
|
|
536
|
+
},
|
|
537
|
+
async (request, _reply) => {
|
|
538
|
+
const label = request.certLabel;
|
|
539
|
+
if (!label) {
|
|
540
|
+
return { pushInstallEnabled: false };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const registry = await loadAgentRegistry();
|
|
544
|
+
const agent = registry.agents.find((a) => a.label === label && !a.revoked);
|
|
545
|
+
|
|
546
|
+
if (!agent) {
|
|
547
|
+
return { pushInstallEnabled: false };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const enabled =
|
|
551
|
+
agent.pushInstallEnabledUntil && new Date(agent.pushInstallEnabledUntil) > new Date();
|
|
552
|
+
|
|
553
|
+
return {
|
|
554
|
+
pushInstallEnabled: !!enabled,
|
|
555
|
+
pushInstallEnabledUntil: agent.pushInstallEnabledUntil || null,
|
|
556
|
+
pushInstallPolicy: agent.pushInstallPolicy || null,
|
|
557
|
+
};
|
|
558
|
+
},
|
|
559
|
+
);
|
|
560
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
ALLOWED_SERVICES,
|
|
4
|
+
getAllServiceStatuses,
|
|
5
|
+
isAllowedService,
|
|
6
|
+
isAllowedAction,
|
|
7
|
+
executeServiceAction,
|
|
8
|
+
} from '../../lib/services.js';
|
|
9
|
+
|
|
10
|
+
const ServiceActionParamsSchema = z.object({
|
|
11
|
+
name: z.string().refine((v) => ALLOWED_SERVICES.includes(v), {
|
|
12
|
+
message: 'Unknown service',
|
|
13
|
+
}),
|
|
14
|
+
action: z.enum(['start', 'stop', 'restart', 'reload']),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export default async function servicesRoutes(fastify, _opts) {
|
|
18
|
+
fastify.get(
|
|
19
|
+
'/services',
|
|
20
|
+
{
|
|
21
|
+
preHandler: fastify.requireRole(['admin', 'agent'], { capability: 'services:read' }),
|
|
22
|
+
},
|
|
23
|
+
async (_request, _reply) => {
|
|
24
|
+
const services = await getAllServiceStatuses();
|
|
25
|
+
return { services };
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
fastify.post(
|
|
30
|
+
'/services/:name/:action',
|
|
31
|
+
{
|
|
32
|
+
preHandler: fastify.requireRole(['admin', 'agent'], { capability: 'services:write' }),
|
|
33
|
+
},
|
|
34
|
+
async (request, reply) => {
|
|
35
|
+
const { name, action } = request.params;
|
|
36
|
+
|
|
37
|
+
// Validate service name
|
|
38
|
+
if (!isAllowedService(name)) {
|
|
39
|
+
return reply.code(400).send({ error: 'Unknown service' });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate action
|
|
43
|
+
if (!isAllowedAction(action)) {
|
|
44
|
+
return reply.code(400).send({ error: 'Invalid action' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse for Zod validation (will throw ZodError caught by error handler)
|
|
48
|
+
ServiceActionParamsSchema.parse({ name, action });
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const result = await executeServiceAction(name, action);
|
|
52
|
+
return result;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
const statusCode = err.statusCode || 500;
|
|
55
|
+
const body = { error: err.message };
|
|
56
|
+
if (err.details) {
|
|
57
|
+
body.details = err.details;
|
|
58
|
+
}
|
|
59
|
+
return reply.code(statusCode).send(body);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
}
|