@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,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lamaste-server tunnels — Manage server tunnels.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* list List active tunnels
|
|
6
|
+
* create --subdomain <s> --port <p> --agent <l> Create a tunnel carried by an agent
|
|
7
|
+
* delete <id> Delete a tunnel by ID
|
|
8
|
+
* toggle <id> --enable|--disable Enable or disable a tunnel
|
|
9
|
+
* assign <id> --agent <label> Move a tunnel to another agent
|
|
10
|
+
* configure <id> [--access-mode <m>] [--max-body-mb <n>]
|
|
11
|
+
* Change access mode / body limit
|
|
12
|
+
*
|
|
13
|
+
* List reads directly from the state file.
|
|
14
|
+
* Create/delete/toggle use the panel REST API (which has the full
|
|
15
|
+
* nginx/certbot/chisel dependency stack).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import chalk from 'chalk';
|
|
19
|
+
import { readTunnels } from '../state.js';
|
|
20
|
+
import { panelRequest } from '../panel-api.js';
|
|
21
|
+
import { emit, emitError, emitComplete } from '../ndjson.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string[]} args
|
|
25
|
+
* @param {{ json: boolean }} options
|
|
26
|
+
*/
|
|
27
|
+
export async function runTunnels(args, { json }) {
|
|
28
|
+
const sub = args[0];
|
|
29
|
+
|
|
30
|
+
switch (sub) {
|
|
31
|
+
case 'list':
|
|
32
|
+
return listTunnels({ json });
|
|
33
|
+
case 'create':
|
|
34
|
+
return createTunnel(args.slice(1), { json });
|
|
35
|
+
case 'delete':
|
|
36
|
+
return deleteTunnel(args[1], { json });
|
|
37
|
+
case 'toggle':
|
|
38
|
+
return toggleTunnel(args.slice(1), { json });
|
|
39
|
+
case 'assign':
|
|
40
|
+
return assignTunnel(args.slice(1), { json });
|
|
41
|
+
case 'configure':
|
|
42
|
+
return configureTunnel(args.slice(1), { json });
|
|
43
|
+
default:
|
|
44
|
+
printTunnelUsage();
|
|
45
|
+
process.exit(sub ? 1 : 0);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function printTunnelUsage() {
|
|
50
|
+
const b = chalk.bold;
|
|
51
|
+
const c = chalk.cyan;
|
|
52
|
+
console.log(`
|
|
53
|
+
${b('Usage:')} lamaste-server tunnels <subcommand>
|
|
54
|
+
|
|
55
|
+
${b('Subcommands:')}
|
|
56
|
+
${c('list')} List active tunnels
|
|
57
|
+
${c('create')} --subdomain <s> --port <p> --agent <l> [opts] Create a tunnel
|
|
58
|
+
${c('delete')} <id> Delete a tunnel by ID
|
|
59
|
+
${c('toggle')} <id> --enable|--disable Enable or disable a tunnel
|
|
60
|
+
${c('assign')} <id> --agent <label> Move a tunnel to another agent
|
|
61
|
+
${c('configure')} <id> [--access-mode <m>] [--max-body-mb <n>] Change access mode / body limit
|
|
62
|
+
|
|
63
|
+
${b('Create options:')}
|
|
64
|
+
--subdomain <name> Tunnel subdomain (required)
|
|
65
|
+
--port <number> Local port to tunnel (required)
|
|
66
|
+
--agent <label> Agent that carries the tunnel (required)
|
|
67
|
+
--description <text> Optional description
|
|
68
|
+
--access-mode <mode> Access mode: public, authenticated, restricted (default: restricted)
|
|
69
|
+
--max-body-mb <n> Largest request body in MiB, 1-10240 (default: 10)
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {{ json: boolean }} options
|
|
75
|
+
*/
|
|
76
|
+
async function listTunnels({ json }) {
|
|
77
|
+
const tunnels = await readTunnels();
|
|
78
|
+
|
|
79
|
+
if (json) {
|
|
80
|
+
emit({ tunnels });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (tunnels.length === 0) {
|
|
85
|
+
console.log('\n No tunnels configured.\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const b = chalk.bold;
|
|
90
|
+
const c = chalk.cyan;
|
|
91
|
+
const g = chalk.green;
|
|
92
|
+
const r = chalk.red;
|
|
93
|
+
const d = chalk.dim;
|
|
94
|
+
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log(b(' Tunnels'));
|
|
97
|
+
console.log(d(' ' + '\u2500'.repeat(50)));
|
|
98
|
+
|
|
99
|
+
for (const t of tunnels) {
|
|
100
|
+
const enabled = t.enabled !== false ? g('enabled') : r('disabled');
|
|
101
|
+
const accessMode = t.accessMode ? d(`[${t.accessMode}]`) : '';
|
|
102
|
+
const type = t.type !== 'app' ? d(`(${t.type})`) : '';
|
|
103
|
+
const carrier = t.agentLabel ? d(`via ${t.agentLabel}`) : r('no agent');
|
|
104
|
+
const bodyLimit = t.type !== 'panel' ? d(`${t.maxBodySizeMb ?? 1} MiB`) : '';
|
|
105
|
+
console.log(
|
|
106
|
+
` ${c(t.subdomain)} \u2192 :${t.port} ${enabled} ${carrier} ${accessMode} ${bodyLimit} ${type}`.trimEnd(),
|
|
107
|
+
);
|
|
108
|
+
if (t.description) {
|
|
109
|
+
console.log(` ${d(t.description)}`);
|
|
110
|
+
}
|
|
111
|
+
console.log(` ${d(`id: ${t.id} fqdn: ${t.fqdn}`)}`);
|
|
112
|
+
}
|
|
113
|
+
console.log('');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {string[]} args
|
|
118
|
+
* @param {{ json: boolean }} options
|
|
119
|
+
*/
|
|
120
|
+
async function createTunnel(args, { json }) {
|
|
121
|
+
const subdomain = getArg(args, 'subdomain');
|
|
122
|
+
const portStr = getArg(args, 'port');
|
|
123
|
+
const description = getArg(args, 'description') || undefined;
|
|
124
|
+
const accessMode = getArg(args, 'access-mode') || undefined;
|
|
125
|
+
const agentLabel = getArg(args, 'agent');
|
|
126
|
+
const maxBodyStr = getArg(args, 'max-body-mb');
|
|
127
|
+
|
|
128
|
+
if (!subdomain || !portStr || !agentLabel) {
|
|
129
|
+
const msg = 'Error: --subdomain, --port and --agent are required';
|
|
130
|
+
if (json) emitError(msg);
|
|
131
|
+
else console.error(`\n ${msg}\n`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const port = parseInt(portStr, 10);
|
|
136
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
137
|
+
const msg = 'Error: --port must be a valid port number (1-65535)';
|
|
138
|
+
if (json) emitError(msg);
|
|
139
|
+
else console.error(`\n ${msg}\n`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (accessMode && !['public', 'authenticated', 'restricted'].includes(accessMode)) {
|
|
144
|
+
const msg = 'Error: --access-mode must be public, authenticated, or restricted';
|
|
145
|
+
if (json) emitError(msg);
|
|
146
|
+
else console.error(`\n ${msg}\n`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const maxBodySizeMb = maxBodyStr === null ? undefined : parseMaxBody(maxBodyStr, json);
|
|
151
|
+
|
|
152
|
+
/** @type {Record<string, unknown>} */
|
|
153
|
+
const body = { subdomain, port, agentLabel };
|
|
154
|
+
if (description) body.description = description;
|
|
155
|
+
if (accessMode) body.accessMode = accessMode;
|
|
156
|
+
if (maxBodySizeMb !== undefined) body.maxBodySizeMb = maxBodySizeMb;
|
|
157
|
+
|
|
158
|
+
if (!json) process.stderr.write(` Creating tunnel ${chalk.cyan(subdomain)} \u2192 :${port}...`);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const { tunnel } = await panelRequest('POST', '/api/tunnels', body);
|
|
162
|
+
|
|
163
|
+
if (json) {
|
|
164
|
+
emitComplete({ tunnel });
|
|
165
|
+
} else {
|
|
166
|
+
console.log(` ${chalk.green('ok')}`);
|
|
167
|
+
console.log(` FQDN: ${chalk.cyan(tunnel.fqdn)}`);
|
|
168
|
+
console.log(` ID: ${chalk.dim(tunnel.id)}`);
|
|
169
|
+
console.log(` Agent: ${tunnel.agentLabel}`);
|
|
170
|
+
console.log(chalk.dim(` ${tunnel.agentLabel} starts carrying it within 30 seconds.`));
|
|
171
|
+
console.log('');
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (json) emitError(err.message);
|
|
175
|
+
else {
|
|
176
|
+
console.log(` ${chalk.red('failed')}`);
|
|
177
|
+
console.error(` ${chalk.red(err.message)}\n`);
|
|
178
|
+
}
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {string | undefined} id
|
|
185
|
+
* @param {{ json: boolean }} options
|
|
186
|
+
*/
|
|
187
|
+
async function deleteTunnel(id, { json }) {
|
|
188
|
+
if (!id) {
|
|
189
|
+
const msg = 'Usage: lamaste-server tunnels delete <id>';
|
|
190
|
+
if (json) emitError(msg);
|
|
191
|
+
else console.error(`\n ${msg}\n`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!json) process.stderr.write(` Deleting tunnel ${chalk.dim(id)}...`);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await panelRequest('DELETE', `/api/tunnels/${id}`);
|
|
199
|
+
if (json) {
|
|
200
|
+
emitComplete({ id, deleted: true });
|
|
201
|
+
} else {
|
|
202
|
+
console.log(` ${chalk.green('ok')}\n`);
|
|
203
|
+
}
|
|
204
|
+
} catch (err) {
|
|
205
|
+
if (json) emitError(err.message);
|
|
206
|
+
else {
|
|
207
|
+
console.log(` ${chalk.red('failed')}`);
|
|
208
|
+
console.error(` ${chalk.red(err.message)}\n`);
|
|
209
|
+
}
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {string[]} args
|
|
216
|
+
* @param {{ json: boolean }} options
|
|
217
|
+
*/
|
|
218
|
+
async function toggleTunnel(args, { json }) {
|
|
219
|
+
const id = args[0];
|
|
220
|
+
const enable = args.includes('--enable');
|
|
221
|
+
const disable = args.includes('--disable');
|
|
222
|
+
|
|
223
|
+
if (!id || (!enable && !disable)) {
|
|
224
|
+
const msg = 'Usage: lamaste-server tunnels toggle <id> --enable|--disable';
|
|
225
|
+
if (json) emitError(msg);
|
|
226
|
+
else console.error(`\n ${msg}\n`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const enabled = enable;
|
|
231
|
+
|
|
232
|
+
if (!json) {
|
|
233
|
+
const action = enabled ? 'Enabling' : 'Disabling';
|
|
234
|
+
process.stderr.write(` ${action} tunnel ${chalk.dim(id)}...`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const { tunnel } = await panelRequest('PATCH', `/api/tunnels/${id}`, { enabled });
|
|
239
|
+
if (json) {
|
|
240
|
+
emitComplete({ id, enabled, tunnel });
|
|
241
|
+
} else {
|
|
242
|
+
console.log(` ${chalk.green('ok')}\n`);
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
if (json) emitError(err.message);
|
|
246
|
+
else {
|
|
247
|
+
console.log(` ${chalk.red('failed')}`);
|
|
248
|
+
console.error(` ${chalk.red(err.message)}\n`);
|
|
249
|
+
}
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @param {string[]} args
|
|
256
|
+
* @param {{ json: boolean }} options
|
|
257
|
+
*/
|
|
258
|
+
async function assignTunnel(args, { json }) {
|
|
259
|
+
const id = args[0];
|
|
260
|
+
const agentLabel = getArg(args, 'agent');
|
|
261
|
+
|
|
262
|
+
if (!id || !agentLabel) {
|
|
263
|
+
const msg = 'Usage: lamaste-server tunnels assign <id> --agent <label>';
|
|
264
|
+
if (json) emitError(msg);
|
|
265
|
+
else console.error(`\n ${msg}\n`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!json) process.stderr.write(` Assigning tunnel ${chalk.dim(id)} to ${agentLabel}...`);
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const { tunnel } = await panelRequest('PATCH', `/api/tunnels/${id}`, { agentLabel });
|
|
273
|
+
if (json) {
|
|
274
|
+
emitComplete({ id, tunnel });
|
|
275
|
+
} else {
|
|
276
|
+
console.log(` ${chalk.green('ok')}`);
|
|
277
|
+
console.log(
|
|
278
|
+
chalk.dim(` ${agentLabel} and the previous agent pick up the change within 30 seconds.\n`),
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
} catch (err) {
|
|
282
|
+
if (json) emitError(err.message);
|
|
283
|
+
else {
|
|
284
|
+
console.log(` ${chalk.red('failed')}`);
|
|
285
|
+
console.error(` ${chalk.red(err.message)}\n`);
|
|
286
|
+
}
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @param {string[]} args
|
|
293
|
+
* @param {{ json: boolean }} options
|
|
294
|
+
*/
|
|
295
|
+
async function configureTunnel(args, { json }) {
|
|
296
|
+
const id = args[0];
|
|
297
|
+
const accessMode = getArg(args, 'access-mode');
|
|
298
|
+
const maxBodyStr = getArg(args, 'max-body-mb');
|
|
299
|
+
|
|
300
|
+
if (!id || (accessMode === null && maxBodyStr === null)) {
|
|
301
|
+
const msg =
|
|
302
|
+
'Usage: lamaste-server tunnels configure <id> [--access-mode <mode>] [--max-body-mb <n>]';
|
|
303
|
+
if (json) emitError(msg);
|
|
304
|
+
else console.error(`\n ${msg}\n`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
if (accessMode !== null && !['public', 'authenticated', 'restricted'].includes(accessMode)) {
|
|
308
|
+
const msg = 'Error: --access-mode must be public, authenticated, or restricted';
|
|
309
|
+
if (json) emitError(msg);
|
|
310
|
+
else console.error(`\n ${msg}\n`);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** @type {Record<string, unknown>} */
|
|
315
|
+
const body = {};
|
|
316
|
+
if (accessMode !== null) body.accessMode = accessMode;
|
|
317
|
+
if (maxBodyStr !== null) body.maxBodySizeMb = parseMaxBody(maxBodyStr, json);
|
|
318
|
+
|
|
319
|
+
if (!json) process.stderr.write(` Reconfiguring tunnel ${chalk.dim(id)}...`);
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const { tunnel } = await panelRequest('PATCH', `/api/tunnels/${id}`, body);
|
|
323
|
+
if (json) {
|
|
324
|
+
emitComplete({ id, tunnel });
|
|
325
|
+
} else {
|
|
326
|
+
console.log(` ${chalk.green('ok')}`);
|
|
327
|
+
console.log(` Access: ${tunnel.accessMode}`);
|
|
328
|
+
console.log(` Body limit: ${tunnel.maxBodySizeMb} MiB\n`);
|
|
329
|
+
}
|
|
330
|
+
} catch (err) {
|
|
331
|
+
if (json) emitError(err.message);
|
|
332
|
+
else {
|
|
333
|
+
console.log(` ${chalk.red('failed')}`);
|
|
334
|
+
console.error(` ${chalk.red(err.message)}\n`);
|
|
335
|
+
}
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @param {string} value
|
|
342
|
+
* @param {boolean} json
|
|
343
|
+
* @returns {number}
|
|
344
|
+
*/
|
|
345
|
+
function parseMaxBody(value, json) {
|
|
346
|
+
const n = Number(value);
|
|
347
|
+
if (!Number.isInteger(n) || n < 1 || n > 10240) {
|
|
348
|
+
const msg = 'Error: --max-body-mb must be an integer from 1 to 10240';
|
|
349
|
+
if (json) emitError(msg);
|
|
350
|
+
else console.error(`\n ${msg}\n`);
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
return n;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @param {string[]} args
|
|
358
|
+
* @param {string} name
|
|
359
|
+
* @returns {string | null}
|
|
360
|
+
*/
|
|
361
|
+
function getArg(args, name) {
|
|
362
|
+
const idx = args.indexOf(`--${name}`);
|
|
363
|
+
if (idx === -1 || idx + 1 >= args.length) return null;
|
|
364
|
+
return args[idx + 1];
|
|
365
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lamaste-server uninstall — Full server teardown.
|
|
3
|
+
*
|
|
4
|
+
* Cleanup order (reverse of installation):
|
|
5
|
+
*
|
|
6
|
+
* 1. Stop and disable services (panel, gatekeeper)
|
|
7
|
+
* 2. Remove systemd service files
|
|
8
|
+
* 3. Remove nginx configurations (vhosts, sites)
|
|
9
|
+
* 4. Reload nginx (falls back to default config)
|
|
10
|
+
* 5. Remove certbot certificates
|
|
11
|
+
* 6. Remove Authelia configuration
|
|
12
|
+
* 7. Remove Lamaste data directory (/etc/lamalibre/lamaste)
|
|
13
|
+
* 8. Remove installed npm packages
|
|
14
|
+
* 9. Remove lamaste system user
|
|
15
|
+
*
|
|
16
|
+
* Requires root access. Interactive confirmation unless --force is passed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFile } from 'node:fs/promises';
|
|
20
|
+
import { createInterface } from 'node:readline';
|
|
21
|
+
import chalk from 'chalk';
|
|
22
|
+
import { execa } from 'execa';
|
|
23
|
+
import { CONFIG_PATH, STATE_DIR, PANEL_SERVICE, GATEKEEPER_SERVICE } from '../config.js';
|
|
24
|
+
import { emitStep, emitError, emitComplete } from '../ndjson.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string[]} args
|
|
28
|
+
* @param {{ json: boolean }} options
|
|
29
|
+
*/
|
|
30
|
+
export async function runUninstall(args, { json }) {
|
|
31
|
+
// Verify running as root
|
|
32
|
+
if (process.getuid && process.getuid() !== 0) {
|
|
33
|
+
const msg = 'lamaste-server uninstall must be run as root.';
|
|
34
|
+
if (json) emitError(msg);
|
|
35
|
+
else {
|
|
36
|
+
console.error(`\n Error: ${msg}`);
|
|
37
|
+
console.error(' Usage: sudo lamaste-server uninstall\n');
|
|
38
|
+
}
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const force = args.includes('--force');
|
|
43
|
+
|
|
44
|
+
// Read config to get domain info for display
|
|
45
|
+
let config = null;
|
|
46
|
+
try {
|
|
47
|
+
const raw = await readFile(CONFIG_PATH, 'utf-8');
|
|
48
|
+
config = JSON.parse(raw);
|
|
49
|
+
} catch {
|
|
50
|
+
// Config may not exist
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const domain = config?.domain || 'unknown';
|
|
54
|
+
|
|
55
|
+
if (!force && !json) {
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(chalk.bold.red(' WARNING: This will completely remove Lamaste from this server.'));
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(` Domain: ${chalk.cyan(String(domain))}`);
|
|
60
|
+
console.log(` Data dir: ${chalk.dim(STATE_DIR)}`);
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(' This action will:');
|
|
63
|
+
console.log(' - Stop and remove all Lamaste services');
|
|
64
|
+
console.log(' - Remove all nginx tunnel/site configurations');
|
|
65
|
+
console.log(" - Remove all certificates (mTLS and Let's Encrypt)");
|
|
66
|
+
console.log(' - Remove Authelia configuration');
|
|
67
|
+
console.log(' - Delete all Lamaste data');
|
|
68
|
+
console.log('');
|
|
69
|
+
|
|
70
|
+
const confirmed = await promptConfirm(' Type "uninstall" to confirm: ', 'uninstall');
|
|
71
|
+
if (!confirmed) {
|
|
72
|
+
console.log('\n Aborted.\n');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const steps = [
|
|
79
|
+
{ name: 'stop-services', label: 'Stopping services', fn: stopServices },
|
|
80
|
+
{ name: 'remove-services', label: 'Removing service files', fn: removeServices },
|
|
81
|
+
{ name: 'remove-nginx', label: 'Removing nginx configurations', fn: removeNginxConfigs },
|
|
82
|
+
{ name: 'reload-nginx', label: 'Reloading nginx', fn: reloadNginx },
|
|
83
|
+
{ name: 'remove-certbot', label: 'Removing certbot certificates', fn: removeCertbotCerts },
|
|
84
|
+
{ name: 'remove-authelia', label: 'Removing Authelia', fn: removeAuthelia },
|
|
85
|
+
{ name: 'remove-data', label: 'Removing Lamaste data', fn: removeData },
|
|
86
|
+
{ name: 'remove-packages', label: 'Removing npm packages', fn: removePackages },
|
|
87
|
+
{ name: 'remove-user', label: 'Removing lamaste user', fn: removeUser },
|
|
88
|
+
{
|
|
89
|
+
name: 'remove-authelia-user',
|
|
90
|
+
label: 'Removing lamaste-authelia user',
|
|
91
|
+
fn: removeAutheliaUser,
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const step of steps) {
|
|
96
|
+
if (json) emitStep(step.name, 'running', step.label);
|
|
97
|
+
else process.stderr.write(` ${step.label}...`);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await step.fn(config);
|
|
101
|
+
if (json) emitStep(step.name, 'complete');
|
|
102
|
+
else console.log(` ${chalk.green('ok')}`);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
// Non-fatal: log and continue with remaining steps
|
|
105
|
+
const msg = err.message || String(err);
|
|
106
|
+
if (json) emitStep(step.name, 'failed', msg);
|
|
107
|
+
else console.log(` ${chalk.yellow('skipped')} ${chalk.dim(`(${msg})`)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (json) {
|
|
112
|
+
emitComplete({ uninstalled: true });
|
|
113
|
+
} else {
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(chalk.green(' Lamaste has been removed from this server.'));
|
|
116
|
+
console.log('');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function stopServices() {
|
|
121
|
+
for (const service of [PANEL_SERVICE, GATEKEEPER_SERVICE, 'chisel']) {
|
|
122
|
+
try {
|
|
123
|
+
await execa('systemctl', ['stop', service], { timeout: 15000 });
|
|
124
|
+
} catch {
|
|
125
|
+
// Service may not be running
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
await execa('systemctl', ['disable', service], { timeout: 10000 });
|
|
129
|
+
} catch {
|
|
130
|
+
// Service may not be enabled
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function removeServices() {
|
|
136
|
+
const serviceFiles = [
|
|
137
|
+
`/etc/systemd/system/${PANEL_SERVICE}.service`,
|
|
138
|
+
`/etc/systemd/system/${GATEKEEPER_SERVICE}.service`,
|
|
139
|
+
'/etc/systemd/system/chisel.service',
|
|
140
|
+
'/etc/systemd/system/authelia.service',
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
for (const file of serviceFiles) {
|
|
144
|
+
await execa('rm', ['-f', file]).catch(() => {});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await execa('systemctl', ['daemon-reload']).catch(() => {});
|
|
148
|
+
|
|
149
|
+
// Remove sudoers file and the root-owned wrappers it referenced
|
|
150
|
+
await execa('rm', ['-f', '/etc/sudoers.d/lamaste']).catch(() => {});
|
|
151
|
+
await execa('rm', [
|
|
152
|
+
'-f',
|
|
153
|
+
'/usr/local/sbin/lamaste-priv',
|
|
154
|
+
'/usr/local/sbin/lamaste-certbot',
|
|
155
|
+
'/usr/local/sbin/lamaste-cert-info',
|
|
156
|
+
// installed by versions before 4.0
|
|
157
|
+
'/usr/local/sbin/lamaste-sign-csr',
|
|
158
|
+
'/usr/local/sbin/lamaste-pki-rename',
|
|
159
|
+
]).catch(() => {});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @param {Record<string, unknown> | null} _config
|
|
164
|
+
*/
|
|
165
|
+
async function removeNginxConfigs(_config) {
|
|
166
|
+
// Remove lamaste-specific sites
|
|
167
|
+
const patterns = [
|
|
168
|
+
'/etc/nginx/sites-enabled/lamalibre-lamaste-*',
|
|
169
|
+
'/etc/nginx/sites-available/lamalibre-lamaste-*',
|
|
170
|
+
'/etc/nginx/snippets/lamalibre-lamaste-*',
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
for (const pattern of patterns) {
|
|
174
|
+
// Use shell glob expansion safely via rm
|
|
175
|
+
await execa('sh', ['-c', `rm -f ${pattern}`]).catch(() => {});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Remove any tunnel/site vhosts (they use the subdomain as filename)
|
|
179
|
+
// Read tunnels and sites to identify their vhost files
|
|
180
|
+
try {
|
|
181
|
+
const tunnelsRaw = await readFile(`${STATE_DIR}/tunnels.json`, 'utf-8');
|
|
182
|
+
const tunnels = JSON.parse(tunnelsRaw);
|
|
183
|
+
if (Array.isArray(tunnels)) {
|
|
184
|
+
for (const t of tunnels) {
|
|
185
|
+
if (t.subdomain) {
|
|
186
|
+
await execa('rm', [
|
|
187
|
+
'-f',
|
|
188
|
+
`/etc/nginx/sites-enabled/${t.subdomain}`,
|
|
189
|
+
`/etc/nginx/sites-available/${t.subdomain}`,
|
|
190
|
+
]).catch(() => {});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
// No tunnels file
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const sitesRaw = await readFile(`${STATE_DIR}/sites.json`, 'utf-8');
|
|
200
|
+
const sites = JSON.parse(sitesRaw);
|
|
201
|
+
if (Array.isArray(sites)) {
|
|
202
|
+
for (const s of sites) {
|
|
203
|
+
if (s.id) {
|
|
204
|
+
await execa('rm', [
|
|
205
|
+
'-f',
|
|
206
|
+
`/etc/nginx/sites-enabled/site-${s.id}`,
|
|
207
|
+
`/etc/nginx/sites-available/site-${s.id}`,
|
|
208
|
+
]).catch(() => {});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
// No sites file
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function reloadNginx() {
|
|
218
|
+
await execa('nginx', ['-t'], { timeout: 10000 });
|
|
219
|
+
await execa('systemctl', ['reload', 'nginx'], { timeout: 10000 });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function removeCertbotCerts() {
|
|
223
|
+
// List all certbot certificates and delete lamaste-related ones
|
|
224
|
+
try {
|
|
225
|
+
const { stdout } = await execa('certbot', ['certificates', '--non-interactive'], {
|
|
226
|
+
timeout: 30000,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const certNames = [];
|
|
230
|
+
for (const match of stdout.matchAll(/Certificate Name:\s+(.+)/g)) {
|
|
231
|
+
certNames.push(match[1].trim());
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
for (const name of certNames) {
|
|
235
|
+
await execa('certbot', ['delete', '--non-interactive', '--cert-name', name], {
|
|
236
|
+
timeout: 15000,
|
|
237
|
+
}).catch(() => {});
|
|
238
|
+
}
|
|
239
|
+
} catch {
|
|
240
|
+
// certbot may not be installed
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function removeAuthelia() {
|
|
245
|
+
// Stop and disable authelia
|
|
246
|
+
await execa('systemctl', ['stop', 'authelia']).catch(() => {});
|
|
247
|
+
await execa('systemctl', ['disable', 'authelia']).catch(() => {});
|
|
248
|
+
|
|
249
|
+
// Remove its configuration, database and log, and the binary
|
|
250
|
+
// create-lamaste installed (the pinned release, at /usr/local/bin).
|
|
251
|
+
await execa('rm', ['-rf', '/etc/authelia', '/var/log/authelia']).catch(() => {});
|
|
252
|
+
await execa('rm', ['-f', '/usr/local/bin/authelia']).catch(() => {});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function removeData() {
|
|
256
|
+
// Remove the entire lamaste data directory
|
|
257
|
+
await execa('rm', ['-rf', STATE_DIR]);
|
|
258
|
+
|
|
259
|
+
// Remove site content directories
|
|
260
|
+
await execa('rm', ['-rf', '/var/www/lamaste']).catch(() => {});
|
|
261
|
+
|
|
262
|
+
// Remove chisel binary
|
|
263
|
+
await execa('rm', ['-f', '/usr/local/bin/chisel']).catch(() => {});
|
|
264
|
+
|
|
265
|
+
// Remove the lamaste-reset-admin symlink (legacy)
|
|
266
|
+
await execa('rm', ['-f', '/usr/local/bin/lamaste-reset-admin']).catch(() => {});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function removePackages() {
|
|
270
|
+
// Locate and remove globally installed lamaste packages
|
|
271
|
+
try {
|
|
272
|
+
const { stdout } = await execa('npm', ['ls', '-g', '--depth=0', '--json'], {
|
|
273
|
+
timeout: 15000,
|
|
274
|
+
});
|
|
275
|
+
const globalPkgs = JSON.parse(stdout);
|
|
276
|
+
const deps = globalPkgs.dependencies || {};
|
|
277
|
+
|
|
278
|
+
for (const [name] of Object.entries(deps)) {
|
|
279
|
+
if (name.startsWith('@lamalibre/')) {
|
|
280
|
+
await execa('npm', ['uninstall', '-g', name], { timeout: 30000 }).catch(() => {});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
// Global packages listing may fail
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Remove the panel server installation directory
|
|
288
|
+
await execa('rm', ['-rf', '/opt/lamalibre/lamaste']).catch(() => {});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function removeUser() {
|
|
292
|
+
// Check if lamaste user exists
|
|
293
|
+
try {
|
|
294
|
+
await execa('id', ['lamaste']);
|
|
295
|
+
} catch {
|
|
296
|
+
return; // User does not exist
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Remove the user and its home directory
|
|
300
|
+
await execa('userdel', ['-r', 'lamaste']).catch(() => {});
|
|
301
|
+
|
|
302
|
+
// Remove groups if they still exist
|
|
303
|
+
await execa('groupdel', ['lamaste']).catch(() => {});
|
|
304
|
+
await execa('groupdel', ['lamaste-chisel']).catch(() => {});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function removeAutheliaUser() {
|
|
308
|
+
await execa('userdel', ['lamaste-authelia']).catch(() => {});
|
|
309
|
+
await execa('groupdel', ['lamaste-authelia']).catch(() => {});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Prompt the user for confirmation.
|
|
314
|
+
* @param {string} prompt
|
|
315
|
+
* @param {string} expected The exact text the user must type
|
|
316
|
+
* @returns {Promise<boolean>}
|
|
317
|
+
*/
|
|
318
|
+
function promptConfirm(prompt, expected) {
|
|
319
|
+
return new Promise((resolve) => {
|
|
320
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
321
|
+
rl.question(prompt, (answer) => {
|
|
322
|
+
rl.close();
|
|
323
|
+
resolve(answer.trim() === expected);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
}
|