@devalade/shipnode 2.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/dist/cli/commands/backup.d.ts +14 -0
- package/dist/cli/commands/backup.d.ts.map +1 -0
- package/dist/cli/commands/backup.js +144 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +5 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +222 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/cloudflare.d.ts +10 -0
- package/dist/cli/commands/cloudflare.d.ts.map +1 -0
- package/dist/cli/commands/cloudflare.js +166 -0
- package/dist/cli/commands/cloudflare.js.map +1 -0
- package/dist/cli/commands/config.d.ts +10 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +99 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/deploy.d.ts +6 -0
- package/dist/cli/commands/deploy.d.ts.map +1 -0
- package/dist/cli/commands/deploy.js +95 -0
- package/dist/cli/commands/deploy.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +5 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +86 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/eject.d.ts +2 -0
- package/dist/cli/commands/eject.d.ts.map +1 -0
- package/dist/cli/commands/eject.js +74 -0
- package/dist/cli/commands/eject.js.map +1 -0
- package/dist/cli/commands/env.d.ts +4 -0
- package/dist/cli/commands/env.d.ts.map +1 -0
- package/dist/cli/commands/env.js +50 -0
- package/dist/cli/commands/env.js.map +1 -0
- package/dist/cli/commands/harden.d.ts +4 -0
- package/dist/cli/commands/harden.d.ts.map +1 -0
- package/dist/cli/commands/harden.js +110 -0
- package/dist/cli/commands/harden.js.map +1 -0
- package/dist/cli/commands/help.d.ts +2 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +99 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +246 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/logs.d.ts +5 -0
- package/dist/cli/commands/logs.d.ts.map +1 -0
- package/dist/cli/commands/logs.js +17 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/metrics.d.ts +4 -0
- package/dist/cli/commands/metrics.d.ts.map +1 -0
- package/dist/cli/commands/metrics.js +21 -0
- package/dist/cli/commands/metrics.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +4 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +71 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/restart.d.ts +4 -0
- package/dist/cli/commands/restart.d.ts.map +1 -0
- package/dist/cli/commands/restart.js +14 -0
- package/dist/cli/commands/restart.js.map +1 -0
- package/dist/cli/commands/rollback.d.ts +5 -0
- package/dist/cli/commands/rollback.d.ts.map +1 -0
- package/dist/cli/commands/rollback.js +49 -0
- package/dist/cli/commands/rollback.js.map +1 -0
- package/dist/cli/commands/run.d.ts +5 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +69 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +4 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +46 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/status.d.ts +4 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +49 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +4 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +14 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/unlock.d.ts +4 -0
- package/dist/cli/commands/unlock.d.ts.map +1 -0
- package/dist/cli/commands/unlock.js +27 -0
- package/dist/cli/commands/unlock.js.map +1 -0
- package/dist/cli/commands/upgrade.d.ts +2 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +63 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/commands/user.d.ts +17 -0
- package/dist/cli/commands/user.d.ts.map +1 -0
- package/dist/cli/commands/user.js +98 -0
- package/dist/cli/commands/user.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +218 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompt.d.ts +2 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +11 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/runner.d.ts +34 -0
- package/dist/cli/runner.d.ts.map +1 -0
- package/dist/cli/runner.js +44 -0
- package/dist/cli/runner.js.map +1 -0
- package/dist/cli/ui.d.ts +9 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +26 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/assembly.d.ts +14 -0
- package/dist/config/assembly.d.ts.map +1 -0
- package/dist/config/assembly.js +62 -0
- package/dist/config/assembly.js.map +1 -0
- package/dist/config/builder.d.ts +33 -0
- package/dist/config/builder.d.ts.map +1 -0
- package/dist/config/builder.js +119 -0
- package/dist/config/builder.js.map +1 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +20 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +416 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +75 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/domain/deploy/backend-strategy.d.ts +15 -0
- package/dist/domain/deploy/backend-strategy.d.ts.map +1 -0
- package/dist/domain/deploy/backend-strategy.js +110 -0
- package/dist/domain/deploy/backend-strategy.js.map +1 -0
- package/dist/domain/deploy/frontend-strategy.d.ts +13 -0
- package/dist/domain/deploy/frontend-strategy.d.ts.map +1 -0
- package/dist/domain/deploy/frontend-strategy.js +64 -0
- package/dist/domain/deploy/frontend-strategy.js.map +1 -0
- package/dist/domain/deploy/orchestrator.d.ts +34 -0
- package/dist/domain/deploy/orchestrator.d.ts.map +1 -0
- package/dist/domain/deploy/orchestrator.js +149 -0
- package/dist/domain/deploy/orchestrator.js.map +1 -0
- package/dist/domain/deploy/strategy.d.ts +44 -0
- package/dist/domain/deploy/strategy.d.ts.map +1 -0
- package/dist/domain/deploy/strategy.js +2 -0
- package/dist/domain/deploy/strategy.js.map +1 -0
- package/dist/domain/framework/detector.d.ts +7 -0
- package/dist/domain/framework/detector.d.ts.map +1 -0
- package/dist/domain/framework/detector.js +109 -0
- package/dist/domain/framework/detector.js.map +1 -0
- package/dist/domain/release/manager.d.ts +22 -0
- package/dist/domain/release/manager.d.ts.map +1 -0
- package/dist/domain/release/manager.js +100 -0
- package/dist/domain/release/manager.js.map +1 -0
- package/dist/domain/remote/executor.d.ts +16 -0
- package/dist/domain/remote/executor.d.ts.map +1 -0
- package/dist/domain/remote/executor.js +2 -0
- package/dist/domain/remote/executor.js.map +1 -0
- package/dist/domain/validation/ip.d.ts +5 -0
- package/dist/domain/validation/ip.d.ts.map +1 -0
- package/dist/domain/validation/ip.js +38 -0
- package/dist/domain/validation/ip.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/ssh/connection.d.ts +19 -0
- package/dist/infrastructure/ssh/connection.d.ts.map +1 -0
- package/dist/infrastructure/ssh/connection.js +89 -0
- package/dist/infrastructure/ssh/connection.js.map +1 -0
- package/dist/services/caddy.service.d.ts +12 -0
- package/dist/services/caddy.service.d.ts.map +1 -0
- package/dist/services/caddy.service.js +56 -0
- package/dist/services/caddy.service.js.map +1 -0
- package/dist/services/deploy.service.d.ts +19 -0
- package/dist/services/deploy.service.d.ts.map +1 -0
- package/dist/services/deploy.service.js +53 -0
- package/dist/services/deploy.service.js.map +1 -0
- package/dist/services/health.service.d.ts +13 -0
- package/dist/services/health.service.d.ts.map +1 -0
- package/dist/services/health.service.js +38 -0
- package/dist/services/health.service.js.map +1 -0
- package/dist/shared/constants.d.ts +127 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +85 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/errors.d.ts +27 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +53 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/types.d.ts +116 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function scheduleToSystemd(schedule: 'hourly' | 'daily' | 'weekly'): string;
|
|
2
|
+
export declare function cmdBackupSetup(cwd: string, options: {
|
|
3
|
+
config?: string;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function cmdBackupRun(cwd: string, options: {
|
|
6
|
+
config?: string;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
export declare function cmdBackupStatus(cwd: string, options: {
|
|
9
|
+
config?: string;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
export declare function cmdBackupList(cwd: string, options: {
|
|
12
|
+
config?: string;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/backup.ts"],"names":[],"mappings":"AAoDA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAOjF;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CA4Ef;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { runRemoteCommand } from '../runner.js';
|
|
2
|
+
import { ui } from '../ui.js';
|
|
3
|
+
const BACKUP_SCRIPT_PATH = '/usr/local/bin/shipnode-backup.sh';
|
|
4
|
+
const TIMER_NAME = 'shipnode-backup';
|
|
5
|
+
function buildBackupScript(config) {
|
|
6
|
+
const prefix = config.s3Prefix ? config.s3Prefix.replace(/\/$/, '') + '/' : '';
|
|
7
|
+
const endpointFlag = config.s3Endpoint ? `--endpoint-url "${config.s3Endpoint}"` : '';
|
|
8
|
+
const ts = '$(date +%Y%m%d_%H%M%S)';
|
|
9
|
+
const dbDump = config.dbType && config.dbType !== 'sqlite'
|
|
10
|
+
? `
|
|
11
|
+
# Database backup
|
|
12
|
+
DB_FILE="/tmp/db_${ts}.sql"
|
|
13
|
+
${config.dbType === 'postgres' ? `PGPASSWORD="$DB_PASSWORD" pg_dump -h "${config.dbHost}" -p "${config.dbPort ?? 5432}" -U "${config.dbUser}" "${config.dbName}" > "$DB_FILE"` : ''}
|
|
14
|
+
${config.dbType === 'mysql' ? `mysqldump -h "${config.dbHost}" -P "${config.dbPort ?? 3306}" -u "${config.dbUser}" -p"$DB_PASSWORD" "${config.dbName}" > "$DB_FILE"` : ''}
|
|
15
|
+
aws s3 cp "$DB_FILE" "s3://${config.s3Bucket}/${prefix}db/$DB_FILE" ${endpointFlag}
|
|
16
|
+
rm -f "$DB_FILE"
|
|
17
|
+
`
|
|
18
|
+
: '';
|
|
19
|
+
return `#!/bin/bash
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
TIMESTAMP="${ts}"
|
|
23
|
+
DEPLOY_PATH="${config.remotePath}"
|
|
24
|
+
S3_BUCKET="${config.s3Bucket}"
|
|
25
|
+
S3_PREFIX="${prefix}"
|
|
26
|
+
|
|
27
|
+
echo "[shipnode-backup] Starting backup at $TIMESTAMP"
|
|
28
|
+
|
|
29
|
+
# Shared files backup
|
|
30
|
+
SHARED_ARCHIVE="/tmp/shared_${ts}.tar.gz"
|
|
31
|
+
tar -czf "$SHARED_ARCHIVE" -C "$DEPLOY_PATH/shared" . 2>/dev/null || true
|
|
32
|
+
aws s3 cp "$SHARED_ARCHIVE" "s3://$S3_BUCKET/${prefix}shared/shared_$TIMESTAMP.tar.gz" ${endpointFlag}
|
|
33
|
+
rm -f "$SHARED_ARCHIVE"
|
|
34
|
+
${dbDump}
|
|
35
|
+
echo "[shipnode-backup] Done"
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
export function scheduleToSystemd(schedule) {
|
|
39
|
+
const map = {
|
|
40
|
+
hourly: '*:00:00',
|
|
41
|
+
daily: '*-*-* 02:00:00',
|
|
42
|
+
weekly: 'Mon *-*-* 02:00:00',
|
|
43
|
+
};
|
|
44
|
+
return map[schedule] ?? map.daily;
|
|
45
|
+
}
|
|
46
|
+
export async function cmdBackupSetup(cwd, options) {
|
|
47
|
+
await runRemoteCommand(cwd, async ({ config, executor }) => {
|
|
48
|
+
if (!config.backup) {
|
|
49
|
+
ui.error('No backup config found. Add a backup block to shipnode.config.ts');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const { s3Bucket, s3Prefix, s3Endpoint, schedule = 'daily', retentionDays = 14 } = config.backup;
|
|
53
|
+
ui.info('Setting up backup...');
|
|
54
|
+
// Check awscli present
|
|
55
|
+
const awsCheck = await executor.exec('command -v aws 2>/dev/null || echo MISSING');
|
|
56
|
+
if (awsCheck.stdout.trim() === 'MISSING') {
|
|
57
|
+
ui.error('aws CLI not found on server. Install it first: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const script = buildBackupScript({
|
|
61
|
+
remotePath: config.remotePath,
|
|
62
|
+
s3Bucket,
|
|
63
|
+
s3Prefix,
|
|
64
|
+
s3Endpoint,
|
|
65
|
+
dbType: config.database?.type,
|
|
66
|
+
dbName: config.database?.name,
|
|
67
|
+
dbUser: config.database?.user,
|
|
68
|
+
dbHost: config.database?.host,
|
|
69
|
+
dbPort: config.database?.port,
|
|
70
|
+
});
|
|
71
|
+
const b64 = Buffer.from(script).toString('base64');
|
|
72
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
73
|
+
`printf '%s' '${b64}' | base64 -d | $SUDO tee ${BACKUP_SCRIPT_PATH} > /dev/null && ` +
|
|
74
|
+
`$SUDO chmod +x ${BACKUP_SCRIPT_PATH}`);
|
|
75
|
+
const onCalendar = scheduleToSystemd(schedule);
|
|
76
|
+
const serviceUnit = `[Unit]
|
|
77
|
+
Description=ShipNode backup
|
|
78
|
+
|
|
79
|
+
[Service]
|
|
80
|
+
Type=oneshot
|
|
81
|
+
ExecStart=${BACKUP_SCRIPT_PATH}
|
|
82
|
+
`;
|
|
83
|
+
const timerUnit = `[Unit]
|
|
84
|
+
Description=ShipNode backup timer
|
|
85
|
+
|
|
86
|
+
[Timer]
|
|
87
|
+
OnCalendar=${onCalendar}
|
|
88
|
+
Persistent=true
|
|
89
|
+
|
|
90
|
+
[Install]
|
|
91
|
+
WantedBy=timers.target
|
|
92
|
+
`;
|
|
93
|
+
const svcB64 = Buffer.from(serviceUnit).toString('base64');
|
|
94
|
+
const timerB64 = Buffer.from(timerUnit).toString('base64');
|
|
95
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
96
|
+
`printf '%s' '${svcB64}' | base64 -d | $SUDO tee /etc/systemd/system/${TIMER_NAME}.service > /dev/null && ` +
|
|
97
|
+
`printf '%s' '${timerB64}' | base64 -d | $SUDO tee /etc/systemd/system/${TIMER_NAME}.timer > /dev/null && ` +
|
|
98
|
+
`$SUDO systemctl daemon-reload && ` +
|
|
99
|
+
`$SUDO systemctl enable --now ${TIMER_NAME}.timer`);
|
|
100
|
+
ui.success(`Backup scheduled (${schedule}, retain ${retentionDays} days).`);
|
|
101
|
+
ui.info(`Script: ${BACKUP_SCRIPT_PATH}`);
|
|
102
|
+
}, { configPath: options.config });
|
|
103
|
+
}
|
|
104
|
+
export async function cmdBackupRun(cwd, options) {
|
|
105
|
+
await runRemoteCommand(cwd, async ({ executor }) => {
|
|
106
|
+
const check = await executor.exec(`[ -f "${BACKUP_SCRIPT_PATH}" ] && echo EXISTS || echo MISSING`);
|
|
107
|
+
if (check.stdout.trim() === 'MISSING') {
|
|
108
|
+
ui.error('Backup not set up. Run: shipnode backup setup');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
ui.info('Running backup now...');
|
|
112
|
+
const result = await executor.exec(BACKUP_SCRIPT_PATH, { timeout: 300_000 });
|
|
113
|
+
console.log(result.stdout);
|
|
114
|
+
if (result.stderr)
|
|
115
|
+
console.error(result.stderr);
|
|
116
|
+
ui.success('Backup complete.');
|
|
117
|
+
}, { configPath: options.config });
|
|
118
|
+
}
|
|
119
|
+
export async function cmdBackupStatus(cwd, options) {
|
|
120
|
+
await runRemoteCommand(cwd, async ({ executor }) => {
|
|
121
|
+
const timerResult = await executor.exec(`systemctl status ${TIMER_NAME}.timer 2>&1 || echo "Timer not found"`);
|
|
122
|
+
console.log(timerResult.stdout);
|
|
123
|
+
const lastResult = await executor.exec(`journalctl -u ${TIMER_NAME}.service -n 20 --no-pager 2>/dev/null || echo "No logs"`);
|
|
124
|
+
console.log(lastResult.stdout);
|
|
125
|
+
}, { configPath: options.config });
|
|
126
|
+
}
|
|
127
|
+
export async function cmdBackupList(cwd, options) {
|
|
128
|
+
await runRemoteCommand(cwd, async ({ config, executor }) => {
|
|
129
|
+
if (!config.backup) {
|
|
130
|
+
ui.error('No backup config found.');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const { s3Bucket, s3Prefix, s3Endpoint } = config.backup;
|
|
134
|
+
const prefix = s3Prefix ? s3Prefix.replace(/\/$/, '') + '/' : '';
|
|
135
|
+
const endpointFlag = s3Endpoint ? `--endpoint-url "${s3Endpoint}"` : '';
|
|
136
|
+
const result = await executor.exec(`aws s3 ls "s3://${s3Bucket}/${prefix}" --recursive ${endpointFlag} | sort -r | head -30`);
|
|
137
|
+
if (!result.stdout.trim()) {
|
|
138
|
+
ui.info('No backups found.');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.log(result.stdout);
|
|
142
|
+
}, { configPath: options.config });
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../../src/cli/commands/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/ci.ts"],"names":[],"mappings":"AAsHA,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC5D;AAsCD,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { readFile, writeFile, pathExists, ensureDir } from 'fs-extra';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import { loadConfig } from '../../config/loader.js';
|
|
5
|
+
import { confirm } from '../prompt.js';
|
|
6
|
+
import { ui } from '../ui.js';
|
|
7
|
+
async function detectPkgManager(cwd) {
|
|
8
|
+
if (await pathExists(resolve(cwd, 'pnpm-lock.yaml'))) {
|
|
9
|
+
return {
|
|
10
|
+
id: 'pnpm',
|
|
11
|
+
cacheKey: 'pnpm',
|
|
12
|
+
setupAction: 'pnpm/action-setup@v4',
|
|
13
|
+
installCmd: 'pnpm install --frozen-lockfile',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (await pathExists(resolve(cwd, 'yarn.lock'))) {
|
|
17
|
+
return {
|
|
18
|
+
id: 'yarn',
|
|
19
|
+
cacheKey: 'yarn',
|
|
20
|
+
setupAction: '',
|
|
21
|
+
installCmd: 'yarn install --frozen-lockfile',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (await pathExists(resolve(cwd, 'bun.lockb'))) {
|
|
25
|
+
return {
|
|
26
|
+
id: 'bun',
|
|
27
|
+
cacheKey: 'bun',
|
|
28
|
+
setupAction: 'oven-sh/setup-bun@v2',
|
|
29
|
+
installCmd: 'bun install',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
id: 'npm',
|
|
34
|
+
cacheKey: 'npm',
|
|
35
|
+
setupAction: '',
|
|
36
|
+
installCmd: 'npm ci',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function hasBuildScript(cwd) {
|
|
40
|
+
const pkgPath = resolve(cwd, 'package.json');
|
|
41
|
+
if (!(await pathExists(pkgPath)))
|
|
42
|
+
return false;
|
|
43
|
+
try {
|
|
44
|
+
const raw = await readFile(pkgPath, 'utf8');
|
|
45
|
+
const pkg = JSON.parse(raw);
|
|
46
|
+
return Boolean(pkg.scripts?.build);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function generateWorkflow(pm, withBuild) {
|
|
53
|
+
const setupPmStep = pm.setupAction
|
|
54
|
+
? ` - name: Setup ${pm.id}\n uses: ${pm.setupAction}\n\n`
|
|
55
|
+
: '';
|
|
56
|
+
const buildStep = withBuild
|
|
57
|
+
? `\n - name: Build\n run: ${pm.id} run build\n`
|
|
58
|
+
: '';
|
|
59
|
+
return `name: Deploy via Shipnode
|
|
60
|
+
|
|
61
|
+
on:
|
|
62
|
+
push:
|
|
63
|
+
branches:
|
|
64
|
+
- main
|
|
65
|
+
- master
|
|
66
|
+
workflow_dispatch:
|
|
67
|
+
|
|
68
|
+
jobs:
|
|
69
|
+
deploy:
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
|
|
72
|
+
steps:
|
|
73
|
+
- name: Checkout
|
|
74
|
+
uses: actions/checkout@v4
|
|
75
|
+
|
|
76
|
+
- name: Setup Node.js
|
|
77
|
+
uses: actions/setup-node@v4
|
|
78
|
+
with:
|
|
79
|
+
node-version: '20'
|
|
80
|
+
cache: '${pm.cacheKey}'
|
|
81
|
+
|
|
82
|
+
${setupPmStep} - name: Install dependencies
|
|
83
|
+
run: ${pm.installCmd}
|
|
84
|
+
${buildStep}
|
|
85
|
+
- name: Setup SSH agent
|
|
86
|
+
uses: webfactory/ssh-agent@v0.9.0
|
|
87
|
+
with:
|
|
88
|
+
ssh-private-key: \${{ secrets.SHIPNODE_SSH_KEY }}
|
|
89
|
+
|
|
90
|
+
- name: Add server to known hosts
|
|
91
|
+
run: |
|
|
92
|
+
mkdir -p ~/.ssh
|
|
93
|
+
echo "\${{ secrets.SHIPNODE_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
|
|
94
|
+
|
|
95
|
+
- name: Install Shipnode
|
|
96
|
+
run: npm install -g shipnode
|
|
97
|
+
|
|
98
|
+
- name: Deploy
|
|
99
|
+
run: shipnode deploy
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
// ── cmdCiGithub ───────────────────────────────────────────────────────────────
|
|
103
|
+
export async function cmdCiGithub(cwd) {
|
|
104
|
+
const pm = await detectPkgManager(cwd);
|
|
105
|
+
const withBuild = await hasBuildScript(cwd);
|
|
106
|
+
ui.info(`Detected package manager: ${pm.id}`);
|
|
107
|
+
if (withBuild) {
|
|
108
|
+
ui.info('Found scripts.build in package.json — build step will be included.');
|
|
109
|
+
}
|
|
110
|
+
const workflowDir = resolve(cwd, '.github', 'workflows');
|
|
111
|
+
const workflowPath = resolve(workflowDir, 'shipnode-deploy.yml');
|
|
112
|
+
if (await pathExists(workflowPath)) {
|
|
113
|
+
const overwrite = await confirm(`${workflowPath} already exists. Overwrite?`);
|
|
114
|
+
if (!overwrite) {
|
|
115
|
+
ui.info('Aborted — existing workflow file kept.');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
await ensureDir(workflowDir);
|
|
120
|
+
const content = generateWorkflow(pm, withBuild);
|
|
121
|
+
await writeFile(workflowPath, content, 'utf8');
|
|
122
|
+
ui.success(`Workflow written to .github/workflows/shipnode-deploy.yml`);
|
|
123
|
+
ui.heading('Required GitHub Secrets');
|
|
124
|
+
console.log(' Add the following secrets in your GitHub repository settings:');
|
|
125
|
+
console.log(' (Settings → Secrets and variables → Actions → New repository secret)');
|
|
126
|
+
console.log('');
|
|
127
|
+
console.log(' SHIPNODE_SSH_KEY Your SSH private key for server access');
|
|
128
|
+
console.log(' SHIPNODE_KNOWN_HOSTS Known hosts entry for your server');
|
|
129
|
+
console.log(' (run: ssh-keyscan -H YOUR_HOST)');
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(' Note: Host, user, and port are read from shipnode.config.ts (committed to repo).');
|
|
132
|
+
console.log(' Only the private key must be kept as a secret.');
|
|
133
|
+
}
|
|
134
|
+
function parseEnvFile(content) {
|
|
135
|
+
const vars = [];
|
|
136
|
+
for (const line of content.split('\n')) {
|
|
137
|
+
const trimmed = line.trim();
|
|
138
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
139
|
+
continue;
|
|
140
|
+
const eqIdx = trimmed.indexOf('=');
|
|
141
|
+
if (eqIdx === -1)
|
|
142
|
+
continue;
|
|
143
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
144
|
+
const value = trimmed.slice(eqIdx + 1).trim();
|
|
145
|
+
if (key)
|
|
146
|
+
vars.push({ key, value });
|
|
147
|
+
}
|
|
148
|
+
return vars;
|
|
149
|
+
}
|
|
150
|
+
async function setGhSecret(key, value) {
|
|
151
|
+
try {
|
|
152
|
+
await execa('gh', ['secret', 'set', key, '--body', value]);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
157
|
+
if (msg.includes('command not found') || msg.includes('ENOENT')) {
|
|
158
|
+
throw new Error('GitHub CLI (gh) not found. Install it from https://cli.github.com/ and authenticate with `gh auth login`.');
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export async function cmdCiEnvSync(cwd, options) {
|
|
164
|
+
const config = await loadConfig(cwd, undefined);
|
|
165
|
+
const envPath = resolve(cwd, config.envFile);
|
|
166
|
+
if (!(await pathExists(envPath))) {
|
|
167
|
+
ui.error(`Env file not found: ${config.envFile}`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
const content = await readFile(envPath, 'utf8');
|
|
171
|
+
const envVars = parseEnvFile(content);
|
|
172
|
+
if (envVars.length === 0) {
|
|
173
|
+
ui.warn('No variables found in env file.');
|
|
174
|
+
}
|
|
175
|
+
else if (!options.all) {
|
|
176
|
+
console.log('\nVariables to sync from env file:');
|
|
177
|
+
for (const { key } of envVars) {
|
|
178
|
+
console.log(` ${key}`);
|
|
179
|
+
}
|
|
180
|
+
console.log('');
|
|
181
|
+
const proceed = await confirm(`Sync ${envVars.length} variable(s) to GitHub Secrets?`);
|
|
182
|
+
if (!proceed) {
|
|
183
|
+
ui.info('Sync cancelled.');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
let set = 0;
|
|
188
|
+
let failed = 0;
|
|
189
|
+
// Sync env file vars
|
|
190
|
+
for (const { key, value } of envVars) {
|
|
191
|
+
const ok = await setGhSecret(key, value);
|
|
192
|
+
if (ok) {
|
|
193
|
+
ui.success(`Set: ${key}`);
|
|
194
|
+
set++;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
ui.warn(`Failed: ${key}`);
|
|
198
|
+
failed++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Sync Shipnode connection vars from config
|
|
202
|
+
const shipnodeSecrets = [
|
|
203
|
+
{ key: 'SHIPNODE_SSH_HOST', value: config.ssh.host },
|
|
204
|
+
{ key: 'SHIPNODE_SSH_USER', value: config.ssh.user },
|
|
205
|
+
{ key: 'SHIPNODE_SSH_PORT', value: String(config.ssh.port) },
|
|
206
|
+
];
|
|
207
|
+
ui.info('Syncing Shipnode connection secrets...');
|
|
208
|
+
for (const { key, value } of shipnodeSecrets) {
|
|
209
|
+
const ok = await setGhSecret(key, value);
|
|
210
|
+
if (ok) {
|
|
211
|
+
ui.success(`Set: ${key}`);
|
|
212
|
+
set++;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
ui.warn(`Failed: ${key}`);
|
|
216
|
+
failed++;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
ui.heading('Sync Summary');
|
|
220
|
+
ui.info(`Set: ${set} Skipped: 0 Failed: ${failed}`);
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=ci.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../../../src/cli/commands/ci.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function cmdCloudflareInit(cwd: string, options: {
|
|
2
|
+
config?: string;
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function cmdCloudflareAudit(cwd: string, options: {
|
|
5
|
+
config?: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function cmdCloudflareStatus(cwd: string, options: {
|
|
8
|
+
config?: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=cloudflare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/cloudflare.ts"],"names":[],"mappings":"AAmCA,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAiIf;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmDf;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { runRemoteCommand } from '../runner.js';
|
|
2
|
+
import { ui } from '../ui.js';
|
|
3
|
+
const CF_API = 'https://api.cloudflare.com/client/v4';
|
|
4
|
+
async function cfFetch(path, opts, init = {}) {
|
|
5
|
+
const res = await fetch(`${CF_API}${path}`, {
|
|
6
|
+
...init,
|
|
7
|
+
headers: {
|
|
8
|
+
'Authorization': `Bearer ${opts.apiToken}`,
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
...(init.headers ?? {}),
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
const json = await res.json();
|
|
14
|
+
if (!json.success) {
|
|
15
|
+
const msg = json.errors?.map((e) => e.message).join(', ') ?? 'Unknown error';
|
|
16
|
+
throw new Error(`Cloudflare API error: ${msg}`);
|
|
17
|
+
}
|
|
18
|
+
return json.result;
|
|
19
|
+
}
|
|
20
|
+
function requireToken() {
|
|
21
|
+
const token = process.env['CLOUDFLARE_API_TOKEN'] ?? process.env['CF_API_TOKEN'];
|
|
22
|
+
if (!token) {
|
|
23
|
+
ui.error('CLOUDFLARE_API_TOKEN env var required');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
28
|
+
export async function cmdCloudflareInit(cwd, options) {
|
|
29
|
+
await runRemoteCommand(cwd, async ({ config, executor }) => {
|
|
30
|
+
if (!config.cloudflare) {
|
|
31
|
+
ui.error('No cloudflare config found. Add a cloudflare block to shipnode.config.ts');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const cf = config.cloudflare;
|
|
35
|
+
const apiToken = requireToken();
|
|
36
|
+
ui.info('Installing cloudflared...');
|
|
37
|
+
const installCheck = await executor.exec('command -v cloudflared 2>/dev/null || echo MISSING');
|
|
38
|
+
if (installCheck.stdout.trim() === 'MISSING') {
|
|
39
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
40
|
+
`curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | $SUDO tee /usr/share/keyrings/cloudflare-main.gpg > /dev/null && ` +
|
|
41
|
+
`echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | ` +
|
|
42
|
+
`$SUDO tee /etc/apt/sources.list.d/cloudflared.list && ` +
|
|
43
|
+
`$SUDO apt-get update -qq && $SUDO apt-get install -y -qq cloudflared`);
|
|
44
|
+
ui.success('cloudflared installed');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
ui.info('cloudflared already installed');
|
|
48
|
+
}
|
|
49
|
+
const tunnelName = cf.tunnelName ?? `shipnode-${config.ssh.host.replace(/\./g, '-')}`;
|
|
50
|
+
ui.info(`Creating tunnel: ${tunnelName}`);
|
|
51
|
+
// Create tunnel via API to get ID, then install on server
|
|
52
|
+
const cfApiOpts = { apiToken };
|
|
53
|
+
// Get zone ID
|
|
54
|
+
const zones = await cfFetch(`/zones?name=${cf.zone}`, cfApiOpts);
|
|
55
|
+
if (!zones.length)
|
|
56
|
+
throw new Error(`Zone "${cf.zone}" not found`);
|
|
57
|
+
const zoneId = zones[0].id;
|
|
58
|
+
// Create tunnel (remote execution: cloudflared tunnel create)
|
|
59
|
+
const createResult = await executor.exec(`cloudflared tunnel create "${tunnelName}" 2>&1`);
|
|
60
|
+
ui.info(createResult.stdout.trim());
|
|
61
|
+
// Get tunnel UUID from list
|
|
62
|
+
const listResult = await executor.exec(`cloudflared tunnel list --output json 2>/dev/null | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4`);
|
|
63
|
+
const tunnelId = listResult.stdout.trim();
|
|
64
|
+
if (!tunnelId) {
|
|
65
|
+
ui.error('Could not determine tunnel ID. Check: cloudflared tunnel list');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
// Create DNS records if appHostname set
|
|
69
|
+
if (cf.appHostname) {
|
|
70
|
+
await executor.exec(`cloudflared tunnel route dns "${tunnelName}" "${cf.appHostname}"`);
|
|
71
|
+
ui.success(`DNS: ${cf.appHostname} → tunnel`);
|
|
72
|
+
}
|
|
73
|
+
if (cf.sshHostname) {
|
|
74
|
+
await executor.exec(`cloudflared tunnel route dns "${tunnelName}" "${cf.sshHostname}"`);
|
|
75
|
+
ui.success(`DNS: ${cf.sshHostname} → tunnel`);
|
|
76
|
+
}
|
|
77
|
+
// Generate config and start as systemd service
|
|
78
|
+
const appIngress = cf.appHostname
|
|
79
|
+
? ` - hostname: ${cf.appHostname}\n service: http://localhost:${config.backend?.port ?? 3000}`
|
|
80
|
+
: '';
|
|
81
|
+
const sshIngress = cf.sshHostname
|
|
82
|
+
? ` - hostname: ${cf.sshHostname}\n service: ssh://localhost:22`
|
|
83
|
+
: '';
|
|
84
|
+
const cfConfig = `tunnel: ${tunnelId}
|
|
85
|
+
credentials-file: /root/.cloudflared/${tunnelId}.json
|
|
86
|
+
|
|
87
|
+
ingress:
|
|
88
|
+
${appIngress}
|
|
89
|
+
${sshIngress}
|
|
90
|
+
- service: http_status:404
|
|
91
|
+
`;
|
|
92
|
+
const b64 = Buffer.from(cfConfig).toString('base64');
|
|
93
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
94
|
+
`mkdir -p /etc/cloudflared && ` +
|
|
95
|
+
`printf '%s' '${b64}' | base64 -d | $SUDO tee /etc/cloudflared/config.yml > /dev/null`);
|
|
96
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
97
|
+
`$SUDO cloudflared service install && $SUDO systemctl start cloudflared`);
|
|
98
|
+
// Setup firewall lockdown if requested
|
|
99
|
+
if (cf.lockdownFirewall) {
|
|
100
|
+
ui.info('Locking down firewall to Cloudflare IPs only...');
|
|
101
|
+
const cfIpv4 = await cfFetch('/ips', cfApiOpts);
|
|
102
|
+
for (const cidr of cfIpv4.ipv4_cidrs ?? []) {
|
|
103
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
104
|
+
`$SUDO ufw allow from ${cidr} to any port ${config.backend?.port ?? 3000} 2>/dev/null || true`);
|
|
105
|
+
}
|
|
106
|
+
await executor.exec(`SUDO=""; [ "$EUID" -ne 0 ] && SUDO="sudo"; ` +
|
|
107
|
+
`$SUDO ufw deny ${config.backend?.port ?? 3000} 2>/dev/null || true`);
|
|
108
|
+
ui.success('Firewall locked to Cloudflare IPs');
|
|
109
|
+
}
|
|
110
|
+
// Setup Access policy if accessEmails present
|
|
111
|
+
if (cf.accessEmails?.length && zoneId) {
|
|
112
|
+
ui.info('Setting up Cloudflare Access policy...');
|
|
113
|
+
await cfFetch(`/accounts`, cfApiOpts); // just to verify token works
|
|
114
|
+
ui.info(`Access policy: configure manually at dash.cloudflare.com for zone ${cf.zone}`);
|
|
115
|
+
ui.info(`Allowed emails: ${cf.accessEmails.join(', ')}`);
|
|
116
|
+
}
|
|
117
|
+
ui.success(`Cloudflare tunnel "${tunnelName}" initialized.`);
|
|
118
|
+
}, { configPath: options.config });
|
|
119
|
+
}
|
|
120
|
+
export async function cmdCloudflareAudit(cwd, options) {
|
|
121
|
+
await runRemoteCommand(cwd, async ({ config, executor }) => {
|
|
122
|
+
if (!config.cloudflare) {
|
|
123
|
+
ui.error('No cloudflare config found.');
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const apiToken = requireToken();
|
|
127
|
+
const cf = config.cloudflare;
|
|
128
|
+
const cfApiOpts = { apiToken };
|
|
129
|
+
ui.info(`Auditing Cloudflare config for zone: ${cf.zone}`);
|
|
130
|
+
const zones = await cfFetch(`/zones?name=${cf.zone}`, cfApiOpts);
|
|
131
|
+
if (!zones.length) {
|
|
132
|
+
ui.error(`Zone "${cf.zone}" not found in account`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const zone = zones[0];
|
|
136
|
+
console.log(` Zone: ${zone.name} (${zone.id}) — ${zone.status}`);
|
|
137
|
+
// Check DNS records
|
|
138
|
+
if (cf.appHostname) {
|
|
139
|
+
const records = await cfFetch(`/zones/${zone.id}/dns_records?name=${cf.appHostname}`, cfApiOpts);
|
|
140
|
+
if (records.length) {
|
|
141
|
+
console.log(` DNS ${cf.appHostname}: ${records[0].type} → ${records[0].content}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
ui.warn(` DNS ${cf.appHostname}: NOT FOUND`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Check tunnel on server
|
|
148
|
+
const tunnelCheck = await executor.exec('cloudflared tunnel list 2>/dev/null || echo MISSING');
|
|
149
|
+
if (tunnelCheck.stdout.includes('MISSING')) {
|
|
150
|
+
ui.warn(' cloudflared: not installed');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.log(` cloudflared tunnels:\n${tunnelCheck.stdout.trim()}`);
|
|
154
|
+
}
|
|
155
|
+
// Check service
|
|
156
|
+
const svcCheck = await executor.exec('systemctl is-active cloudflared 2>/dev/null || echo inactive');
|
|
157
|
+
console.log(` cloudflared service: ${svcCheck.stdout.trim()}`);
|
|
158
|
+
}, { configPath: options.config });
|
|
159
|
+
}
|
|
160
|
+
export async function cmdCloudflareStatus(cwd, options) {
|
|
161
|
+
await runRemoteCommand(cwd, async ({ executor }) => {
|
|
162
|
+
const result = await executor.exec(`systemctl status cloudflared 2>&1; echo "---"; cloudflared tunnel info 2>&1 || true`);
|
|
163
|
+
console.log(result.stdout);
|
|
164
|
+
}, { configPath: options.config });
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=cloudflare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/cli/commands/cloudflare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function cmdConfigShow(cwd: string, options: {
|
|
2
|
+
config?: string;
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function cmdConfigValidate(cwd: string, options: {
|
|
5
|
+
config?: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function cmdConfigPath(cwd: string, options: {
|
|
8
|
+
config?: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAMA,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE5F;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAShG;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5F"}
|