@cicore/cli 1.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/bin/ci.js +13 -0
- package/dist/commands/addon/api-actions.d.ts +45 -0
- package/dist/commands/addon/api-actions.d.ts.map +1 -0
- package/dist/commands/addon/api-actions.js +281 -0
- package/dist/commands/addon/api-actions.js.map +1 -0
- package/dist/commands/addon/build.d.ts +11 -0
- package/dist/commands/addon/build.d.ts.map +1 -0
- package/dist/commands/addon/build.js +182 -0
- package/dist/commands/addon/build.js.map +1 -0
- package/dist/commands/addon/create.d.ts +11 -0
- package/dist/commands/addon/create.d.ts.map +1 -0
- package/dist/commands/addon/create.js +1186 -0
- package/dist/commands/addon/create.js.map +1 -0
- package/dist/commands/addon/delete.d.ts +13 -0
- package/dist/commands/addon/delete.d.ts.map +1 -0
- package/dist/commands/addon/delete.js +83 -0
- package/dist/commands/addon/delete.js.map +1 -0
- package/dist/commands/addon/deploy.d.ts +27 -0
- package/dist/commands/addon/deploy.d.ts.map +1 -0
- package/dist/commands/addon/deploy.js +459 -0
- package/dist/commands/addon/deploy.js.map +1 -0
- package/dist/commands/addon/dev-deploy.d.ts +31 -0
- package/dist/commands/addon/dev-deploy.d.ts.map +1 -0
- package/dist/commands/addon/dev-deploy.js +128 -0
- package/dist/commands/addon/dev-deploy.js.map +1 -0
- package/dist/commands/addon/dev.d.ts +36 -0
- package/dist/commands/addon/dev.d.ts.map +1 -0
- package/dist/commands/addon/dev.js +323 -0
- package/dist/commands/addon/dev.js.map +1 -0
- package/dist/commands/addon/extract-classes.d.ts +23 -0
- package/dist/commands/addon/extract-classes.d.ts.map +1 -0
- package/dist/commands/addon/extract-classes.js +281 -0
- package/dist/commands/addon/extract-classes.js.map +1 -0
- package/dist/commands/addon/generate-safelist.d.ts +24 -0
- package/dist/commands/addon/generate-safelist.d.ts.map +1 -0
- package/dist/commands/addon/generate-safelist.js +276 -0
- package/dist/commands/addon/generate-safelist.js.map +1 -0
- package/dist/commands/addon/index.d.ts +19 -0
- package/dist/commands/addon/index.d.ts.map +1 -0
- package/dist/commands/addon/index.js +296 -0
- package/dist/commands/addon/index.js.map +1 -0
- package/dist/commands/addon/init-repo.d.ts +25 -0
- package/dist/commands/addon/init-repo.d.ts.map +1 -0
- package/dist/commands/addon/init-repo.js +171 -0
- package/dist/commands/addon/init-repo.js.map +1 -0
- package/dist/commands/addon/install.d.ts +23 -0
- package/dist/commands/addon/install.d.ts.map +1 -0
- package/dist/commands/addon/install.js +84 -0
- package/dist/commands/addon/install.js.map +1 -0
- package/dist/commands/addon/list.d.ts +10 -0
- package/dist/commands/addon/list.d.ts.map +1 -0
- package/dist/commands/addon/list.js +102 -0
- package/dist/commands/addon/list.js.map +1 -0
- package/dist/commands/addon/manifest-refresh.d.ts +17 -0
- package/dist/commands/addon/manifest-refresh.d.ts.map +1 -0
- package/dist/commands/addon/manifest-refresh.js +48 -0
- package/dist/commands/addon/manifest-refresh.js.map +1 -0
- package/dist/commands/addon/migrate.d.ts +40 -0
- package/dist/commands/addon/migrate.d.ts.map +1 -0
- package/dist/commands/addon/migrate.js +236 -0
- package/dist/commands/addon/migrate.js.map +1 -0
- package/dist/commands/addon/publish.d.ts +33 -0
- package/dist/commands/addon/publish.d.ts.map +1 -0
- package/dist/commands/addon/publish.js +236 -0
- package/dist/commands/addon/publish.js.map +1 -0
- package/dist/commands/addon/scaffold-quality.d.ts +21 -0
- package/dist/commands/addon/scaffold-quality.d.ts.map +1 -0
- package/dist/commands/addon/scaffold-quality.js +90 -0
- package/dist/commands/addon/scaffold-quality.js.map +1 -0
- package/dist/commands/addon/sign.d.ts +9 -0
- package/dist/commands/addon/sign.d.ts.map +1 -0
- package/dist/commands/addon/sign.js +83 -0
- package/dist/commands/addon/sign.js.map +1 -0
- package/dist/commands/addon/toggle.d.ts +6 -0
- package/dist/commands/addon/toggle.d.ts.map +1 -0
- package/dist/commands/addon/toggle.js +46 -0
- package/dist/commands/addon/toggle.js.map +1 -0
- package/dist/commands/agent/index.d.ts +34 -0
- package/dist/commands/agent/index.d.ts.map +1 -0
- package/dist/commands/agent/index.js +564 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/brand/index.d.ts +54 -0
- package/dist/commands/brand/index.d.ts.map +1 -0
- package/dist/commands/brand/index.js +367 -0
- package/dist/commands/brand/index.js.map +1 -0
- package/dist/commands/build/index.d.ts +53 -0
- package/dist/commands/build/index.d.ts.map +1 -0
- package/dist/commands/build/index.js +726 -0
- package/dist/commands/build/index.js.map +1 -0
- package/dist/commands/cache/flush-local.d.ts +31 -0
- package/dist/commands/cache/flush-local.d.ts.map +1 -0
- package/dist/commands/cache/flush-local.js +161 -0
- package/dist/commands/cache/flush-local.js.map +1 -0
- package/dist/commands/cache/index.d.ts +14 -0
- package/dist/commands/cache/index.d.ts.map +1 -0
- package/dist/commands/cache/index.js +453 -0
- package/dist/commands/cache/index.js.map +1 -0
- package/dist/commands/check/index.d.ts +8 -0
- package/dist/commands/check/index.d.ts.map +1 -0
- package/dist/commands/check/index.js +1316 -0
- package/dist/commands/check/index.js.map +1 -0
- package/dist/commands/cloudflare/index.d.ts +8 -0
- package/dist/commands/cloudflare/index.d.ts.map +1 -0
- package/dist/commands/cloudflare/index.js +453 -0
- package/dist/commands/cloudflare/index.js.map +1 -0
- package/dist/commands/core/create.d.ts +12 -0
- package/dist/commands/core/create.d.ts.map +1 -0
- package/dist/commands/core/create.js +206 -0
- package/dist/commands/core/create.js.map +1 -0
- package/dist/commands/core/delete.d.ts +11 -0
- package/dist/commands/core/delete.d.ts.map +1 -0
- package/dist/commands/core/delete.js +64 -0
- package/dist/commands/core/delete.js.map +1 -0
- package/dist/commands/core/env.d.ts +12 -0
- package/dist/commands/core/env.d.ts.map +1 -0
- package/dist/commands/core/env.js +95 -0
- package/dist/commands/core/env.js.map +1 -0
- package/dist/commands/core/health.d.ts +6 -0
- package/dist/commands/core/health.d.ts.map +1 -0
- package/dist/commands/core/health.js +215 -0
- package/dist/commands/core/health.js.map +1 -0
- package/dist/commands/core/index.d.ts +15 -0
- package/dist/commands/core/index.d.ts.map +1 -0
- package/dist/commands/core/index.js +86 -0
- package/dist/commands/core/index.js.map +1 -0
- package/dist/commands/core/list.d.ts +11 -0
- package/dist/commands/core/list.d.ts.map +1 -0
- package/dist/commands/core/list.js +58 -0
- package/dist/commands/core/list.js.map +1 -0
- package/dist/commands/core/rebuild.d.ts +13 -0
- package/dist/commands/core/rebuild.d.ts.map +1 -0
- package/dist/commands/core/rebuild.js +119 -0
- package/dist/commands/core/rebuild.js.map +1 -0
- package/dist/commands/db/index.d.ts +23 -0
- package/dist/commands/db/index.d.ts.map +1 -0
- package/dist/commands/db/index.js +355 -0
- package/dist/commands/db/index.js.map +1 -0
- package/dist/commands/db/promote-silo.d.ts +320 -0
- package/dist/commands/db/promote-silo.d.ts.map +1 -0
- package/dist/commands/db/promote-silo.js +930 -0
- package/dist/commands/db/promote-silo.js.map +1 -0
- package/dist/commands/db/relocate.d.ts +41 -0
- package/dist/commands/db/relocate.d.ts.map +1 -0
- package/dist/commands/db/relocate.js +482 -0
- package/dist/commands/db/relocate.js.map +1 -0
- package/dist/commands/db/rollback-silo.d.ts +44 -0
- package/dist/commands/db/rollback-silo.d.ts.map +1 -0
- package/dist/commands/db/rollback-silo.js +402 -0
- package/dist/commands/db/rollback-silo.js.map +1 -0
- package/dist/commands/deploy/index.d.ts +26 -0
- package/dist/commands/deploy/index.d.ts.map +1 -0
- package/dist/commands/deploy/index.js +107 -0
- package/dist/commands/deploy/index.js.map +1 -0
- package/dist/commands/devops/index.d.ts +6 -0
- package/dist/commands/devops/index.d.ts.map +1 -0
- package/dist/commands/devops/index.js +220 -0
- package/dist/commands/devops/index.js.map +1 -0
- package/dist/commands/domain/index.d.ts +8 -0
- package/dist/commands/domain/index.d.ts.map +1 -0
- package/dist/commands/domain/index.js +386 -0
- package/dist/commands/domain/index.js.map +1 -0
- package/dist/commands/image/index.d.ts +8 -0
- package/dist/commands/image/index.d.ts.map +1 -0
- package/dist/commands/image/index.js +308 -0
- package/dist/commands/image/index.js.map +1 -0
- package/dist/commands/install/factory-reset.d.ts +21 -0
- package/dist/commands/install/factory-reset.d.ts.map +1 -0
- package/dist/commands/install/factory-reset.js +83 -0
- package/dist/commands/install/factory-reset.js.map +1 -0
- package/dist/commands/install/index.d.ts +17 -0
- package/dist/commands/install/index.d.ts.map +1 -0
- package/dist/commands/install/index.js +44 -0
- package/dist/commands/install/index.js.map +1 -0
- package/dist/commands/install/install.d.ts +35 -0
- package/dist/commands/install/install.d.ts.map +1 -0
- package/dist/commands/install/install.js +171 -0
- package/dist/commands/install/install.js.map +1 -0
- package/dist/commands/login/index.d.ts +15 -0
- package/dist/commands/login/index.d.ts.map +1 -0
- package/dist/commands/login/index.js +58 -0
- package/dist/commands/login/index.js.map +1 -0
- package/dist/commands/nginx/index.d.ts +11 -0
- package/dist/commands/nginx/index.d.ts.map +1 -0
- package/dist/commands/nginx/index.js +580 -0
- package/dist/commands/nginx/index.js.map +1 -0
- package/dist/commands/server/bootstrap.d.ts +25 -0
- package/dist/commands/server/bootstrap.d.ts.map +1 -0
- package/dist/commands/server/bootstrap.js +260 -0
- package/dist/commands/server/bootstrap.js.map +1 -0
- package/dist/commands/server/index.d.ts +8 -0
- package/dist/commands/server/index.d.ts.map +1 -0
- package/dist/commands/server/index.js +2524 -0
- package/dist/commands/server/index.js.map +1 -0
- package/dist/commands/setup/index.d.ts +34 -0
- package/dist/commands/setup/index.d.ts.map +1 -0
- package/dist/commands/setup/index.js +423 -0
- package/dist/commands/setup/index.js.map +1 -0
- package/dist/commands/ssl/index.d.ts +8 -0
- package/dist/commands/ssl/index.d.ts.map +1 -0
- package/dist/commands/ssl/index.js +275 -0
- package/dist/commands/ssl/index.js.map +1 -0
- package/dist/commands/superadmin/index.d.ts +16 -0
- package/dist/commands/superadmin/index.d.ts.map +1 -0
- package/dist/commands/superadmin/index.js +81 -0
- package/dist/commands/superadmin/index.js.map +1 -0
- package/dist/commands/tenant/index.d.ts +6 -0
- package/dist/commands/tenant/index.d.ts.map +1 -0
- package/dist/commands/tenant/index.js +192 -0
- package/dist/commands/tenant/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/addon-sign.d.ts +23 -0
- package/dist/lib/addon-sign.d.ts.map +1 -0
- package/dist/lib/addon-sign.js +39 -0
- package/dist/lib/addon-sign.js.map +1 -0
- package/dist/lib/addon-sign.test.d.ts +2 -0
- package/dist/lib/addon-sign.test.d.ts.map +1 -0
- package/dist/lib/addon-sign.test.js +27 -0
- package/dist/lib/addon-sign.test.js.map +1 -0
- package/dist/lib/cdn.d.ts +25 -0
- package/dist/lib/cdn.d.ts.map +1 -0
- package/dist/lib/cdn.js +131 -0
- package/dist/lib/cdn.js.map +1 -0
- package/dist/lib/cloudflare.d.ts +133 -0
- package/dist/lib/cloudflare.d.ts.map +1 -0
- package/dist/lib/cloudflare.js +435 -0
- package/dist/lib/cloudflare.js.map +1 -0
- package/dist/lib/config.d.ts +96 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +132 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env.d.ts +8 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +64 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/hosts.d.ts +194 -0
- package/dist/lib/hosts.d.ts.map +1 -0
- package/dist/lib/hosts.js +183 -0
- package/dist/lib/hosts.js.map +1 -0
- package/dist/lib/logger.d.ts +68 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +130 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/nginx-config.d.ts +78 -0
- package/dist/lib/nginx-config.d.ts.map +1 -0
- package/dist/lib/nginx-config.js +736 -0
- package/dist/lib/nginx-config.js.map +1 -0
- package/dist/lib/ops/addon-dev.d.ts +93 -0
- package/dist/lib/ops/addon-dev.d.ts.map +1 -0
- package/dist/lib/ops/addon-dev.js +237 -0
- package/dist/lib/ops/addon-dev.js.map +1 -0
- package/dist/lib/ops/addon-quality.d.ts +38 -0
- package/dist/lib/ops/addon-quality.d.ts.map +1 -0
- package/dist/lib/ops/addon-quality.js +338 -0
- package/dist/lib/ops/addon-quality.js.map +1 -0
- package/dist/lib/ops/addon-routes.d.ts +49 -0
- package/dist/lib/ops/addon-routes.d.ts.map +1 -0
- package/dist/lib/ops/addon-routes.js +189 -0
- package/dist/lib/ops/addon-routes.js.map +1 -0
- package/dist/lib/ops/addon.d.ts +120 -0
- package/dist/lib/ops/addon.d.ts.map +1 -0
- package/dist/lib/ops/addon.js +260 -0
- package/dist/lib/ops/addon.js.map +1 -0
- package/dist/lib/ops/cdn.d.ts +87 -0
- package/dist/lib/ops/cdn.d.ts.map +1 -0
- package/dist/lib/ops/cdn.js +170 -0
- package/dist/lib/ops/cdn.js.map +1 -0
- package/dist/lib/ops/cf.d.ts +36 -0
- package/dist/lib/ops/cf.d.ts.map +1 -0
- package/dist/lib/ops/cf.js +114 -0
- package/dist/lib/ops/cf.js.map +1 -0
- package/dist/lib/ops/compose.d.ts +95 -0
- package/dist/lib/ops/compose.d.ts.map +1 -0
- package/dist/lib/ops/compose.js +165 -0
- package/dist/lib/ops/compose.js.map +1 -0
- package/dist/lib/ops/core.d.ts +117 -0
- package/dist/lib/ops/core.d.ts.map +1 -0
- package/dist/lib/ops/core.js +322 -0
- package/dist/lib/ops/core.js.map +1 -0
- package/dist/lib/ops/db.d.ts +116 -0
- package/dist/lib/ops/db.d.ts.map +1 -0
- package/dist/lib/ops/db.js +351 -0
- package/dist/lib/ops/db.js.map +1 -0
- package/dist/lib/ops/dns.d.ts +111 -0
- package/dist/lib/ops/dns.d.ts.map +1 -0
- package/dist/lib/ops/dns.js +306 -0
- package/dist/lib/ops/dns.js.map +1 -0
- package/dist/lib/ops/image.d.ts +94 -0
- package/dist/lib/ops/image.d.ts.map +1 -0
- package/dist/lib/ops/image.js +159 -0
- package/dist/lib/ops/image.js.map +1 -0
- package/dist/lib/ops/nginx.d.ts +114 -0
- package/dist/lib/ops/nginx.d.ts.map +1 -0
- package/dist/lib/ops/nginx.js +388 -0
- package/dist/lib/ops/nginx.js.map +1 -0
- package/dist/lib/ops/redis.d.ts +7 -0
- package/dist/lib/ops/redis.d.ts.map +1 -0
- package/dist/lib/ops/redis.js +35 -0
- package/dist/lib/ops/redis.js.map +1 -0
- package/dist/lib/ops/ssh.d.ts +127 -0
- package/dist/lib/ops/ssh.d.ts.map +1 -0
- package/dist/lib/ops/ssh.js +269 -0
- package/dist/lib/ops/ssh.js.map +1 -0
- package/dist/lib/prompts.d.ts +46 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +113 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/sast.d.ts +43 -0
- package/dist/lib/sast.d.ts.map +1 -0
- package/dist/lib/sast.js +79 -0
- package/dist/lib/sast.js.map +1 -0
- package/dist/lib/sast.test.d.ts +2 -0
- package/dist/lib/sast.test.d.ts.map +1 -0
- package/dist/lib/sast.test.js +33 -0
- package/dist/lib/sast.test.js.map +1 -0
- package/dist/lib/shell.d.ts +61 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +183 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/ssh-config.d.ts +37 -0
- package/dist/lib/ssh-config.d.ts.map +1 -0
- package/dist/lib/ssh-config.js +122 -0
- package/dist/lib/ssh-config.js.map +1 -0
- package/dist/lib/tenant-scope.d.ts +38 -0
- package/dist/lib/tenant-scope.d.ts.map +1 -0
- package/dist/lib/tenant-scope.js +129 -0
- package/dist/lib/tenant-scope.js.map +1 -0
- package/dist/lib/tenant-scope.test.d.ts +2 -0
- package/dist/lib/tenant-scope.test.d.ts.map +1 -0
- package/dist/lib/tenant-scope.test.js +223 -0
- package/dist/lib/tenant-scope.test.js.map +1 -0
- package/package.json +58 -0
- package/templates/bootstrap/.env.template +54 -0
- package/templates/bootstrap/docker-compose.yml +145 -0
- package/templates/vhost.conf.tmpl +446 -0
|
@@ -0,0 +1,1316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CiCore CLI - Health Check & Diagnostics
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive system checks for ciCore deployments
|
|
5
|
+
*/
|
|
6
|
+
import { log, spinner, formatTable } from '../../lib/logger.js';
|
|
7
|
+
import { exec } from '../../lib/shell.js';
|
|
8
|
+
import { paths, getDockerRegistry } from '../../lib/config.js';
|
|
9
|
+
import { getSSHHost, buildSSHCommand, listSSHHosts } from '../../lib/ssh-config.js';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
export function registerCheckCommands(program) {
|
|
13
|
+
const check = program
|
|
14
|
+
.command('check')
|
|
15
|
+
.description('Health checks and diagnostics');
|
|
16
|
+
// vu check:all
|
|
17
|
+
check
|
|
18
|
+
.command('all')
|
|
19
|
+
.description('Run all checks')
|
|
20
|
+
.option('-h, --host <host>', 'SSH host for server checks', 'cicore')
|
|
21
|
+
.option('--local', 'Only run local checks')
|
|
22
|
+
.option('--server', 'Only run server checks')
|
|
23
|
+
.action(async (options) => {
|
|
24
|
+
await runAllChecks(options);
|
|
25
|
+
});
|
|
26
|
+
// vu check:local
|
|
27
|
+
check
|
|
28
|
+
.command('local')
|
|
29
|
+
.description('Check local development environment')
|
|
30
|
+
.action(async () => {
|
|
31
|
+
await checkLocal();
|
|
32
|
+
});
|
|
33
|
+
// vu check:server
|
|
34
|
+
check
|
|
35
|
+
.command('server')
|
|
36
|
+
.description('Check server health')
|
|
37
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
38
|
+
.action(async (options) => {
|
|
39
|
+
await checkServer(options);
|
|
40
|
+
});
|
|
41
|
+
// vu check:docker
|
|
42
|
+
check
|
|
43
|
+
.command('docker')
|
|
44
|
+
.description('Check Docker images and containers')
|
|
45
|
+
.option('-h, --host <host>', 'SSH host (or "local")', 'local')
|
|
46
|
+
.action(async (options) => {
|
|
47
|
+
await checkDocker(options);
|
|
48
|
+
});
|
|
49
|
+
// vu check:files
|
|
50
|
+
check
|
|
51
|
+
.command('files')
|
|
52
|
+
.description('Check ciCore file structure')
|
|
53
|
+
.option('-h, --host <host>', 'SSH host for server check', 'cicore')
|
|
54
|
+
.option('-c, --core <core>', 'Core name to check', 'core1')
|
|
55
|
+
.action(async (options) => {
|
|
56
|
+
await checkFiles(options);
|
|
57
|
+
});
|
|
58
|
+
// vu check:addons
|
|
59
|
+
check
|
|
60
|
+
.command('addons')
|
|
61
|
+
.description('Validate addon configurations')
|
|
62
|
+
.option('-h, --host <host>', 'SSH host (or "local")', 'local')
|
|
63
|
+
.option('-c, --core <core>', 'Core name', 'core1')
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
await checkAddons(options);
|
|
66
|
+
});
|
|
67
|
+
// vu check:ssl
|
|
68
|
+
check
|
|
69
|
+
.command('ssl')
|
|
70
|
+
.description('Check SSL certificates')
|
|
71
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
72
|
+
.option('-d, --domain <domain>', 'Domain to check')
|
|
73
|
+
.action(async (options) => {
|
|
74
|
+
await checkSSL(options);
|
|
75
|
+
});
|
|
76
|
+
// vu check:connectivity
|
|
77
|
+
check
|
|
78
|
+
.command('connectivity')
|
|
79
|
+
.description('Check network connectivity')
|
|
80
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
81
|
+
.action(async (options) => {
|
|
82
|
+
await checkConnectivity(options);
|
|
83
|
+
});
|
|
84
|
+
// vu check:requirements
|
|
85
|
+
check
|
|
86
|
+
.command('requirements')
|
|
87
|
+
.alias('req')
|
|
88
|
+
.description('Check server requirements for ciCore installation')
|
|
89
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
90
|
+
.action(async (options) => {
|
|
91
|
+
await checkRequirements(options);
|
|
92
|
+
});
|
|
93
|
+
// vu check:tree
|
|
94
|
+
check
|
|
95
|
+
.command('tree')
|
|
96
|
+
.description('Show server directory structure')
|
|
97
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
98
|
+
.option('-c, --core <core>', 'Core name to show')
|
|
99
|
+
.option('-d, --depth <depth>', 'Tree depth', '3')
|
|
100
|
+
.action(async (options) => {
|
|
101
|
+
await showTree(options);
|
|
102
|
+
});
|
|
103
|
+
// vu check:api
|
|
104
|
+
check
|
|
105
|
+
.command('api')
|
|
106
|
+
.description('Check API endpoints')
|
|
107
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
108
|
+
.option('-d, --domain <domain>', 'Domain to check')
|
|
109
|
+
.action(async (options) => {
|
|
110
|
+
await checkAPI(options);
|
|
111
|
+
});
|
|
112
|
+
// vu check:services
|
|
113
|
+
check
|
|
114
|
+
.command('services')
|
|
115
|
+
.description('Check all ciCore services status')
|
|
116
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
117
|
+
.action(async (options) => {
|
|
118
|
+
await checkServices(options);
|
|
119
|
+
});
|
|
120
|
+
// vu check:ports
|
|
121
|
+
check
|
|
122
|
+
.command('ports')
|
|
123
|
+
.description('Check required ports')
|
|
124
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
125
|
+
.action(async (options) => {
|
|
126
|
+
await checkPorts(options);
|
|
127
|
+
});
|
|
128
|
+
// vu check:install
|
|
129
|
+
check
|
|
130
|
+
.command('install')
|
|
131
|
+
.alias('pre')
|
|
132
|
+
.description('Pre-installation check (all requirements)')
|
|
133
|
+
.option('-h, --host <host>', 'SSH host name', 'cicore')
|
|
134
|
+
.action(async (options) => {
|
|
135
|
+
await preInstallCheck(options);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// ============================================
|
|
139
|
+
// RUN ALL CHECKS
|
|
140
|
+
// ============================================
|
|
141
|
+
async function runAllChecks(options) {
|
|
142
|
+
log.title('🔍 ciCore System Diagnostics');
|
|
143
|
+
log.blank();
|
|
144
|
+
const results = [];
|
|
145
|
+
if (!options.server) {
|
|
146
|
+
log.info('=== Local Environment ===');
|
|
147
|
+
const localResults = await runLocalChecks();
|
|
148
|
+
results.push(...localResults);
|
|
149
|
+
log.blank();
|
|
150
|
+
}
|
|
151
|
+
if (!options.local) {
|
|
152
|
+
log.info('=== Server Environment ===');
|
|
153
|
+
const serverResults = await runServerChecks(options.host);
|
|
154
|
+
results.push(...serverResults);
|
|
155
|
+
log.blank();
|
|
156
|
+
}
|
|
157
|
+
// Summary
|
|
158
|
+
printCheckSummary(results);
|
|
159
|
+
}
|
|
160
|
+
// ============================================
|
|
161
|
+
// LOCAL CHECKS
|
|
162
|
+
// ============================================
|
|
163
|
+
async function checkLocal() {
|
|
164
|
+
log.title('🖥️ Local Environment Check');
|
|
165
|
+
log.blank();
|
|
166
|
+
const results = await runLocalChecks();
|
|
167
|
+
printCheckSummary(results);
|
|
168
|
+
}
|
|
169
|
+
async function runLocalChecks() {
|
|
170
|
+
const results = [];
|
|
171
|
+
// Check Node.js
|
|
172
|
+
const nodeSpinner = spinner('Checking Node.js...').start();
|
|
173
|
+
const nodeResult = await exec('node', ['--version'], { silent: true });
|
|
174
|
+
if (nodeResult.success) {
|
|
175
|
+
const version = nodeResult.stdout.trim();
|
|
176
|
+
const major = parseInt(version.replace('v', '').split('.')[0]);
|
|
177
|
+
if (major >= 18) {
|
|
178
|
+
nodeSpinner.succeed(`Node.js: ${version}`);
|
|
179
|
+
results.push({ name: 'Node.js', status: 'pass', message: version });
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
nodeSpinner.warn(`Node.js: ${version} (recommend v18+)`);
|
|
183
|
+
results.push({ name: 'Node.js', status: 'warn', message: `${version} - recommend v18+` });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
nodeSpinner.fail('Node.js not found');
|
|
188
|
+
results.push({ name: 'Node.js', status: 'fail', message: 'Not installed' });
|
|
189
|
+
}
|
|
190
|
+
// Check npm
|
|
191
|
+
const npmSpinner = spinner('Checking npm...').start();
|
|
192
|
+
const npmResult = await exec('npm', ['--version'], { silent: true });
|
|
193
|
+
if (npmResult.success) {
|
|
194
|
+
npmSpinner.succeed(`npm: v${npmResult.stdout.trim()}`);
|
|
195
|
+
results.push({ name: 'npm', status: 'pass', message: `v${npmResult.stdout.trim()}` });
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
npmSpinner.fail('npm not found');
|
|
199
|
+
results.push({ name: 'npm', status: 'fail', message: 'Not installed' });
|
|
200
|
+
}
|
|
201
|
+
// Check Docker
|
|
202
|
+
const dockerSpinner = spinner('Checking Docker...').start();
|
|
203
|
+
const dockerResult = await exec('docker', ['--version'], { silent: true });
|
|
204
|
+
if (dockerResult.success) {
|
|
205
|
+
dockerSpinner.succeed(`Docker: ${dockerResult.stdout.trim().split(' ')[2]?.replace(',', '')}`);
|
|
206
|
+
results.push({ name: 'Docker', status: 'pass', message: 'Installed' });
|
|
207
|
+
// Check if Docker is running
|
|
208
|
+
const dockerPsResult = await exec('docker', ['ps'], { silent: true });
|
|
209
|
+
if (!dockerPsResult.success) {
|
|
210
|
+
results.push({ name: 'Docker Daemon', status: 'warn', message: 'Not running' });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
dockerSpinner.fail('Docker not found');
|
|
215
|
+
results.push({ name: 'Docker', status: 'fail', message: 'Not installed' });
|
|
216
|
+
}
|
|
217
|
+
// Check Git
|
|
218
|
+
const gitSpinner = spinner('Checking Git...').start();
|
|
219
|
+
const gitResult = await exec('git', ['--version'], { silent: true });
|
|
220
|
+
if (gitResult.success) {
|
|
221
|
+
gitSpinner.succeed(`Git: ${gitResult.stdout.trim().split(' ')[2]}`);
|
|
222
|
+
results.push({ name: 'Git', status: 'pass', message: 'Installed' });
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
gitSpinner.fail('Git not found');
|
|
226
|
+
results.push({ name: 'Git', status: 'fail', message: 'Not installed' });
|
|
227
|
+
}
|
|
228
|
+
// Check SSH config
|
|
229
|
+
const sshSpinner = spinner('Checking SSH config...').start();
|
|
230
|
+
const hosts = await listSSHHosts();
|
|
231
|
+
const vucoreHost = hosts.find(h => h.name === 'cicore');
|
|
232
|
+
if (vucoreHost) {
|
|
233
|
+
sshSpinner.succeed(`SSH: vucore host configured (${vucoreHost.hostname})`);
|
|
234
|
+
results.push({ name: 'SSH Config', status: 'pass', message: `vucore → ${vucoreHost.hostname}` });
|
|
235
|
+
}
|
|
236
|
+
else if (hosts.length > 0) {
|
|
237
|
+
sshSpinner.warn(`SSH: ${hosts.length} hosts found, but no 'cicore' host`);
|
|
238
|
+
results.push({ name: 'SSH Config', status: 'warn', message: 'No vucore host' });
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
sshSpinner.fail('SSH: No hosts configured');
|
|
242
|
+
results.push({ name: 'SSH Config', status: 'fail', message: 'No hosts in ~/.ssh/config' });
|
|
243
|
+
}
|
|
244
|
+
// Check ciCore project structure
|
|
245
|
+
const projectSpinner = spinner('Checking project structure...').start();
|
|
246
|
+
const requiredFiles = [
|
|
247
|
+
'package.json',
|
|
248
|
+
'nuxt.config.ts',
|
|
249
|
+
'docker-compose.dev.yml',
|
|
250
|
+
'Dockerfile.nuxt.production',
|
|
251
|
+
'Dockerfile.php.production',
|
|
252
|
+
'addons',
|
|
253
|
+
];
|
|
254
|
+
const missingFiles = [];
|
|
255
|
+
for (const file of requiredFiles) {
|
|
256
|
+
if (!await fs.pathExists(path.join(paths.dev.root, file))) {
|
|
257
|
+
missingFiles.push(file);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (missingFiles.length === 0) {
|
|
261
|
+
projectSpinner.succeed('Project structure: OK');
|
|
262
|
+
results.push({ name: 'Project Structure', status: 'pass', message: 'All files present' });
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
projectSpinner.warn(`Project structure: Missing ${missingFiles.length} files`);
|
|
266
|
+
results.push({
|
|
267
|
+
name: 'Project Structure',
|
|
268
|
+
status: 'warn',
|
|
269
|
+
message: `Missing: ${missingFiles.join(', ')}`
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Check local containers
|
|
273
|
+
const containerSpinner = spinner('Checking local containers...').start();
|
|
274
|
+
const psResult = await exec('docker', ['ps', '--filter', 'name=vucore_dev', '--format', '{{.Names}}'], { silent: true });
|
|
275
|
+
if (psResult.success && psResult.stdout.trim()) {
|
|
276
|
+
const containers = psResult.stdout.trim().split('\n').length;
|
|
277
|
+
containerSpinner.succeed(`Local containers: ${containers} running`);
|
|
278
|
+
results.push({ name: 'Local Containers', status: 'pass', message: `${containers} running` });
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
containerSpinner.warn('Local containers: None running');
|
|
282
|
+
results.push({ name: 'Local Containers', status: 'warn', message: 'None running' });
|
|
283
|
+
}
|
|
284
|
+
return results;
|
|
285
|
+
}
|
|
286
|
+
// ============================================
|
|
287
|
+
// SERVER CHECKS
|
|
288
|
+
// ============================================
|
|
289
|
+
async function checkServer(options) {
|
|
290
|
+
log.title('🌐 Server Health Check');
|
|
291
|
+
log.blank();
|
|
292
|
+
const results = await runServerChecks(options.host);
|
|
293
|
+
printCheckSummary(results);
|
|
294
|
+
}
|
|
295
|
+
async function runServerChecks(hostName) {
|
|
296
|
+
const results = [];
|
|
297
|
+
const host = await getSSHHost(hostName);
|
|
298
|
+
if (!host) {
|
|
299
|
+
results.push({ name: 'SSH Connection', status: 'fail', message: `Host "${hostName}" not found` });
|
|
300
|
+
return results;
|
|
301
|
+
}
|
|
302
|
+
// Test SSH connection
|
|
303
|
+
const sshSpinner = spinner(`Connecting to ${host.hostname}...`).start();
|
|
304
|
+
const sshResult = await runSSH(host, 'echo "OK"');
|
|
305
|
+
if (!sshResult.success) {
|
|
306
|
+
sshSpinner.fail('SSH connection failed');
|
|
307
|
+
results.push({ name: 'SSH Connection', status: 'fail', message: 'Connection failed' });
|
|
308
|
+
return results;
|
|
309
|
+
}
|
|
310
|
+
sshSpinner.succeed(`Connected to ${host.hostname}`);
|
|
311
|
+
results.push({ name: 'SSH Connection', status: 'pass', message: host.hostname });
|
|
312
|
+
// Check Docker
|
|
313
|
+
const dockerSpinner = spinner('Checking Docker...').start();
|
|
314
|
+
const dockerResult = await runSSH(host, 'docker --version');
|
|
315
|
+
if (dockerResult.success) {
|
|
316
|
+
dockerSpinner.succeed('Docker: Installed');
|
|
317
|
+
results.push({ name: 'Docker', status: 'pass', message: 'Installed' });
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
dockerSpinner.fail('Docker: Not installed');
|
|
321
|
+
results.push({ name: 'Docker', status: 'fail', message: 'Not installed' });
|
|
322
|
+
}
|
|
323
|
+
// Check containers
|
|
324
|
+
const containerSpinner = spinner('Checking containers...').start();
|
|
325
|
+
const psResult = await runSSH(host, 'docker ps --format "{{.Names}}:{{.Status}}" | grep vucore');
|
|
326
|
+
if (psResult.success && psResult.stdout.trim()) {
|
|
327
|
+
const containers = psResult.stdout.trim().split('\n');
|
|
328
|
+
const running = containers.filter(c => c.includes('Up')).length;
|
|
329
|
+
const total = containers.length;
|
|
330
|
+
if (running === total) {
|
|
331
|
+
containerSpinner.succeed(`Containers: ${running}/${total} running`);
|
|
332
|
+
results.push({ name: 'Containers', status: 'pass', message: `${running}/${total} running` });
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
containerSpinner.warn(`Containers: ${running}/${total} running`);
|
|
336
|
+
results.push({ name: 'Containers', status: 'warn', message: `${running}/${total} running` });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
containerSpinner.fail('Containers: None found');
|
|
341
|
+
results.push({ name: 'Containers', status: 'fail', message: 'No ciCore containers' });
|
|
342
|
+
}
|
|
343
|
+
// Check disk space
|
|
344
|
+
const diskSpinner = spinner('Checking disk space...').start();
|
|
345
|
+
const diskResult = await runSSH(host, "df -h / | tail -1 | awk '{print $5}'");
|
|
346
|
+
if (diskResult.success) {
|
|
347
|
+
const usage = parseInt(diskResult.stdout.trim().replace('%', ''));
|
|
348
|
+
if (usage < 80) {
|
|
349
|
+
diskSpinner.succeed(`Disk: ${usage}% used`);
|
|
350
|
+
results.push({ name: 'Disk Space', status: 'pass', message: `${usage}% used` });
|
|
351
|
+
}
|
|
352
|
+
else if (usage < 90) {
|
|
353
|
+
diskSpinner.warn(`Disk: ${usage}% used`);
|
|
354
|
+
results.push({ name: 'Disk Space', status: 'warn', message: `${usage}% used - consider cleanup` });
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
diskSpinner.fail(`Disk: ${usage}% used`);
|
|
358
|
+
results.push({ name: 'Disk Space', status: 'fail', message: `${usage}% used - CRITICAL` });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Check memory
|
|
362
|
+
const memSpinner = spinner('Checking memory...').start();
|
|
363
|
+
const memResult = await runSSH(host, "free | grep Mem | awk '{printf \"%.0f\", $3/$2 * 100}'");
|
|
364
|
+
if (memResult.success) {
|
|
365
|
+
const usage = parseInt(memResult.stdout.trim());
|
|
366
|
+
if (usage < 80) {
|
|
367
|
+
memSpinner.succeed(`Memory: ${usage}% used`);
|
|
368
|
+
results.push({ name: 'Memory', status: 'pass', message: `${usage}% used` });
|
|
369
|
+
}
|
|
370
|
+
else if (usage < 90) {
|
|
371
|
+
memSpinner.warn(`Memory: ${usage}% used`);
|
|
372
|
+
results.push({ name: 'Memory', status: 'warn', message: `${usage}% used` });
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
memSpinner.fail(`Memory: ${usage}% used`);
|
|
376
|
+
results.push({ name: 'Memory', status: 'fail', message: `${usage}% used - CRITICAL` });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Check Nginx
|
|
380
|
+
const nginxSpinner = spinner('Checking Nginx...').start();
|
|
381
|
+
const nginxResult = await runSSH(host, 'systemctl is-active nginx');
|
|
382
|
+
if (nginxResult.success && nginxResult.stdout.trim() === 'active') {
|
|
383
|
+
nginxSpinner.succeed('Nginx: Active');
|
|
384
|
+
results.push({ name: 'Nginx', status: 'pass', message: 'Active' });
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
nginxSpinner.fail('Nginx: Not active');
|
|
388
|
+
results.push({ name: 'Nginx', status: 'fail', message: 'Not active' });
|
|
389
|
+
}
|
|
390
|
+
// Check API health
|
|
391
|
+
const apiSpinner = spinner('Checking API health...').start();
|
|
392
|
+
const apiResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/health 2>/dev/null || echo "000"');
|
|
393
|
+
const statusCode = apiResult.stdout.trim();
|
|
394
|
+
if (statusCode === '200') {
|
|
395
|
+
apiSpinner.succeed('API: Healthy');
|
|
396
|
+
results.push({ name: 'API Health', status: 'pass', message: 'HTTP 200' });
|
|
397
|
+
}
|
|
398
|
+
else if (statusCode === '000') {
|
|
399
|
+
apiSpinner.fail('API: Not responding');
|
|
400
|
+
results.push({ name: 'API Health', status: 'fail', message: 'Not responding' });
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
apiSpinner.warn(`API: HTTP ${statusCode}`);
|
|
404
|
+
results.push({ name: 'API Health', status: 'warn', message: `HTTP ${statusCode}` });
|
|
405
|
+
}
|
|
406
|
+
return results;
|
|
407
|
+
}
|
|
408
|
+
// ============================================
|
|
409
|
+
// DOCKER CHECKS
|
|
410
|
+
// ============================================
|
|
411
|
+
async function checkDocker(options) {
|
|
412
|
+
log.title('🐳 Docker Check');
|
|
413
|
+
log.blank();
|
|
414
|
+
const results = [];
|
|
415
|
+
const registry = getDockerRegistry();
|
|
416
|
+
if (options.host === 'local') {
|
|
417
|
+
// Local Docker check
|
|
418
|
+
const imagesSpinner = spinner('Checking local images...').start();
|
|
419
|
+
const imagesResult = await exec('docker', ['images', '--format', '{{.Repository}}:{{.Tag}}\t{{.Size}}', '--filter', `reference=${registry}/vucore-*`], { silent: true });
|
|
420
|
+
if (imagesResult.success && imagesResult.stdout.trim()) {
|
|
421
|
+
const images = imagesResult.stdout.trim().split('\n');
|
|
422
|
+
imagesSpinner.succeed(`Found ${images.length} ciCore images`);
|
|
423
|
+
log.blank();
|
|
424
|
+
images.forEach(img => {
|
|
425
|
+
const [name, size] = img.split('\t');
|
|
426
|
+
log.item(`${name} (${size})`);
|
|
427
|
+
});
|
|
428
|
+
results.push({ name: 'Local Images', status: 'pass', message: `${images.length} images` });
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
imagesSpinner.warn('No ciCore images found');
|
|
432
|
+
results.push({ name: 'Local Images', status: 'warn', message: 'None found' });
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
// Remote Docker check
|
|
437
|
+
const host = await getSSHHost(options.host);
|
|
438
|
+
if (!host) {
|
|
439
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const imagesSpinner = spinner('Checking server images...').start();
|
|
443
|
+
const imagesResult = await runSSH(host, `docker images --format "{{.Repository}}:{{.Tag}}\t{{.Size}}" | grep vucore`);
|
|
444
|
+
if (imagesResult.success && imagesResult.stdout.trim()) {
|
|
445
|
+
const images = imagesResult.stdout.trim().split('\n');
|
|
446
|
+
imagesSpinner.succeed(`Found ${images.length} ciCore images`);
|
|
447
|
+
log.blank();
|
|
448
|
+
images.forEach(img => {
|
|
449
|
+
const [name, size] = img.split('\t');
|
|
450
|
+
log.item(`${name} (${size})`);
|
|
451
|
+
});
|
|
452
|
+
results.push({ name: 'Server Images', status: 'pass', message: `${images.length} images` });
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
imagesSpinner.warn('No ciCore images found');
|
|
456
|
+
results.push({ name: 'Server Images', status: 'warn', message: 'None found' });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
log.blank();
|
|
460
|
+
printCheckSummary(results);
|
|
461
|
+
}
|
|
462
|
+
// ============================================
|
|
463
|
+
// FILE STRUCTURE CHECKS
|
|
464
|
+
// ============================================
|
|
465
|
+
async function checkFiles(options) {
|
|
466
|
+
log.title('📁 File Structure Check');
|
|
467
|
+
log.blank();
|
|
468
|
+
const results = [];
|
|
469
|
+
const host = await getSSHHost(options.host);
|
|
470
|
+
if (!host) {
|
|
471
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const coreName = options.core;
|
|
475
|
+
const requiredDirs = [
|
|
476
|
+
`/home/cores/${coreName}`,
|
|
477
|
+
`/home/cores/${coreName}/addons`,
|
|
478
|
+
`/home/cores/${coreName}/storage`,
|
|
479
|
+
'/home/services',
|
|
480
|
+
'/home/scripts',
|
|
481
|
+
'/home/nginx/conf.d',
|
|
482
|
+
];
|
|
483
|
+
const requiredFiles = [
|
|
484
|
+
`/home/cores/${coreName}/.env`,
|
|
485
|
+
'/home/services/docker-core-nuxt.yml',
|
|
486
|
+
'/home/services/docker-core-php.yml',
|
|
487
|
+
'/home/scripts/cleanup-and-rebuild-nuxt.sh',
|
|
488
|
+
'/home/scripts/cleanup-and-rebuild-php.sh',
|
|
489
|
+
];
|
|
490
|
+
// Check directories
|
|
491
|
+
for (const dir of requiredDirs) {
|
|
492
|
+
const checkSpinner = spinner(`Checking ${dir}...`).start();
|
|
493
|
+
const result = await runSSH(host, `test -d "${dir}" && echo "OK" || echo "MISSING"`);
|
|
494
|
+
if (result.stdout.trim() === 'OK') {
|
|
495
|
+
checkSpinner.succeed(`${dir}: OK`);
|
|
496
|
+
results.push({ name: dir, status: 'pass', message: 'Exists' });
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
checkSpinner.fail(`${dir}: MISSING`);
|
|
500
|
+
results.push({ name: dir, status: 'fail', message: 'Missing' });
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// Check files
|
|
504
|
+
for (const file of requiredFiles) {
|
|
505
|
+
const checkSpinner = spinner(`Checking ${file}...`).start();
|
|
506
|
+
const result = await runSSH(host, `test -f "${file}" && echo "OK" || echo "MISSING"`);
|
|
507
|
+
if (result.stdout.trim() === 'OK') {
|
|
508
|
+
checkSpinner.succeed(`${file}: OK`);
|
|
509
|
+
results.push({ name: file, status: 'pass', message: 'Exists' });
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
checkSpinner.fail(`${file}: MISSING`);
|
|
513
|
+
results.push({ name: file, status: 'fail', message: 'Missing' });
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
log.blank();
|
|
517
|
+
printCheckSummary(results);
|
|
518
|
+
}
|
|
519
|
+
// ============================================
|
|
520
|
+
// ADDON CHECKS
|
|
521
|
+
// ============================================
|
|
522
|
+
async function checkAddons(options) {
|
|
523
|
+
log.title('🧩 Addon Validation');
|
|
524
|
+
log.blank();
|
|
525
|
+
const results = [];
|
|
526
|
+
if (options.host === 'local') {
|
|
527
|
+
// Local addon check
|
|
528
|
+
const addonsPath = paths.dev.addons;
|
|
529
|
+
if (!await fs.pathExists(addonsPath)) {
|
|
530
|
+
log.error('Addons directory not found');
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const entries = await fs.readdir(addonsPath, { withFileTypes: true });
|
|
534
|
+
const addons = entries.filter(e => e.isDirectory() && !e.name.startsWith('.'));
|
|
535
|
+
for (const addon of addons) {
|
|
536
|
+
const addonPath = path.join(addonsPath, addon.name);
|
|
537
|
+
const addonSpinner = spinner(`Checking ${addon.name}...`).start();
|
|
538
|
+
const issues = [];
|
|
539
|
+
// Check addon.json
|
|
540
|
+
const addonJsonPath = path.join(addonPath, 'addon.json');
|
|
541
|
+
if (!await fs.pathExists(addonJsonPath)) {
|
|
542
|
+
issues.push('Missing addon.json');
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
try {
|
|
546
|
+
const config = await fs.readJson(addonJsonPath);
|
|
547
|
+
if (!config.name)
|
|
548
|
+
issues.push('addon.json: missing name');
|
|
549
|
+
if (!config.version)
|
|
550
|
+
issues.push('addon.json: missing version');
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
issues.push('addon.json: invalid JSON');
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// Check Backend
|
|
557
|
+
if (!await fs.pathExists(path.join(addonPath, 'Backend'))) {
|
|
558
|
+
issues.push('Missing Backend/');
|
|
559
|
+
}
|
|
560
|
+
// Check entry point
|
|
561
|
+
const hasEntry = await fs.pathExists(path.join(addonPath, 'ui', 'entry.js')) ||
|
|
562
|
+
await fs.pathExists(path.join(addonPath, 'ui', 'entry.ts'));
|
|
563
|
+
if (!hasEntry) {
|
|
564
|
+
issues.push('Missing ui/entry.js or entry.ts');
|
|
565
|
+
}
|
|
566
|
+
if (issues.length === 0) {
|
|
567
|
+
addonSpinner.succeed(`${addon.name}: Valid`);
|
|
568
|
+
results.push({ name: addon.name, status: 'pass', message: 'Valid' });
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
addonSpinner.warn(`${addon.name}: ${issues.length} issue(s)`);
|
|
572
|
+
results.push({ name: addon.name, status: 'warn', message: issues.join(', ') });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
// Server addon check
|
|
578
|
+
const host = await getSSHHost(options.host);
|
|
579
|
+
if (!host) {
|
|
580
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const addonsResult = await runSSH(host, `ls -1 /home/cores/${options.core}/addons/ 2>/dev/null`);
|
|
584
|
+
if (!addonsResult.success || !addonsResult.stdout.trim()) {
|
|
585
|
+
log.info('No addons found on server');
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const addons = addonsResult.stdout.trim().split('\n');
|
|
589
|
+
for (const addon of addons) {
|
|
590
|
+
const addonSpinner = spinner(`Checking ${addon}...`).start();
|
|
591
|
+
const addonPath = `/home/cores/${options.core}/addons/${addon}`;
|
|
592
|
+
const issues = [];
|
|
593
|
+
// Check addon.json
|
|
594
|
+
const jsonResult = await runSSH(host, `test -f "${addonPath}/addon.json" && echo "OK"`);
|
|
595
|
+
if (jsonResult.stdout.trim() !== 'OK') {
|
|
596
|
+
issues.push('Missing addon.json');
|
|
597
|
+
}
|
|
598
|
+
// Check ui-manifest.json
|
|
599
|
+
const manifestResult = await runSSH(host, `test -f "${addonPath}/ui-manifest.json" && echo "OK"`);
|
|
600
|
+
if (manifestResult.stdout.trim() !== 'OK') {
|
|
601
|
+
issues.push('Missing ui-manifest.json');
|
|
602
|
+
}
|
|
603
|
+
// Check Backend
|
|
604
|
+
const backendResult = await runSSH(host, `test -d "${addonPath}/Backend" && echo "OK"`);
|
|
605
|
+
if (backendResult.stdout.trim() !== 'OK') {
|
|
606
|
+
issues.push('Missing Backend/');
|
|
607
|
+
}
|
|
608
|
+
if (issues.length === 0) {
|
|
609
|
+
addonSpinner.succeed(`${addon}: Valid`);
|
|
610
|
+
results.push({ name: addon, status: 'pass', message: 'Valid' });
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
addonSpinner.warn(`${addon}: ${issues.length} issue(s)`);
|
|
614
|
+
results.push({ name: addon, status: 'warn', message: issues.join(', ') });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
log.blank();
|
|
619
|
+
printCheckSummary(results);
|
|
620
|
+
}
|
|
621
|
+
// ============================================
|
|
622
|
+
// SSL CHECKS
|
|
623
|
+
// ============================================
|
|
624
|
+
async function checkSSL(options) {
|
|
625
|
+
log.title('🔒 SSL Certificate Check');
|
|
626
|
+
log.blank();
|
|
627
|
+
const host = await getSSHHost(options.host);
|
|
628
|
+
if (!host) {
|
|
629
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const results = [];
|
|
633
|
+
// List certificates
|
|
634
|
+
const certSpinner = spinner('Checking certificates...').start();
|
|
635
|
+
const certResult = await runSSH(host, 'certbot certificates 2>/dev/null | grep -E "(Certificate Name|Expiry Date|Domains)"');
|
|
636
|
+
if (certResult.success && certResult.stdout.trim()) {
|
|
637
|
+
certSpinner.succeed('Certificates found');
|
|
638
|
+
log.blank();
|
|
639
|
+
console.log(certResult.stdout);
|
|
640
|
+
results.push({ name: 'SSL Certificates', status: 'pass', message: 'Found' });
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
certSpinner.warn('No certificates found');
|
|
644
|
+
results.push({ name: 'SSL Certificates', status: 'warn', message: 'None found' });
|
|
645
|
+
}
|
|
646
|
+
// Check specific domain if provided
|
|
647
|
+
if (options.domain) {
|
|
648
|
+
const domainSpinner = spinner(`Checking ${options.domain}...`).start();
|
|
649
|
+
const domainResult = await runSSH(host, `openssl s_client -connect ${options.domain}:443 -servername ${options.domain} </dev/null 2>/dev/null | openssl x509 -noout -dates 2>/dev/null`);
|
|
650
|
+
if (domainResult.success && domainResult.stdout.trim()) {
|
|
651
|
+
domainSpinner.succeed(`${options.domain}: Valid`);
|
|
652
|
+
log.blank();
|
|
653
|
+
console.log(domainResult.stdout);
|
|
654
|
+
results.push({ name: options.domain, status: 'pass', message: 'Valid' });
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
domainSpinner.fail(`${options.domain}: Invalid or not found`);
|
|
658
|
+
results.push({ name: options.domain, status: 'fail', message: 'Invalid' });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
log.blank();
|
|
662
|
+
printCheckSummary(results);
|
|
663
|
+
}
|
|
664
|
+
// ============================================
|
|
665
|
+
// CONNECTIVITY CHECKS
|
|
666
|
+
// ============================================
|
|
667
|
+
async function checkConnectivity(options) {
|
|
668
|
+
log.title('🌐 Connectivity Check');
|
|
669
|
+
log.blank();
|
|
670
|
+
const host = await getSSHHost(options.host);
|
|
671
|
+
if (!host) {
|
|
672
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
const results = [];
|
|
676
|
+
// Check internet
|
|
677
|
+
const internetSpinner = spinner('Checking internet...').start();
|
|
678
|
+
const internetResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" https://google.com');
|
|
679
|
+
if (internetResult.stdout.trim() === '200' || internetResult.stdout.trim() === '301') {
|
|
680
|
+
internetSpinner.succeed('Internet: Connected');
|
|
681
|
+
results.push({ name: 'Internet', status: 'pass', message: 'Connected' });
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
internetSpinner.fail('Internet: Not connected');
|
|
685
|
+
results.push({ name: 'Internet', status: 'fail', message: 'Not connected' });
|
|
686
|
+
}
|
|
687
|
+
// Check Docker Hub
|
|
688
|
+
const dockerHubSpinner = spinner('Checking Docker Hub...').start();
|
|
689
|
+
const dockerHubResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" https://hub.docker.com');
|
|
690
|
+
if (dockerHubResult.stdout.trim() === '200') {
|
|
691
|
+
dockerHubSpinner.succeed('Docker Hub: Accessible');
|
|
692
|
+
results.push({ name: 'Docker Hub', status: 'pass', message: 'Accessible' });
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
dockerHubSpinner.fail('Docker Hub: Not accessible');
|
|
696
|
+
results.push({ name: 'Docker Hub', status: 'fail', message: 'Not accessible' });
|
|
697
|
+
}
|
|
698
|
+
// Check CDN
|
|
699
|
+
const cdnSpinner = spinner('Checking CDN...').start();
|
|
700
|
+
const cdnResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" https://cdn.cicore.com.tr');
|
|
701
|
+
if (cdnResult.stdout.trim() === '200') {
|
|
702
|
+
cdnSpinner.succeed('CDN: Accessible');
|
|
703
|
+
results.push({ name: 'CDN', status: 'pass', message: 'Accessible' });
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
cdnSpinner.warn('CDN: Not accessible');
|
|
707
|
+
results.push({ name: 'CDN', status: 'warn', message: 'Not accessible' });
|
|
708
|
+
}
|
|
709
|
+
log.blank();
|
|
710
|
+
printCheckSummary(results);
|
|
711
|
+
}
|
|
712
|
+
// ============================================
|
|
713
|
+
// HELPER FUNCTIONS
|
|
714
|
+
// ============================================
|
|
715
|
+
async function runSSH(host, command) {
|
|
716
|
+
const args = buildSSHCommand(host, command);
|
|
717
|
+
return await exec('ssh', args, { silent: true });
|
|
718
|
+
}
|
|
719
|
+
function printCheckSummary(results) {
|
|
720
|
+
const passed = results.filter(r => r.status === 'pass').length;
|
|
721
|
+
const warned = results.filter(r => r.status === 'warn').length;
|
|
722
|
+
const failed = results.filter(r => r.status === 'fail').length;
|
|
723
|
+
log.blank();
|
|
724
|
+
log.info('=== Summary ===');
|
|
725
|
+
if (failed > 0) {
|
|
726
|
+
log.error(`❌ ${failed} failed`);
|
|
727
|
+
}
|
|
728
|
+
if (warned > 0) {
|
|
729
|
+
log.warn(`⚠️ ${warned} warnings`);
|
|
730
|
+
}
|
|
731
|
+
if (passed > 0) {
|
|
732
|
+
log.success(`✅ ${passed} passed`);
|
|
733
|
+
}
|
|
734
|
+
log.blank();
|
|
735
|
+
if (failed > 0) {
|
|
736
|
+
log.info('Failed checks:');
|
|
737
|
+
results.filter(r => r.status === 'fail').forEach(r => {
|
|
738
|
+
log.item(`${r.name}: ${r.message}`);
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
if (warned > 0) {
|
|
742
|
+
log.info('Warnings:');
|
|
743
|
+
results.filter(r => r.status === 'warn').forEach(r => {
|
|
744
|
+
log.item(`${r.name}: ${r.message}`);
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
// ============================================
|
|
749
|
+
// SERVER REQUIREMENTS CHECK
|
|
750
|
+
// ============================================
|
|
751
|
+
async function checkRequirements(options) {
|
|
752
|
+
log.title('📋 Server Requirements Check');
|
|
753
|
+
log.blank();
|
|
754
|
+
const host = await getSSHHost(options.host);
|
|
755
|
+
if (!host) {
|
|
756
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
log.kv('Server', `${host.name} (${host.hostname})`);
|
|
760
|
+
log.blank();
|
|
761
|
+
const results = [];
|
|
762
|
+
// Required programs with minimum versions
|
|
763
|
+
const programs = [
|
|
764
|
+
{ name: 'docker', cmd: 'docker --version', minVersion: '20.0.0', required: true },
|
|
765
|
+
{ name: 'docker-compose', cmd: 'docker compose version 2>/dev/null || docker-compose --version', minVersion: '2.0.0', required: true },
|
|
766
|
+
{ name: 'curl', cmd: 'curl --version | head -1', minVersion: null, required: true },
|
|
767
|
+
{ name: 'tree', cmd: 'tree --version 2>/dev/null || echo "not installed"', minVersion: null, required: false },
|
|
768
|
+
{ name: 'git', cmd: 'git --version', minVersion: null, required: false },
|
|
769
|
+
{ name: 'openssl', cmd: 'openssl version', minVersion: null, required: true },
|
|
770
|
+
{ name: 'certbot', cmd: 'certbot --version 2>/dev/null || echo "not installed"', minVersion: null, required: false },
|
|
771
|
+
];
|
|
772
|
+
log.info('=== Required Programs ===');
|
|
773
|
+
for (const prog of programs) {
|
|
774
|
+
const progSpinner = spinner(`Checking ${prog.name}...`).start();
|
|
775
|
+
const result = await runSSH(host, prog.cmd);
|
|
776
|
+
if (result.success && !result.stdout.includes('not installed') && !result.stdout.includes('not found')) {
|
|
777
|
+
const version = result.stdout.trim().split('\n')[0];
|
|
778
|
+
progSpinner.succeed(`${prog.name}: ${version}`);
|
|
779
|
+
results.push({ name: prog.name, status: 'pass', message: version });
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
if (prog.required) {
|
|
783
|
+
progSpinner.fail(`${prog.name}: NOT INSTALLED (required)`);
|
|
784
|
+
results.push({ name: prog.name, status: 'fail', message: 'Not installed (required)' });
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
progSpinner.warn(`${prog.name}: not installed (optional)`);
|
|
788
|
+
results.push({ name: prog.name, status: 'warn', message: 'Not installed (optional)' });
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
log.blank();
|
|
793
|
+
log.info('=== System Resources ===');
|
|
794
|
+
// Check CPU
|
|
795
|
+
const cpuSpinner = spinner('Checking CPU...').start();
|
|
796
|
+
const cpuResult = await runSSH(host, 'nproc');
|
|
797
|
+
if (cpuResult.success) {
|
|
798
|
+
const cores = parseInt(cpuResult.stdout.trim());
|
|
799
|
+
if (cores >= 2) {
|
|
800
|
+
cpuSpinner.succeed(`CPU: ${cores} cores`);
|
|
801
|
+
results.push({ name: 'CPU', status: 'pass', message: `${cores} cores` });
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
cpuSpinner.warn(`CPU: ${cores} core (recommend 2+)`);
|
|
805
|
+
results.push({ name: 'CPU', status: 'warn', message: `${cores} core - recommend 2+` });
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
// Check RAM
|
|
809
|
+
const ramSpinner = spinner('Checking RAM...').start();
|
|
810
|
+
const ramResult = await runSSH(host, "free -g | grep Mem | awk '{print $2}'");
|
|
811
|
+
if (ramResult.success) {
|
|
812
|
+
const gb = parseInt(ramResult.stdout.trim());
|
|
813
|
+
if (gb >= 4) {
|
|
814
|
+
ramSpinner.succeed(`RAM: ${gb}GB`);
|
|
815
|
+
results.push({ name: 'RAM', status: 'pass', message: `${gb}GB` });
|
|
816
|
+
}
|
|
817
|
+
else if (gb >= 2) {
|
|
818
|
+
ramSpinner.warn(`RAM: ${gb}GB (recommend 4GB+)`);
|
|
819
|
+
results.push({ name: 'RAM', status: 'warn', message: `${gb}GB - recommend 4GB+` });
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
ramSpinner.fail(`RAM: ${gb}GB (minimum 2GB required)`);
|
|
823
|
+
results.push({ name: 'RAM', status: 'fail', message: `${gb}GB - minimum 2GB required` });
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// Check Disk
|
|
827
|
+
const diskSpinner = spinner('Checking disk space...').start();
|
|
828
|
+
const diskResult = await runSSH(host, "df -BG / | tail -1 | awk '{print $4}' | tr -d 'G'");
|
|
829
|
+
if (diskResult.success) {
|
|
830
|
+
const freeGB = parseInt(diskResult.stdout.trim());
|
|
831
|
+
if (freeGB >= 20) {
|
|
832
|
+
diskSpinner.succeed(`Disk: ${freeGB}GB free`);
|
|
833
|
+
results.push({ name: 'Disk', status: 'pass', message: `${freeGB}GB free` });
|
|
834
|
+
}
|
|
835
|
+
else if (freeGB >= 10) {
|
|
836
|
+
diskSpinner.warn(`Disk: ${freeGB}GB free (recommend 20GB+)`);
|
|
837
|
+
results.push({ name: 'Disk', status: 'warn', message: `${freeGB}GB free - recommend 20GB+` });
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
diskSpinner.fail(`Disk: ${freeGB}GB free (minimum 10GB required)`);
|
|
841
|
+
results.push({ name: 'Disk', status: 'fail', message: `${freeGB}GB free - minimum 10GB required` });
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
// Check OS
|
|
845
|
+
const osSpinner = spinner('Checking OS...').start();
|
|
846
|
+
const osResult = await runSSH(host, 'cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d \'"\'');
|
|
847
|
+
if (osResult.success) {
|
|
848
|
+
osSpinner.succeed(`OS: ${osResult.stdout.trim()}`);
|
|
849
|
+
results.push({ name: 'OS', status: 'pass', message: osResult.stdout.trim() });
|
|
850
|
+
}
|
|
851
|
+
log.blank();
|
|
852
|
+
log.info('=== Docker Status ===');
|
|
853
|
+
// Check Docker daemon
|
|
854
|
+
const dockerDaemonSpinner = spinner('Checking Docker daemon...').start();
|
|
855
|
+
const dockerDaemonResult = await runSSH(host, 'docker info >/dev/null 2>&1 && echo "running" || echo "not running"');
|
|
856
|
+
if (dockerDaemonResult.stdout.trim() === 'running') {
|
|
857
|
+
dockerDaemonSpinner.succeed('Docker daemon: Running');
|
|
858
|
+
results.push({ name: 'Docker Daemon', status: 'pass', message: 'Running' });
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
dockerDaemonSpinner.fail('Docker daemon: Not running');
|
|
862
|
+
results.push({ name: 'Docker Daemon', status: 'fail', message: 'Not running' });
|
|
863
|
+
}
|
|
864
|
+
// Check Docker networks
|
|
865
|
+
const networkSpinner = spinner('Checking Docker networks...').start();
|
|
866
|
+
const networkResult = await runSSH(host, 'docker network ls --format "{{.Name}}" | grep -E "vucore" || echo "none"');
|
|
867
|
+
if (networkResult.stdout.trim() !== 'none') {
|
|
868
|
+
const networks = networkResult.stdout.trim().split('\n');
|
|
869
|
+
networkSpinner.succeed(`Docker networks: ${networks.length} ciCore networks`);
|
|
870
|
+
results.push({ name: 'Docker Networks', status: 'pass', message: networks.join(', ') });
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
networkSpinner.warn('Docker networks: No ciCore networks found');
|
|
874
|
+
results.push({ name: 'Docker Networks', status: 'warn', message: 'None found - will be created during install' });
|
|
875
|
+
}
|
|
876
|
+
log.blank();
|
|
877
|
+
printCheckSummary(results);
|
|
878
|
+
// Print installation commands if needed
|
|
879
|
+
const failed = results.filter(r => r.status === 'fail');
|
|
880
|
+
if (failed.length > 0) {
|
|
881
|
+
log.blank();
|
|
882
|
+
log.info('=== Installation Commands ===');
|
|
883
|
+
if (failed.some(r => r.name === 'docker')) {
|
|
884
|
+
log.item('Docker: curl -fsSL https://get.docker.com | sh');
|
|
885
|
+
}
|
|
886
|
+
if (failed.some(r => r.name === 'tree')) {
|
|
887
|
+
log.item('Tree: apt-get install -y tree');
|
|
888
|
+
}
|
|
889
|
+
if (failed.some(r => r.name === 'certbot')) {
|
|
890
|
+
log.item('Certbot: apt-get install -y certbot');
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
// ============================================
|
|
895
|
+
// SHOW TREE
|
|
896
|
+
// ============================================
|
|
897
|
+
async function showTree(options) {
|
|
898
|
+
log.title('🌳 Server Directory Structure');
|
|
899
|
+
log.blank();
|
|
900
|
+
const host = await getSSHHost(options.host);
|
|
901
|
+
if (!host) {
|
|
902
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const depth = options.depth || '3';
|
|
906
|
+
// Check if tree is installed
|
|
907
|
+
const treeCheck = await runSSH(host, 'which tree 2>/dev/null || echo "not found"');
|
|
908
|
+
const hasTree = !treeCheck.stdout.includes('not found');
|
|
909
|
+
if (options.core) {
|
|
910
|
+
// Show specific core structure
|
|
911
|
+
log.info(`Core: ${options.core}`);
|
|
912
|
+
log.blank();
|
|
913
|
+
const corePath = `/home/cores/${options.core}`;
|
|
914
|
+
if (hasTree) {
|
|
915
|
+
const result = await runSSH(host, `tree -L ${depth} --dirsfirst ${corePath} 2>/dev/null || echo "Directory not found"`);
|
|
916
|
+
console.log(result.stdout);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
const result = await runSSH(host, `find ${corePath} -maxdepth ${depth} -type d 2>/dev/null | head -50`);
|
|
920
|
+
console.log(result.stdout);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
// Show full /home structure
|
|
925
|
+
log.info('Full /home structure:');
|
|
926
|
+
log.blank();
|
|
927
|
+
if (hasTree) {
|
|
928
|
+
const result = await runSSH(host, `tree -L ${depth} --dirsfirst /home 2>/dev/null || echo "Directory not found"`);
|
|
929
|
+
console.log(result.stdout);
|
|
930
|
+
}
|
|
931
|
+
else {
|
|
932
|
+
log.warn('tree command not installed. Using find instead...');
|
|
933
|
+
log.info('Install tree: apt-get install -y tree');
|
|
934
|
+
log.blank();
|
|
935
|
+
const result = await runSSH(host, `find /home -maxdepth ${depth} -type d 2>/dev/null | head -100`);
|
|
936
|
+
console.log(result.stdout);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
// Show expected structure
|
|
940
|
+
log.blank();
|
|
941
|
+
log.info('=== Expected ciCore Structure ===');
|
|
942
|
+
console.log(`
|
|
943
|
+
/home/
|
|
944
|
+
├── cores/
|
|
945
|
+
│ ├── core1/
|
|
946
|
+
│ │ ├── .env
|
|
947
|
+
│ │ ├── addons/
|
|
948
|
+
│ │ │ └── AddonName/
|
|
949
|
+
│ │ │ ├── Backend/
|
|
950
|
+
│ │ │ └── ui/
|
|
951
|
+
│ │ ├── shared/
|
|
952
|
+
│ │ └── storage/
|
|
953
|
+
│ └── core2/
|
|
954
|
+
│ └── ...
|
|
955
|
+
├── services/
|
|
956
|
+
│ ├── docker-core-nuxt.yml
|
|
957
|
+
│ ├── docker-core-php.yml
|
|
958
|
+
│ ├── docker-postgresql.yml
|
|
959
|
+
│ ├── docker-redis.yml
|
|
960
|
+
│ ├── docker-server-nginx.yml
|
|
961
|
+
│ ├── .env
|
|
962
|
+
│ ├── nginx/
|
|
963
|
+
│ │ ├── conf.d/
|
|
964
|
+
│ │ │ └── vucore-global-router.conf
|
|
965
|
+
│ │ └── ssl/
|
|
966
|
+
│ │ ├── domain.crt
|
|
967
|
+
│ │ └── domain.key
|
|
968
|
+
│ ├── postgres/
|
|
969
|
+
│ │ ├── backups/
|
|
970
|
+
│ │ ├── data/
|
|
971
|
+
│ │ └── init/
|
|
972
|
+
│ ├── redis/
|
|
973
|
+
│ │ └── data/
|
|
974
|
+
│ └── scripts/
|
|
975
|
+
│ ├── clear-all-cache.sh
|
|
976
|
+
│ └── addons/
|
|
977
|
+
│ └── build-addon-esm-master.sh
|
|
978
|
+
`);
|
|
979
|
+
}
|
|
980
|
+
// ============================================
|
|
981
|
+
// CHECK API ENDPOINTS
|
|
982
|
+
// ============================================
|
|
983
|
+
async function checkAPI(options) {
|
|
984
|
+
log.title('🔌 API Endpoint Check');
|
|
985
|
+
log.blank();
|
|
986
|
+
const host = await getSSHHost(options.host);
|
|
987
|
+
if (!host) {
|
|
988
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const results = [];
|
|
992
|
+
const domain = options.domain;
|
|
993
|
+
// Internal endpoints (via localhost)
|
|
994
|
+
log.info('=== Internal Endpoints ===');
|
|
995
|
+
const internalEndpoints = [
|
|
996
|
+
{ name: 'Nuxt Health', url: 'http://localhost:3000', expected: 200 },
|
|
997
|
+
{ name: 'PHP Health', url: 'http://localhost:9000/health', expected: null }, // FPM doesn't respond to HTTP
|
|
998
|
+
{ name: 'Nginx', url: 'http://localhost:80', expected: [200, 301, 302] },
|
|
999
|
+
];
|
|
1000
|
+
for (const endpoint of internalEndpoints) {
|
|
1001
|
+
const epSpinner = spinner(`Checking ${endpoint.name}...`).start();
|
|
1002
|
+
const result = await runSSH(host, `curl -s -o /dev/null -w "%{http_code}" ${endpoint.url} 2>/dev/null || echo "000"`);
|
|
1003
|
+
const status = parseInt(result.stdout.trim());
|
|
1004
|
+
if (endpoint.expected === null) {
|
|
1005
|
+
epSpinner.warn(`${endpoint.name}: N/A (FPM)`);
|
|
1006
|
+
results.push({ name: endpoint.name, status: 'warn', message: 'FPM - use docker logs' });
|
|
1007
|
+
}
|
|
1008
|
+
else if (Array.isArray(endpoint.expected) ? endpoint.expected.includes(status) : status === endpoint.expected) {
|
|
1009
|
+
epSpinner.succeed(`${endpoint.name}: HTTP ${status}`);
|
|
1010
|
+
results.push({ name: endpoint.name, status: 'pass', message: `HTTP ${status}` });
|
|
1011
|
+
}
|
|
1012
|
+
else if (status === 0) {
|
|
1013
|
+
epSpinner.fail(`${endpoint.name}: Not responding`);
|
|
1014
|
+
results.push({ name: endpoint.name, status: 'fail', message: 'Not responding' });
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
epSpinner.warn(`${endpoint.name}: HTTP ${status}`);
|
|
1018
|
+
results.push({ name: endpoint.name, status: 'warn', message: `HTTP ${status}` });
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
// External endpoints (if domain provided)
|
|
1022
|
+
if (domain) {
|
|
1023
|
+
log.blank();
|
|
1024
|
+
log.info(`=== External Endpoints (${domain}) ===`);
|
|
1025
|
+
const externalEndpoints = [
|
|
1026
|
+
{ name: 'Main Site', url: `https://${domain}`, expected: 200 },
|
|
1027
|
+
{ name: 'WWW', url: `https://www.${domain}`, expected: [200, 301] },
|
|
1028
|
+
{ name: 'API Health', url: `https://api.${domain}/api/health`, expected: 200 },
|
|
1029
|
+
{ name: 'API System', url: `https://api.${domain}/api/system/health`, expected: 200 },
|
|
1030
|
+
];
|
|
1031
|
+
for (const endpoint of externalEndpoints) {
|
|
1032
|
+
const epSpinner = spinner(`Checking ${endpoint.name}...`).start();
|
|
1033
|
+
const result = await runSSH(host, `curl -sk -o /dev/null -w "%{http_code}" ${endpoint.url} 2>/dev/null || echo "000"`);
|
|
1034
|
+
const status = parseInt(result.stdout.trim());
|
|
1035
|
+
if (Array.isArray(endpoint.expected) ? endpoint.expected.includes(status) : status === endpoint.expected) {
|
|
1036
|
+
epSpinner.succeed(`${endpoint.name}: HTTP ${status}`);
|
|
1037
|
+
results.push({ name: `${endpoint.name} (${domain})`, status: 'pass', message: `HTTP ${status}` });
|
|
1038
|
+
}
|
|
1039
|
+
else if (status === 0) {
|
|
1040
|
+
epSpinner.fail(`${endpoint.name}: Not responding`);
|
|
1041
|
+
results.push({ name: `${endpoint.name} (${domain})`, status: 'fail', message: 'Not responding' });
|
|
1042
|
+
}
|
|
1043
|
+
else {
|
|
1044
|
+
epSpinner.warn(`${endpoint.name}: HTTP ${status}`);
|
|
1045
|
+
results.push({ name: `${endpoint.name} (${domain})`, status: 'warn', message: `HTTP ${status}` });
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
log.blank();
|
|
1050
|
+
printCheckSummary(results);
|
|
1051
|
+
}
|
|
1052
|
+
// ============================================
|
|
1053
|
+
// CHECK SERVICES
|
|
1054
|
+
// ============================================
|
|
1055
|
+
async function checkServices(options) {
|
|
1056
|
+
log.title('🐳 ciCore Services Status');
|
|
1057
|
+
log.blank();
|
|
1058
|
+
const host = await getSSHHost(options.host);
|
|
1059
|
+
if (!host) {
|
|
1060
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const results = [];
|
|
1064
|
+
// Expected containers
|
|
1065
|
+
const expectedContainers = [
|
|
1066
|
+
{ name: 'cicore_global_nginx', port: '80, 443', critical: true },
|
|
1067
|
+
{ name: 'cicore_nuxt', port: '3000', critical: true },
|
|
1068
|
+
{ name: 'cicore_php', port: '9000', critical: true },
|
|
1069
|
+
{ name: 'cicore_global_postgres', port: '5432', critical: true },
|
|
1070
|
+
{ name: 'cicore_global_redis', port: '6379', critical: true },
|
|
1071
|
+
];
|
|
1072
|
+
// Get all container statuses
|
|
1073
|
+
const psResult = await runSSH(host, 'docker ps -a --format "{{.Names}}|{{.Status}}|{{.Ports}}" | grep vucore');
|
|
1074
|
+
const runningContainers = new Map();
|
|
1075
|
+
if (psResult.success && psResult.stdout.trim()) {
|
|
1076
|
+
psResult.stdout.trim().split('\n').forEach(line => {
|
|
1077
|
+
const [name, status, ports] = line.split('|');
|
|
1078
|
+
runningContainers.set(name, { status, ports: ports || '' });
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
log.info('=== Container Status ===');
|
|
1082
|
+
formatTable(['Container', 'Status', 'Ports', 'Health'], expectedContainers.map(c => {
|
|
1083
|
+
const container = runningContainers.get(c.name);
|
|
1084
|
+
if (container) {
|
|
1085
|
+
const isUp = container.status.includes('Up');
|
|
1086
|
+
const health = isUp ? '✅' : '⚠️';
|
|
1087
|
+
results.push({
|
|
1088
|
+
name: c.name,
|
|
1089
|
+
status: isUp ? 'pass' : 'warn',
|
|
1090
|
+
message: container.status
|
|
1091
|
+
});
|
|
1092
|
+
return [c.name, container.status.substring(0, 20), container.ports.substring(0, 30), health];
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
results.push({
|
|
1096
|
+
name: c.name,
|
|
1097
|
+
status: c.critical ? 'fail' : 'warn',
|
|
1098
|
+
message: 'Not found'
|
|
1099
|
+
});
|
|
1100
|
+
return [c.name, 'NOT FOUND', '-', '❌'];
|
|
1101
|
+
}
|
|
1102
|
+
}));
|
|
1103
|
+
// Check Docker networks
|
|
1104
|
+
log.blank();
|
|
1105
|
+
log.info('=== Docker Networks ===');
|
|
1106
|
+
const networkResult = await runSSH(host, 'docker network ls --format "{{.Name}}\t{{.Driver}}" | grep vucore');
|
|
1107
|
+
if (networkResult.success && networkResult.stdout.trim()) {
|
|
1108
|
+
console.log(networkResult.stdout);
|
|
1109
|
+
results.push({ name: 'Docker Networks', status: 'pass', message: 'Found' });
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
log.warn('No ciCore networks found');
|
|
1113
|
+
results.push({ name: 'Docker Networks', status: 'warn', message: 'None found' });
|
|
1114
|
+
}
|
|
1115
|
+
// Check Docker volumes
|
|
1116
|
+
log.blank();
|
|
1117
|
+
log.info('=== Docker Volumes ===');
|
|
1118
|
+
const volumeResult = await runSSH(host, 'docker volume ls --format "{{.Name}}" | grep -E "postgres|redis" | head -10');
|
|
1119
|
+
if (volumeResult.success && volumeResult.stdout.trim()) {
|
|
1120
|
+
volumeResult.stdout.trim().split('\n').forEach(v => log.item(v));
|
|
1121
|
+
results.push({ name: 'Docker Volumes', status: 'pass', message: 'Found' });
|
|
1122
|
+
}
|
|
1123
|
+
else {
|
|
1124
|
+
log.warn('No data volumes found');
|
|
1125
|
+
results.push({ name: 'Docker Volumes', status: 'warn', message: 'None found' });
|
|
1126
|
+
}
|
|
1127
|
+
log.blank();
|
|
1128
|
+
printCheckSummary(results);
|
|
1129
|
+
}
|
|
1130
|
+
// ============================================
|
|
1131
|
+
// CHECK PORTS
|
|
1132
|
+
// ============================================
|
|
1133
|
+
async function checkPorts(options) {
|
|
1134
|
+
log.title('🔌 Port Check');
|
|
1135
|
+
log.blank();
|
|
1136
|
+
const host = await getSSHHost(options.host);
|
|
1137
|
+
if (!host) {
|
|
1138
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const results = [];
|
|
1142
|
+
const requiredPorts = [
|
|
1143
|
+
{ port: 22, name: 'SSH', required: true },
|
|
1144
|
+
{ port: 80, name: 'HTTP', required: true },
|
|
1145
|
+
{ port: 443, name: 'HTTPS', required: true },
|
|
1146
|
+
{ port: 3000, name: 'Nuxt (internal)', required: false },
|
|
1147
|
+
{ port: 9000, name: 'PHP-FPM (internal)', required: false },
|
|
1148
|
+
{ port: 5432, name: 'PostgreSQL (internal)', required: false },
|
|
1149
|
+
{ port: 6379, name: 'Redis (internal)', required: false },
|
|
1150
|
+
];
|
|
1151
|
+
log.info('=== Listening Ports ===');
|
|
1152
|
+
for (const p of requiredPorts) {
|
|
1153
|
+
const portSpinner = spinner(`Checking port ${p.port} (${p.name})...`).start();
|
|
1154
|
+
const result = await runSSH(host, `ss -tlnp | grep ":${p.port} " | head -1`);
|
|
1155
|
+
if (result.success && result.stdout.trim()) {
|
|
1156
|
+
portSpinner.succeed(`Port ${p.port} (${p.name}): LISTENING`);
|
|
1157
|
+
results.push({ name: `Port ${p.port}`, status: 'pass', message: `${p.name} - Listening` });
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
if (p.required) {
|
|
1161
|
+
portSpinner.fail(`Port ${p.port} (${p.name}): NOT LISTENING`);
|
|
1162
|
+
results.push({ name: `Port ${p.port}`, status: 'fail', message: `${p.name} - Not listening` });
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
portSpinner.warn(`Port ${p.port} (${p.name}): not listening`);
|
|
1166
|
+
results.push({ name: `Port ${p.port}`, status: 'warn', message: `${p.name} - Not listening (internal)` });
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
// Check firewall
|
|
1171
|
+
log.blank();
|
|
1172
|
+
log.info('=== Firewall Status ===');
|
|
1173
|
+
const ufwResult = await runSSH(host, 'ufw status 2>/dev/null || echo "ufw not installed"');
|
|
1174
|
+
if (ufwResult.stdout.includes('not installed')) {
|
|
1175
|
+
log.info('UFW: Not installed');
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
console.log(ufwResult.stdout);
|
|
1179
|
+
}
|
|
1180
|
+
log.blank();
|
|
1181
|
+
printCheckSummary(results);
|
|
1182
|
+
}
|
|
1183
|
+
// ============================================
|
|
1184
|
+
// PRE-INSTALLATION CHECK
|
|
1185
|
+
// ============================================
|
|
1186
|
+
async function preInstallCheck(options) {
|
|
1187
|
+
log.title('🚀 Pre-Installation Check');
|
|
1188
|
+
log.blank();
|
|
1189
|
+
const host = await getSSHHost(options.host);
|
|
1190
|
+
if (!host) {
|
|
1191
|
+
log.error(`SSH host "${options.host}" not found`);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
log.kv('Server', `${host.name} (${host.hostname})`);
|
|
1195
|
+
log.blank();
|
|
1196
|
+
const results = [];
|
|
1197
|
+
// 1. SSH Connection
|
|
1198
|
+
log.info('=== 1. SSH Connection ===');
|
|
1199
|
+
const sshSpinner = spinner('Testing SSH connection...').start();
|
|
1200
|
+
const sshResult = await runSSH(host, 'echo "OK"');
|
|
1201
|
+
if (sshResult.success) {
|
|
1202
|
+
sshSpinner.succeed('SSH: Connected');
|
|
1203
|
+
results.push({ name: 'SSH Connection', status: 'pass', message: 'Connected' });
|
|
1204
|
+
}
|
|
1205
|
+
else {
|
|
1206
|
+
sshSpinner.fail('SSH: Connection failed');
|
|
1207
|
+
results.push({ name: 'SSH Connection', status: 'fail', message: 'Failed' });
|
|
1208
|
+
printCheckSummary(results);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
// 2. OS Check
|
|
1212
|
+
log.blank();
|
|
1213
|
+
log.info('=== 2. Operating System ===');
|
|
1214
|
+
const osSpinner = spinner('Checking OS...').start();
|
|
1215
|
+
const osResult = await runSSH(host, 'cat /etc/os-release | grep -E "^(NAME|VERSION)=" | head -2');
|
|
1216
|
+
if (osResult.success) {
|
|
1217
|
+
osSpinner.succeed('OS: ' + osResult.stdout.trim().replace(/\n/g, ' '));
|
|
1218
|
+
results.push({ name: 'Operating System', status: 'pass', message: 'Linux' });
|
|
1219
|
+
}
|
|
1220
|
+
// 3. Docker
|
|
1221
|
+
log.blank();
|
|
1222
|
+
log.info('=== 3. Docker ===');
|
|
1223
|
+
const dockerSpinner = spinner('Checking Docker...').start();
|
|
1224
|
+
const dockerResult = await runSSH(host, 'docker --version 2>/dev/null');
|
|
1225
|
+
if (dockerResult.success) {
|
|
1226
|
+
dockerSpinner.succeed('Docker: ' + dockerResult.stdout.trim());
|
|
1227
|
+
results.push({ name: 'Docker', status: 'pass', message: 'Installed' });
|
|
1228
|
+
// Check Docker daemon
|
|
1229
|
+
const daemonResult = await runSSH(host, 'docker info >/dev/null 2>&1 && echo "running"');
|
|
1230
|
+
if (daemonResult.stdout.includes('running')) {
|
|
1231
|
+
log.item('Docker daemon: Running');
|
|
1232
|
+
results.push({ name: 'Docker Daemon', status: 'pass', message: 'Running' });
|
|
1233
|
+
}
|
|
1234
|
+
else {
|
|
1235
|
+
log.warn('Docker daemon: Not running');
|
|
1236
|
+
results.push({ name: 'Docker Daemon', status: 'fail', message: 'Not running' });
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
else {
|
|
1240
|
+
dockerSpinner.fail('Docker: NOT INSTALLED');
|
|
1241
|
+
results.push({ name: 'Docker', status: 'fail', message: 'Not installed' });
|
|
1242
|
+
log.item('Install: curl -fsSL https://get.docker.com | sh');
|
|
1243
|
+
}
|
|
1244
|
+
// 4. Resources
|
|
1245
|
+
log.blank();
|
|
1246
|
+
log.info('=== 4. System Resources ===');
|
|
1247
|
+
// CPU
|
|
1248
|
+
const cpuResult = await runSSH(host, 'nproc');
|
|
1249
|
+
const cores = parseInt(cpuResult.stdout.trim()) || 0;
|
|
1250
|
+
log.item(`CPU: ${cores} cores ${cores >= 2 ? '✅' : '⚠️'}`);
|
|
1251
|
+
results.push({ name: 'CPU', status: cores >= 2 ? 'pass' : 'warn', message: `${cores} cores` });
|
|
1252
|
+
// RAM
|
|
1253
|
+
const ramResult = await runSSH(host, "free -g | grep Mem | awk '{print $2}'");
|
|
1254
|
+
const ram = parseInt(ramResult.stdout.trim()) || 0;
|
|
1255
|
+
log.item(`RAM: ${ram}GB ${ram >= 4 ? '✅' : ram >= 2 ? '⚠️' : '❌'}`);
|
|
1256
|
+
results.push({ name: 'RAM', status: ram >= 4 ? 'pass' : ram >= 2 ? 'warn' : 'fail', message: `${ram}GB` });
|
|
1257
|
+
// Disk
|
|
1258
|
+
const diskResult = await runSSH(host, "df -BG / | tail -1 | awk '{print $4}' | tr -d 'G'");
|
|
1259
|
+
const disk = parseInt(diskResult.stdout.trim()) || 0;
|
|
1260
|
+
log.item(`Disk Free: ${disk}GB ${disk >= 20 ? '✅' : disk >= 10 ? '⚠️' : '❌'}`);
|
|
1261
|
+
results.push({ name: 'Disk', status: disk >= 20 ? 'pass' : disk >= 10 ? 'warn' : 'fail', message: `${disk}GB free` });
|
|
1262
|
+
// 5. Network
|
|
1263
|
+
log.blank();
|
|
1264
|
+
log.info('=== 5. Network ===');
|
|
1265
|
+
// Internet
|
|
1266
|
+
const internetResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" https://google.com');
|
|
1267
|
+
const hasInternet = internetResult.stdout.trim() === '200' || internetResult.stdout.trim() === '301';
|
|
1268
|
+
log.item(`Internet: ${hasInternet ? '✅ Connected' : '❌ Not connected'}`);
|
|
1269
|
+
results.push({ name: 'Internet', status: hasInternet ? 'pass' : 'fail', message: hasInternet ? 'Connected' : 'Not connected' });
|
|
1270
|
+
// Docker Hub
|
|
1271
|
+
const dockerHubResult = await runSSH(host, 'curl -s -o /dev/null -w "%{http_code}" https://hub.docker.com');
|
|
1272
|
+
const hasDockerHub = dockerHubResult.stdout.trim() === '200';
|
|
1273
|
+
log.item(`Docker Hub: ${hasDockerHub ? '✅ Accessible' : '⚠️ Not accessible'}`);
|
|
1274
|
+
results.push({ name: 'Docker Hub', status: hasDockerHub ? 'pass' : 'warn', message: hasDockerHub ? 'Accessible' : 'Not accessible' });
|
|
1275
|
+
// 6. Ports
|
|
1276
|
+
log.blank();
|
|
1277
|
+
log.info('=== 6. Required Ports ===');
|
|
1278
|
+
const portsToCheck = [80, 443];
|
|
1279
|
+
for (const port of portsToCheck) {
|
|
1280
|
+
const portResult = await runSSH(host, `ss -tlnp | grep ":${port} " | head -1 || echo "free"`);
|
|
1281
|
+
const isFree = portResult.stdout.includes('free') || !portResult.stdout.trim();
|
|
1282
|
+
const isVucore = portResult.stdout.includes('vucore') || portResult.stdout.includes('nginx');
|
|
1283
|
+
if (isFree) {
|
|
1284
|
+
log.item(`Port ${port}: ✅ Available`);
|
|
1285
|
+
results.push({ name: `Port ${port}`, status: 'pass', message: 'Available' });
|
|
1286
|
+
}
|
|
1287
|
+
else if (isVucore) {
|
|
1288
|
+
log.item(`Port ${port}: ✅ Used by VuCore`);
|
|
1289
|
+
results.push({ name: `Port ${port}`, status: 'pass', message: 'VuCore' });
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
log.item(`Port ${port}: ⚠️ In use by another service`);
|
|
1293
|
+
results.push({ name: `Port ${port}`, status: 'warn', message: 'In use' });
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
// Summary
|
|
1297
|
+
log.blank();
|
|
1298
|
+
printCheckSummary(results);
|
|
1299
|
+
// Final verdict
|
|
1300
|
+
const failed = results.filter(r => r.status === 'fail').length;
|
|
1301
|
+
const warned = results.filter(r => r.status === 'warn').length;
|
|
1302
|
+
log.blank();
|
|
1303
|
+
if (failed === 0) {
|
|
1304
|
+
log.box('✅ Server Ready for Installation', [
|
|
1305
|
+
'All critical requirements met.',
|
|
1306
|
+
`Run: vu server install --host ${host.name} --core core1 --domain yourdomain.com`,
|
|
1307
|
+
]);
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
log.box('❌ Server Not Ready', [
|
|
1311
|
+
`${failed} critical issue(s) must be resolved.`,
|
|
1312
|
+
'Fix the issues above and run this check again.',
|
|
1313
|
+
]);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
//# sourceMappingURL=index.js.map
|