hostfn 0.1.1 → 0.1.2
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/__tests__/core/backup.test.d.ts +2 -0
- package/dist/__tests__/core/backup.test.d.ts.map +1 -0
- package/dist/__tests__/core/backup.test.js +108 -0
- package/dist/__tests__/core/backup.test.js.map +1 -0
- package/dist/__tests__/core/health.test.d.ts +2 -0
- package/dist/__tests__/core/health.test.d.ts.map +1 -0
- package/dist/__tests__/core/health.test.js +97 -0
- package/dist/__tests__/core/health.test.js.map +1 -0
- package/dist/__tests__/core/lock.test.d.ts +2 -0
- package/dist/__tests__/core/lock.test.d.ts.map +1 -0
- package/dist/__tests__/core/lock.test.js +136 -0
- package/dist/__tests__/core/lock.test.js.map +1 -0
- package/dist/__tests__/core/nginx-multi-domain.test.d.ts +2 -0
- package/dist/__tests__/core/nginx-multi-domain.test.d.ts.map +1 -0
- package/dist/__tests__/core/nginx-multi-domain.test.js +158 -0
- package/dist/__tests__/core/nginx-multi-domain.test.js.map +1 -0
- package/dist/__tests__/runtimes/pm2.test.d.ts +2 -0
- package/dist/__tests__/runtimes/pm2.test.d.ts.map +1 -0
- package/dist/__tests__/runtimes/pm2.test.js +111 -0
- package/dist/__tests__/runtimes/pm2.test.js.map +1 -0
- package/dist/__tests__/utils/validation.test.d.ts +2 -0
- package/dist/__tests__/utils/validation.test.d.ts.map +1 -0
- package/dist/__tests__/utils/validation.test.js +136 -0
- package/dist/__tests__/utils/validation.test.js.map +1 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +636 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/env.d.ts +21 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +317 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/expose.d.ts +6 -0
- package/dist/commands/expose.d.ts.map +1 -0
- package/dist/commands/expose.js +379 -0
- package/dist/commands/expose.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +175 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +75 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/rollback.d.ts +6 -0
- package/dist/commands/rollback.d.ts.map +1 -0
- package/dist/commands/rollback.js +113 -0
- package/dist/commands/rollback.js.map +1 -0
- package/dist/commands/server/info.d.ts +2 -0
- package/dist/commands/server/info.d.ts.map +1 -0
- package/dist/commands/server/info.js +104 -0
- package/dist/commands/server/info.js.map +1 -0
- package/dist/commands/server/setup.d.ts +11 -0
- package/dist/commands/server/setup.d.ts.map +1 -0
- package/dist/commands/server/setup.js +161 -0
- package/dist/commands/server/setup.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +120 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +54 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +323 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +108 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/backup.d.ts +34 -0
- package/dist/core/backup.d.ts.map +1 -0
- package/dist/core/backup.js +95 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/health.d.ts +31 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +78 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/local.d.ts +19 -0
- package/dist/core/local.d.ts.map +1 -0
- package/dist/core/local.js +50 -0
- package/dist/core/local.js.map +1 -0
- package/dist/core/lock.d.ts +28 -0
- package/dist/core/lock.d.ts.map +1 -0
- package/dist/core/lock.js +89 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/nginx.d.ts +43 -0
- package/dist/core/nginx.d.ts.map +1 -0
- package/dist/core/nginx.js +131 -0
- package/dist/core/nginx.js.map +1 -0
- package/dist/core/ssh.d.ts +79 -0
- package/dist/core/ssh.d.ts.map +1 -0
- package/dist/core/ssh.js +264 -0
- package/dist/core/ssh.js.map +1 -0
- package/dist/core/sync.d.ts +25 -0
- package/dist/core/sync.d.ts.map +1 -0
- package/dist/core/sync.js +117 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/core/workspace.d.ts +13 -0
- package/dist/core/workspace.d.ts.map +1 -0
- package/dist/core/workspace.js +141 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/runtimes/base.d.ts +115 -0
- package/dist/runtimes/base.d.ts.map +1 -0
- package/dist/runtimes/base.js +16 -0
- package/dist/runtimes/base.js.map +1 -0
- package/dist/runtimes/nodejs/detector.d.ts +47 -0
- package/dist/runtimes/nodejs/detector.d.ts.map +1 -0
- package/dist/runtimes/nodejs/detector.js +143 -0
- package/dist/runtimes/nodejs/detector.js.map +1 -0
- package/dist/runtimes/nodejs/index.d.ts +14 -0
- package/dist/runtimes/nodejs/index.d.ts.map +1 -0
- package/dist/runtimes/nodejs/index.js +213 -0
- package/dist/runtimes/nodejs/index.js.map +1 -0
- package/dist/runtimes/nodejs/pm2.d.ts +17 -0
- package/dist/runtimes/nodejs/pm2.d.ts.map +1 -0
- package/dist/runtimes/nodejs/pm2.js +60 -0
- package/dist/runtimes/nodejs/pm2.js.map +1 -0
- package/dist/runtimes/registry.d.ts +34 -0
- package/dist/runtimes/registry.d.ts.map +1 -0
- package/dist/runtimes/registry.js +58 -0
- package/dist/runtimes/registry.js.map +1 -0
- package/dist/utils/logger.d.ts +47 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +76 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +32 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +125 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +33 -16
- package/LICENSE +0 -21
- package/README.md +0 -1136
- package/_conduct/specs/1.v0.spec.md +0 -1041
- package/examples/express-api/package.json +0 -22
- package/examples/express-api/src/index.ts +0 -16
- package/examples/express-api/tsconfig.json +0 -11
- package/examples/github-actions-deploy.yml +0 -40
- package/examples/monorepo-config.json +0 -76
- package/examples/monorepo-multi-server-config.json +0 -74
- package/packages/cli/package.json +0 -40
- package/turbo.json +0 -24
- /package/{packages/cli/src → src}/__tests__/core/backup.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/health.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/lock.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/core/nginx-multi-domain.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/runtimes/pm2.test.ts +0 -0
- /package/{packages/cli/src → src}/__tests__/utils/validation.test.ts +0 -0
- /package/{packages/cli/src → src}/commands/deploy.ts +0 -0
- /package/{packages/cli/src → src}/commands/env.ts +0 -0
- /package/{packages/cli/src → src}/commands/expose.ts +0 -0
- /package/{packages/cli/src → src}/commands/init.ts +0 -0
- /package/{packages/cli/src → src}/commands/logs.ts +0 -0
- /package/{packages/cli/src → src}/commands/rollback.ts +0 -0
- /package/{packages/cli/src → src}/commands/server/info.ts +0 -0
- /package/{packages/cli/src → src}/commands/server/setup.ts +0 -0
- /package/{packages/cli/src → src}/commands/status.ts +0 -0
- /package/{packages/cli/src → src}/config/loader.ts +0 -0
- /package/{packages/cli/src → src}/config/schema.ts +0 -0
- /package/{packages/cli/src → src}/core/backup.ts +0 -0
- /package/{packages/cli/src → src}/core/health.ts +0 -0
- /package/{packages/cli/src → src}/core/local.ts +0 -0
- /package/{packages/cli/src → src}/core/lock.ts +0 -0
- /package/{packages/cli/src → src}/core/nginx.ts +0 -0
- /package/{packages/cli/src → src}/core/ssh.ts +0 -0
- /package/{packages/cli/src → src}/core/sync.ts +0 -0
- /package/{packages/cli/src → src}/core/workspace.ts +0 -0
- /package/{packages/cli/src → src}/index.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/base.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/detector.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/index.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/nodejs/pm2.ts +0 -0
- /package/{packages/cli/src → src}/runtimes/registry.ts +0 -0
- /package/{packages/cli/src → src}/utils/logger.ts +0 -0
- /package/{packages/cli/src → src}/utils/validation.ts +0 -0
- /package/{packages/cli/tsconfig.json → tsconfig.json} +0 -0
- /package/{packages/cli/vitest.config.ts → vitest.config.ts} +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SSHConnection } from './ssh.js';
|
|
2
|
+
export interface BackupOptions {
|
|
3
|
+
keep?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Backup manager for deployments
|
|
7
|
+
*/
|
|
8
|
+
export declare class BackupManager {
|
|
9
|
+
private ssh;
|
|
10
|
+
private appDir;
|
|
11
|
+
private backupDir;
|
|
12
|
+
constructor(ssh: SSHConnection, appDir: string);
|
|
13
|
+
/**
|
|
14
|
+
* Create backup of current deployment
|
|
15
|
+
*/
|
|
16
|
+
create(): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* List all backups
|
|
19
|
+
*/
|
|
20
|
+
list(): Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Restore from backup
|
|
23
|
+
*/
|
|
24
|
+
restore(backupName: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Clean up old backups (keep only last N)
|
|
27
|
+
*/
|
|
28
|
+
cleanup(keep?: number): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Get most recent backup
|
|
31
|
+
*/
|
|
32
|
+
getLatest(): Promise<string | null>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../../src/core/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM;IAM9C;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IA6B/B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAqB/B;;OAEG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBhD;;OAEG;IACG,OAAO,CAAC,IAAI,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAI1C"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Backup manager for deployments
|
|
4
|
+
*/
|
|
5
|
+
export class BackupManager {
|
|
6
|
+
ssh;
|
|
7
|
+
appDir;
|
|
8
|
+
backupDir;
|
|
9
|
+
constructor(ssh, appDir) {
|
|
10
|
+
this.ssh = ssh;
|
|
11
|
+
this.appDir = appDir;
|
|
12
|
+
this.backupDir = `${appDir}/backups`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create backup of current deployment
|
|
16
|
+
*/
|
|
17
|
+
async create() {
|
|
18
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
19
|
+
const backupPath = `${this.backupDir}/${timestamp}`;
|
|
20
|
+
// Create backup directory if it doesn't exist
|
|
21
|
+
await this.ssh.mkdir(this.backupDir, true);
|
|
22
|
+
// Check if there's anything to backup
|
|
23
|
+
const distExists = await this.ssh.exists(`${this.appDir}/dist`);
|
|
24
|
+
if (!distExists) {
|
|
25
|
+
Logger.warn('No existing deployment to backup');
|
|
26
|
+
return backupPath;
|
|
27
|
+
}
|
|
28
|
+
// Create backup
|
|
29
|
+
const result = await this.ssh.exec(`mkdir -p ${backupPath} && ` +
|
|
30
|
+
`cp -r ${this.appDir}/dist ${backupPath}/ && ` +
|
|
31
|
+
`cp ${this.appDir}/package.json ${backupPath}/ 2>/dev/null || true`);
|
|
32
|
+
if (result.exitCode !== 0) {
|
|
33
|
+
throw new Error(`Failed to create backup: ${result.stderr}`);
|
|
34
|
+
}
|
|
35
|
+
return backupPath;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* List all backups
|
|
39
|
+
*/
|
|
40
|
+
async list() {
|
|
41
|
+
const exists = await this.ssh.exists(this.backupDir);
|
|
42
|
+
if (!exists) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
const result = await this.ssh.exec(`ls -1 ${this.backupDir}`);
|
|
46
|
+
if (result.exitCode !== 0) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
return result.stdout
|
|
50
|
+
.trim()
|
|
51
|
+
.split('\n')
|
|
52
|
+
.filter(line => line.length > 0)
|
|
53
|
+
.sort()
|
|
54
|
+
.reverse(); // Most recent first
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Restore from backup
|
|
58
|
+
*/
|
|
59
|
+
async restore(backupName) {
|
|
60
|
+
const backupPath = `${this.backupDir}/${backupName}`;
|
|
61
|
+
// Check if backup exists
|
|
62
|
+
const exists = await this.ssh.exists(backupPath);
|
|
63
|
+
if (!exists) {
|
|
64
|
+
throw new Error(`Backup not found: ${backupName}`);
|
|
65
|
+
}
|
|
66
|
+
// Restore
|
|
67
|
+
const result = await this.ssh.exec(`rm -rf ${this.appDir}/dist && ` +
|
|
68
|
+
`cp -r ${backupPath}/dist ${this.appDir}/`);
|
|
69
|
+
if (result.exitCode !== 0) {
|
|
70
|
+
throw new Error(`Failed to restore backup: ${result.stderr}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Clean up old backups (keep only last N)
|
|
75
|
+
*/
|
|
76
|
+
async cleanup(keep = 5) {
|
|
77
|
+
const backups = await this.list();
|
|
78
|
+
if (backups.length <= keep) {
|
|
79
|
+
return; // Nothing to clean up
|
|
80
|
+
}
|
|
81
|
+
const toDelete = backups.slice(keep);
|
|
82
|
+
for (const backup of toDelete) {
|
|
83
|
+
await this.ssh.exec(`rm -rf ${this.backupDir}/${backup}`);
|
|
84
|
+
}
|
|
85
|
+
Logger.info(`Cleaned up ${toDelete.length} old backup(s)`);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get most recent backup
|
|
89
|
+
*/
|
|
90
|
+
async getLatest() {
|
|
91
|
+
const backups = await this.list();
|
|
92
|
+
return backups.length > 0 ? backups[0] : null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/core/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface HealthCheckOptions {
|
|
2
|
+
url: string;
|
|
3
|
+
timeout?: number;
|
|
4
|
+
retries?: number;
|
|
5
|
+
interval?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface HealthCheckResult {
|
|
8
|
+
healthy: boolean;
|
|
9
|
+
statusCode?: number;
|
|
10
|
+
responseTime?: number;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Health check utility
|
|
15
|
+
*/
|
|
16
|
+
export declare class HealthCheck {
|
|
17
|
+
/**
|
|
18
|
+
* Check if service is healthy
|
|
19
|
+
*/
|
|
20
|
+
static check(options: HealthCheckOptions): Promise<HealthCheckResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Poll health endpoint until healthy or max retries reached
|
|
23
|
+
*/
|
|
24
|
+
static poll(options: HealthCheckOptions): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Wait for service to be ready
|
|
27
|
+
*/
|
|
28
|
+
static waitForReady(options: HealthCheckOptions, onProgress?: (attempt: number) => void): Promise<boolean>;
|
|
29
|
+
private static sleep;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB;;OAEG;WACU,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiC3E;;OAEG;WACU,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IA0BhE;;OAEG;WACU,YAAY,CACvB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACrC,OAAO,CAAC,OAAO,CAAC;IAqBnB,OAAO,CAAC,MAAM,CAAC,KAAK;CAGrB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Health check utility
|
|
4
|
+
*/
|
|
5
|
+
export class HealthCheck {
|
|
6
|
+
/**
|
|
7
|
+
* Check if service is healthy
|
|
8
|
+
*/
|
|
9
|
+
static async check(options) {
|
|
10
|
+
const timeout = options.timeout || 5000;
|
|
11
|
+
try {
|
|
12
|
+
const startTime = Date.now();
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
15
|
+
const response = await fetch(options.url, {
|
|
16
|
+
signal: controller.signal,
|
|
17
|
+
headers: {
|
|
18
|
+
'User-Agent': 'hostfn-health-check',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(timeoutId);
|
|
22
|
+
const responseTime = Date.now() - startTime;
|
|
23
|
+
return {
|
|
24
|
+
healthy: response.ok,
|
|
25
|
+
statusCode: response.status,
|
|
26
|
+
responseTime,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
healthy: false,
|
|
32
|
+
error: error instanceof Error ? error.message : String(error),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Poll health endpoint until healthy or max retries reached
|
|
38
|
+
*/
|
|
39
|
+
static async poll(options) {
|
|
40
|
+
const retries = options.retries || 10;
|
|
41
|
+
const interval = options.interval || 3000;
|
|
42
|
+
for (let i = 0; i < retries; i++) {
|
|
43
|
+
const result = await this.check(options);
|
|
44
|
+
if (result.healthy) {
|
|
45
|
+
Logger.success(`Health check passed (${result.statusCode}, ${result.responseTime}ms)`);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (i < retries - 1) {
|
|
49
|
+
Logger.warn(`Health check failed (attempt ${i + 1}/${retries}), retrying in ${interval / 1000}s...`);
|
|
50
|
+
await this.sleep(interval);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
Logger.error(`Health check failed after ${retries} attempts`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Wait for service to be ready
|
|
58
|
+
*/
|
|
59
|
+
static async waitForReady(options, onProgress) {
|
|
60
|
+
const retries = options.retries || 10;
|
|
61
|
+
const interval = options.interval || 3000;
|
|
62
|
+
for (let i = 0; i < retries; i++) {
|
|
63
|
+
onProgress?.(i + 1);
|
|
64
|
+
const result = await this.check(options);
|
|
65
|
+
if (result.healthy) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (i < retries - 1) {
|
|
69
|
+
await this.sleep(interval);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
static sleep(ms) {
|
|
75
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/core/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface LocalCommandResult {
|
|
2
|
+
stdout: string;
|
|
3
|
+
stderr: string;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class LocalExecutor {
|
|
7
|
+
connect(): Promise<void>;
|
|
8
|
+
exec(command: string, options?: {
|
|
9
|
+
streaming?: boolean;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
}): Promise<LocalCommandResult>;
|
|
12
|
+
uploadFile(localPath: string, remotePath: string): Promise<void>;
|
|
13
|
+
downloadFile(remotePath: string, localPath: string): Promise<void>;
|
|
14
|
+
exists(path: string): Promise<boolean>;
|
|
15
|
+
mkdir(path: string, recursive?: boolean): Promise<void>;
|
|
16
|
+
isConnected(): boolean;
|
|
17
|
+
disconnect(): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/core/local.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAa;IAClB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACpC,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqBzB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAItC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,OAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE,WAAW,IAAI,OAAO;IAItB,UAAU,IAAI,IAAI;CAEnB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { mkdir } from 'fs/promises';
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
export class LocalExecutor {
|
|
7
|
+
async connect() {
|
|
8
|
+
return Promise.resolve();
|
|
9
|
+
}
|
|
10
|
+
async exec(command, options) {
|
|
11
|
+
try {
|
|
12
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
13
|
+
cwd: options?.cwd,
|
|
14
|
+
shell: '/bin/bash',
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
stdout,
|
|
18
|
+
stderr,
|
|
19
|
+
exitCode: 0,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return {
|
|
24
|
+
stdout: error.stdout || '',
|
|
25
|
+
stderr: error.stderr || '',
|
|
26
|
+
exitCode: error.code || 1,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async uploadFile(localPath, remotePath) {
|
|
31
|
+
const { copyFileSync } = await import('fs');
|
|
32
|
+
copyFileSync(localPath, remotePath);
|
|
33
|
+
}
|
|
34
|
+
async downloadFile(remotePath, localPath) {
|
|
35
|
+
const { copyFileSync } = await import('fs');
|
|
36
|
+
copyFileSync(remotePath, localPath);
|
|
37
|
+
}
|
|
38
|
+
async exists(path) {
|
|
39
|
+
return existsSync(path);
|
|
40
|
+
}
|
|
41
|
+
async mkdir(path, recursive = true) {
|
|
42
|
+
await mkdir(path, { recursive });
|
|
43
|
+
}
|
|
44
|
+
isConnected() {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
disconnect() {
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/core/local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAQlC,MAAM,OAAO,aAAa;IACxB,KAAK,CAAC,OAAO;QACX,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,OAG3B;QACC,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE;gBAClD,GAAG,EAAE,OAAO,EAAE,GAAG;gBACjB,KAAK,EAAE,WAAW;aACnB,CAAC,CAAC;YAEH,OAAO;gBACL,MAAM;gBACN,MAAM;gBACN,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO;gBACL,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBAC1B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBAC1B,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC;aAC1B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,UAAkB;QACpD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,SAAiB;QACtD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,YAAqB,IAAI;QACjD,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU;IACV,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { SSHConnection } from './ssh.js';
|
|
2
|
+
export interface DeploymentLock {
|
|
3
|
+
pid: number;
|
|
4
|
+
user: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
hostname: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deployment lock manager to prevent concurrent deployments
|
|
10
|
+
*/
|
|
11
|
+
export declare class LockManager {
|
|
12
|
+
private ssh;
|
|
13
|
+
private lockFile;
|
|
14
|
+
constructor(ssh: SSHConnection, remoteDir: string);
|
|
15
|
+
/**
|
|
16
|
+
* Acquire deployment lock
|
|
17
|
+
*/
|
|
18
|
+
acquire(timeout?: number): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Release deployment lock
|
|
21
|
+
*/
|
|
22
|
+
release(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if deployment is locked
|
|
25
|
+
*/
|
|
26
|
+
isLocked(): Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/core/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAS;gBAEb,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM;IAKjD;;OAEG;IACG,OAAO,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAiDtD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;CAqBnC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deployment lock manager to prevent concurrent deployments
|
|
4
|
+
*/
|
|
5
|
+
export class LockManager {
|
|
6
|
+
ssh;
|
|
7
|
+
lockFile;
|
|
8
|
+
constructor(ssh, remoteDir) {
|
|
9
|
+
this.ssh = ssh;
|
|
10
|
+
this.lockFile = `${remoteDir}/.hostfn-deploy.lock`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Acquire deployment lock
|
|
14
|
+
*/
|
|
15
|
+
async acquire(timeout = 300) {
|
|
16
|
+
// Check if lock exists
|
|
17
|
+
const exists = await this.ssh.exists(this.lockFile);
|
|
18
|
+
if (exists) {
|
|
19
|
+
// Read lock info
|
|
20
|
+
const result = await this.ssh.exec(`cat ${this.lockFile}`);
|
|
21
|
+
try {
|
|
22
|
+
const lock = JSON.parse(result.stdout);
|
|
23
|
+
const age = Date.now() - lock.timestamp;
|
|
24
|
+
// Check if lock is stale (older than timeout)
|
|
25
|
+
if (age > timeout * 1000) {
|
|
26
|
+
Logger.warn('Found stale lock file, removing...');
|
|
27
|
+
await this.release();
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const ageMinutes = Math.round(age / 1000 / 60);
|
|
31
|
+
Logger.error('Deployment is already in progress');
|
|
32
|
+
Logger.info(`Started by: ${lock.user}`);
|
|
33
|
+
Logger.info(`Started at: ${new Date(lock.timestamp).toLocaleString()}`);
|
|
34
|
+
Logger.info(`Age: ${ageMinutes} minute(s)`);
|
|
35
|
+
Logger.br();
|
|
36
|
+
Logger.warn('Wait for the current deployment to finish or:');
|
|
37
|
+
Logger.log(' 1. Wait for lock to expire (5 minutes)');
|
|
38
|
+
Logger.log(' 2. Manually remove: ssh <host> rm ' + this.lockFile);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Invalid lock file, remove it
|
|
44
|
+
await this.release();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Create lock
|
|
48
|
+
const lock = {
|
|
49
|
+
pid: process.pid,
|
|
50
|
+
user: process.env.USER || 'unknown',
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
hostname: process.env.HOSTNAME || 'unknown',
|
|
53
|
+
};
|
|
54
|
+
await this.ssh.exec(`cat > ${this.lockFile} << 'LOCKEOF'
|
|
55
|
+
${JSON.stringify(lock, null, 2)}
|
|
56
|
+
LOCKEOF`);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Release deployment lock
|
|
61
|
+
*/
|
|
62
|
+
async release() {
|
|
63
|
+
await this.ssh.exec(`rm -f ${this.lockFile}`).catch(() => {
|
|
64
|
+
// Ignore errors on cleanup
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if deployment is locked
|
|
69
|
+
*/
|
|
70
|
+
async isLocked() {
|
|
71
|
+
const exists = await this.ssh.exists(this.lockFile);
|
|
72
|
+
if (!exists) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
// Check if lock is stale
|
|
76
|
+
const result = await this.ssh.exec(`cat ${this.lockFile}`);
|
|
77
|
+
try {
|
|
78
|
+
const lock = JSON.parse(result.stdout);
|
|
79
|
+
const age = Date.now() - lock.timestamp;
|
|
80
|
+
// Lock is stale if older than 5 minutes
|
|
81
|
+
return age < 300 * 1000;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Invalid lock file = not locked
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/core/lock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAS5C;;GAEG;AACH,MAAM,OAAO,WAAW;IACd,GAAG,CAAgB;IACnB,QAAQ,CAAS;IAEzB,YAAY,GAAkB,EAAE,SAAiB;QAC/C,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,GAAG,SAAS,sBAAsB,CAAC;IACrD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,UAAkB,GAAG;QACjC,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEpD,IAAI,MAAM,EAAE,CAAC;YACX,iBAAiB;YACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAE3D,IAAI,CAAC;gBACH,MAAM,IAAI,GAAmB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;gBAExC,8CAA8C;gBAC9C,IAAI,GAAG,GAAG,OAAO,GAAG,IAAI,EAAE,CAAC;oBACzB,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;oBAClD,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACN,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;oBAC/C,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;oBAClD,MAAM,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;oBACxC,MAAM,CAAC,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;oBACxE,MAAM,CAAC,IAAI,CAAC,QAAQ,UAAU,YAAY,CAAC,CAAC;oBAC5C,MAAM,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;oBAC7D,MAAM,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;oBACvD,MAAM,CAAC,GAAG,CAAC,sCAAsC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACnE,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;gBAC/B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QAED,cAAc;QACd,MAAM,IAAI,GAAmB;YAC3B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,SAAS;YACnC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,SAAS;SAC5C,CAAC;QAEF,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;EAC5C,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACvB,CAAC,CAAC;QAEN,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACvD,2BAA2B;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEpD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,KAAK,CAAC;QACf,CAAC;QAED,yBAAyB;QACzB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,IAAI,GAAmB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;YAExC,wCAAwC;YACxC,OAAO,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface NginxServiceConfig {
|
|
2
|
+
name: string;
|
|
3
|
+
port: number;
|
|
4
|
+
exposePath?: string;
|
|
5
|
+
isDefault?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface NginxConfig {
|
|
8
|
+
domain?: string | string[];
|
|
9
|
+
ssl: boolean;
|
|
10
|
+
services: NginxServiceConfig[];
|
|
11
|
+
environment: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class NginxConfigGenerator {
|
|
14
|
+
/**
|
|
15
|
+
* Generate nginx configuration for services
|
|
16
|
+
*/
|
|
17
|
+
static generate(config: NginxConfig): string;
|
|
18
|
+
/**
|
|
19
|
+
* Generate HTTPS server block
|
|
20
|
+
*/
|
|
21
|
+
private static generateHttpsBlock;
|
|
22
|
+
/**
|
|
23
|
+
* Generate HTTP server block
|
|
24
|
+
*/
|
|
25
|
+
private static generateHttpBlock;
|
|
26
|
+
/**
|
|
27
|
+
* Generate HTTP to HTTPS redirect block
|
|
28
|
+
*/
|
|
29
|
+
private static generateHttpRedirectBlock;
|
|
30
|
+
/**
|
|
31
|
+
* Generate location block for a service
|
|
32
|
+
*/
|
|
33
|
+
private static generateLocationBlock;
|
|
34
|
+
/**
|
|
35
|
+
* Get nginx config file path based on the system
|
|
36
|
+
*/
|
|
37
|
+
static getConfigPath(environment: string, useSitesAvailable: boolean): string;
|
|
38
|
+
/**
|
|
39
|
+
* Get command to enable site (for sites-available/sites-enabled systems)
|
|
40
|
+
*/
|
|
41
|
+
static getEnableCommand(environment: string, useSitesAvailable: boolean): string | null;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=nginx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nginx.d.ts","sourceRoot":"","sources":["../../src/core/nginx.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,oBAAoB;IAC/B;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IA0B5C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAgCjC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAyBhC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAYxC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAuBpC;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAG,MAAM;IAO7E;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;CAMxF"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export class NginxConfigGenerator {
|
|
2
|
+
/**
|
|
3
|
+
* Generate nginx configuration for services
|
|
4
|
+
*/
|
|
5
|
+
static generate(config) {
|
|
6
|
+
const { domain, ssl, services, environment } = config;
|
|
7
|
+
// Handle both single domain string and array of domains
|
|
8
|
+
const domains = domain ? (Array.isArray(domain) ? domain : [domain]) : [];
|
|
9
|
+
const serverName = domains.length > 0 ? domains.join(' ') : '_';
|
|
10
|
+
// Separate default and path-based services
|
|
11
|
+
const defaultService = services.find(s => s.isDefault);
|
|
12
|
+
const pathServices = services.filter(s => !s.isDefault && s.exposePath);
|
|
13
|
+
let nginxConfig = '';
|
|
14
|
+
if (ssl) {
|
|
15
|
+
// HTTPS server block
|
|
16
|
+
nginxConfig += this.generateHttpsBlock(serverName, defaultService, pathServices);
|
|
17
|
+
nginxConfig += '\n\n';
|
|
18
|
+
// HTTP redirect block
|
|
19
|
+
nginxConfig += this.generateHttpRedirectBlock(serverName);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
// HTTP only server block
|
|
23
|
+
nginxConfig += this.generateHttpBlock(serverName, defaultService, pathServices);
|
|
24
|
+
}
|
|
25
|
+
return nginxConfig;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate HTTPS server block
|
|
29
|
+
*/
|
|
30
|
+
static generateHttpsBlock(serverName, defaultService, pathServices) {
|
|
31
|
+
// Extract primary domain for certificate path (first domain in the list)
|
|
32
|
+
const primaryDomain = serverName.split(' ')[0];
|
|
33
|
+
let config = `server {
|
|
34
|
+
listen 443 ssl http2;
|
|
35
|
+
listen [::]:443 ssl http2;
|
|
36
|
+
server_name ${serverName};
|
|
37
|
+
|
|
38
|
+
ssl_certificate /etc/letsencrypt/live/${primaryDomain}/fullchain.pem; # managed by Certbot
|
|
39
|
+
ssl_certificate_key /etc/letsencrypt/live/${primaryDomain}/privkey.pem; # managed by Certbot
|
|
40
|
+
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
|
41
|
+
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
|
42
|
+
`;
|
|
43
|
+
// Add path-based services first (higher priority)
|
|
44
|
+
for (const service of pathServices) {
|
|
45
|
+
config += this.generateLocationBlock(service);
|
|
46
|
+
}
|
|
47
|
+
// Add default service
|
|
48
|
+
if (defaultService) {
|
|
49
|
+
config += this.generateLocationBlock(defaultService);
|
|
50
|
+
}
|
|
51
|
+
config += '}\n';
|
|
52
|
+
return config;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generate HTTP server block
|
|
56
|
+
*/
|
|
57
|
+
static generateHttpBlock(serverName, defaultService, pathServices) {
|
|
58
|
+
let config = `server {
|
|
59
|
+
listen 80;
|
|
60
|
+
listen [::]:80;
|
|
61
|
+
server_name ${serverName};
|
|
62
|
+
`;
|
|
63
|
+
// Add path-based services first (higher priority)
|
|
64
|
+
for (const service of pathServices) {
|
|
65
|
+
config += this.generateLocationBlock(service);
|
|
66
|
+
}
|
|
67
|
+
// Add default service
|
|
68
|
+
if (defaultService) {
|
|
69
|
+
config += this.generateLocationBlock(defaultService);
|
|
70
|
+
}
|
|
71
|
+
config += '}\n';
|
|
72
|
+
return config;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate HTTP to HTTPS redirect block
|
|
76
|
+
*/
|
|
77
|
+
static generateHttpRedirectBlock(serverName) {
|
|
78
|
+
return `server {
|
|
79
|
+
listen 80;
|
|
80
|
+
listen [::]:80;
|
|
81
|
+
server_name ${serverName};
|
|
82
|
+
|
|
83
|
+
location / {
|
|
84
|
+
return 301 https://$host$request_uri;
|
|
85
|
+
}
|
|
86
|
+
}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Generate location block for a service
|
|
90
|
+
*/
|
|
91
|
+
static generateLocationBlock(service) {
|
|
92
|
+
const path = service.exposePath || '/';
|
|
93
|
+
const hasTrailingSlash = path !== '/' && path.endsWith('/');
|
|
94
|
+
const proxyPassPath = hasTrailingSlash ? '/' : '';
|
|
95
|
+
return `
|
|
96
|
+
# ${service.name}
|
|
97
|
+
location ${path} {
|
|
98
|
+
proxy_pass http://localhost:${service.port}${proxyPassPath};
|
|
99
|
+
proxy_http_version 1.1;
|
|
100
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
101
|
+
proxy_set_header Connection 'upgrade';
|
|
102
|
+
proxy_set_header Host $host;
|
|
103
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
104
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
105
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
106
|
+
proxy_cache_bypass $http_upgrade;
|
|
107
|
+
proxy_read_timeout 60s;
|
|
108
|
+
proxy_connect_timeout 60s;
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get nginx config file path based on the system
|
|
114
|
+
*/
|
|
115
|
+
static getConfigPath(environment, useSitesAvailable) {
|
|
116
|
+
if (useSitesAvailable) {
|
|
117
|
+
return `/etc/nginx/sites-available/hostfn-${environment}`;
|
|
118
|
+
}
|
|
119
|
+
return `/etc/nginx/conf.d/hostfn-${environment}.conf`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get command to enable site (for sites-available/sites-enabled systems)
|
|
123
|
+
*/
|
|
124
|
+
static getEnableCommand(environment, useSitesAvailable) {
|
|
125
|
+
if (useSitesAvailable) {
|
|
126
|
+
return `ln -sf /etc/nginx/sites-available/hostfn-${environment} /etc/nginx/sites-enabled/hostfn-${environment}`;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=nginx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nginx.js","sourceRoot":"","sources":["../../src/core/nginx.ts"],"names":[],"mappings":"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"}
|