suthep 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/.editorconfig +17 -0
- package/.github/workflows/publish.yml +42 -0
- package/.prettierignore +6 -0
- package/.prettierrc +7 -0
- package/.scannerwork/.sonar_lock +0 -0
- package/.scannerwork/report-task.txt +6 -0
- package/.vscode/settings.json +19 -0
- package/LICENSE +21 -0
- package/README.md +317 -0
- package/dist/commands/deploy.js +371 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/down.js +179 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/init.js +188 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/setup.js +90 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/up.js +213 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/certbot.js +64 -0
- package/dist/utils/certbot.js.map +1 -0
- package/dist/utils/config-loader.js +127 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/deployment.js +85 -0
- package/dist/utils/deployment.js.map +1 -0
- package/dist/utils/docker.js +425 -0
- package/dist/utils/docker.js.map +1 -0
- package/dist/utils/env-loader.js +53 -0
- package/dist/utils/env-loader.js.map +1 -0
- package/dist/utils/nginx.js +378 -0
- package/dist/utils/nginx.js.map +1 -0
- package/docs/README.md +38 -0
- package/docs/english/01-introduction.md +84 -0
- package/docs/english/02-installation.md +200 -0
- package/docs/english/03-quick-start.md +258 -0
- package/docs/english/04-configuration.md +433 -0
- package/docs/english/05-commands.md +336 -0
- package/docs/english/06-examples.md +456 -0
- package/docs/english/07-troubleshooting.md +417 -0
- package/docs/english/08-advanced.md +411 -0
- package/docs/english/README.md +48 -0
- package/docs/thai/01-introduction.md +84 -0
- package/docs/thai/02-installation.md +200 -0
- package/docs/thai/03-quick-start.md +258 -0
- package/docs/thai/04-configuration.md +433 -0
- package/docs/thai/05-commands.md +336 -0
- package/docs/thai/06-examples.md +456 -0
- package/docs/thai/07-troubleshooting.md +417 -0
- package/docs/thai/08-advanced.md +411 -0
- package/docs/thai/README.md +48 -0
- package/example/suthep-complete.yml +103 -0
- package/example/suthep-docker-only.yml +71 -0
- package/example/suthep-env-example.yml +113 -0
- package/example/suthep-no-docker.yml +51 -0
- package/example/suthep-path-routing.yml +62 -0
- package/example/suthep.example.yml +88 -0
- package/package.json +51 -0
- package/src/commands/deploy.ts +488 -0
- package/src/commands/down.ts +240 -0
- package/src/commands/init.ts +214 -0
- package/src/commands/setup.ts +112 -0
- package/src/commands/up.ts +271 -0
- package/src/index.ts +109 -0
- package/src/types/config.ts +52 -0
- package/src/utils/__tests__/certbot.test.ts +222 -0
- package/src/utils/__tests__/config-loader.test.ts +419 -0
- package/src/utils/__tests__/deployment.test.ts +243 -0
- package/src/utils/__tests__/nginx.test.ts +412 -0
- package/src/utils/certbot.ts +144 -0
- package/src/utils/config-loader.ts +184 -0
- package/src/utils/deployment.ts +157 -0
- package/src/utils/docker.ts +768 -0
- package/src/utils/env-loader.ts +135 -0
- package/src/utils/nginx.ts +443 -0
- package/suthep-1.0.0.tgz +0 -0
- package/suthep.example.yml +98 -0
- package/suthep.yml +39 -0
- package/todo.md +6 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +46 -0
- package/vitest.config.ts +21 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import { saveConfig } from "../utils/config-loader.js";
|
|
5
|
+
async function initCommand(options) {
|
|
6
|
+
console.log(chalk.blue.bold("\nš Suthep Deployment Configuration\n"));
|
|
7
|
+
if (await fs.pathExists(options.file)) {
|
|
8
|
+
const { overwrite } = await inquirer.prompt([
|
|
9
|
+
{
|
|
10
|
+
type: "confirm",
|
|
11
|
+
name: "overwrite",
|
|
12
|
+
message: `File ${options.file} already exists. Overwrite?`,
|
|
13
|
+
default: false
|
|
14
|
+
}
|
|
15
|
+
]);
|
|
16
|
+
if (!overwrite) {
|
|
17
|
+
console.log(chalk.yellow("Aborted."));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const projectAnswers = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: "input",
|
|
24
|
+
name: "projectName",
|
|
25
|
+
message: "Project name:",
|
|
26
|
+
default: "my-app"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: "input",
|
|
30
|
+
name: "projectVersion",
|
|
31
|
+
message: "Project version:",
|
|
32
|
+
default: "1.0.0"
|
|
33
|
+
}
|
|
34
|
+
]);
|
|
35
|
+
const services = [];
|
|
36
|
+
let addMoreServices = true;
|
|
37
|
+
while (addMoreServices) {
|
|
38
|
+
console.log(chalk.cyan(`
|
|
39
|
+
š¦ Service ${services.length + 1} Configuration`));
|
|
40
|
+
const serviceAnswers = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: "input",
|
|
43
|
+
name: "name",
|
|
44
|
+
message: "Service name:",
|
|
45
|
+
validate: (input) => input.trim() !== "" || "Service name is required"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "number",
|
|
49
|
+
name: "port",
|
|
50
|
+
message: "Service port:",
|
|
51
|
+
default: 3e3,
|
|
52
|
+
validate: (input) => {
|
|
53
|
+
if (input === void 0) return "Port is required";
|
|
54
|
+
return input > 0 && input < 65536 || "Port must be between 1 and 65535";
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: "input",
|
|
59
|
+
name: "domains",
|
|
60
|
+
message: "Domain names (comma-separated):",
|
|
61
|
+
validate: (input) => input.trim() !== "" || "At least one domain is required",
|
|
62
|
+
filter: (input) => input.split(",").map((d) => d.trim())
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: "confirm",
|
|
66
|
+
name: "useDocker",
|
|
67
|
+
message: "Use Docker?",
|
|
68
|
+
default: false
|
|
69
|
+
}
|
|
70
|
+
]);
|
|
71
|
+
let dockerConfig = void 0;
|
|
72
|
+
if (serviceAnswers.useDocker) {
|
|
73
|
+
const dockerAnswers = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: "input",
|
|
76
|
+
name: "image",
|
|
77
|
+
message: "Docker image (leave empty to connect to existing container):"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: "input",
|
|
81
|
+
name: "container",
|
|
82
|
+
message: "Container name:",
|
|
83
|
+
validate: (input) => input.trim() !== "" || "Container name is required"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: "number",
|
|
87
|
+
name: "port",
|
|
88
|
+
message: "Container port:",
|
|
89
|
+
default: serviceAnswers.port
|
|
90
|
+
}
|
|
91
|
+
]);
|
|
92
|
+
dockerConfig = {
|
|
93
|
+
image: dockerAnswers.image || void 0,
|
|
94
|
+
container: dockerAnswers.container,
|
|
95
|
+
port: dockerAnswers.port
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const { addHealthCheck } = await inquirer.prompt([
|
|
99
|
+
{
|
|
100
|
+
type: "confirm",
|
|
101
|
+
name: "addHealthCheck",
|
|
102
|
+
message: "Add health check?",
|
|
103
|
+
default: true
|
|
104
|
+
}
|
|
105
|
+
]);
|
|
106
|
+
let healthCheck = void 0;
|
|
107
|
+
if (addHealthCheck) {
|
|
108
|
+
const healthCheckAnswers = await inquirer.prompt([
|
|
109
|
+
{
|
|
110
|
+
type: "input",
|
|
111
|
+
name: "path",
|
|
112
|
+
message: "Health check path:",
|
|
113
|
+
default: "/health"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
type: "number",
|
|
117
|
+
name: "interval",
|
|
118
|
+
message: "Health check interval (seconds):",
|
|
119
|
+
default: 30
|
|
120
|
+
}
|
|
121
|
+
]);
|
|
122
|
+
healthCheck = healthCheckAnswers;
|
|
123
|
+
}
|
|
124
|
+
services.push({
|
|
125
|
+
name: serviceAnswers.name,
|
|
126
|
+
port: serviceAnswers.port,
|
|
127
|
+
domains: serviceAnswers.domains,
|
|
128
|
+
docker: dockerConfig,
|
|
129
|
+
healthCheck
|
|
130
|
+
});
|
|
131
|
+
const { addMore } = await inquirer.prompt([
|
|
132
|
+
{
|
|
133
|
+
type: "confirm",
|
|
134
|
+
name: "addMore",
|
|
135
|
+
message: "Add another service?",
|
|
136
|
+
default: false
|
|
137
|
+
}
|
|
138
|
+
]);
|
|
139
|
+
addMoreServices = addMore;
|
|
140
|
+
}
|
|
141
|
+
const certbotAnswers = await inquirer.prompt([
|
|
142
|
+
{
|
|
143
|
+
type: "input",
|
|
144
|
+
name: "email",
|
|
145
|
+
message: "Email for SSL certificates:",
|
|
146
|
+
validate: (input) => {
|
|
147
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
148
|
+
return emailRegex.test(input) || "Please enter a valid email address";
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: "confirm",
|
|
153
|
+
name: "staging",
|
|
154
|
+
message: "Use Certbot staging environment? (for testing)",
|
|
155
|
+
default: false
|
|
156
|
+
}
|
|
157
|
+
]);
|
|
158
|
+
const config = {
|
|
159
|
+
project: {
|
|
160
|
+
name: projectAnswers.projectName,
|
|
161
|
+
version: projectAnswers.projectVersion
|
|
162
|
+
},
|
|
163
|
+
services,
|
|
164
|
+
nginx: {
|
|
165
|
+
configPath: "/etc/nginx/sites-available",
|
|
166
|
+
reloadCommand: "sudo nginx -t && sudo systemctl reload nginx"
|
|
167
|
+
},
|
|
168
|
+
certbot: {
|
|
169
|
+
email: certbotAnswers.email,
|
|
170
|
+
staging: certbotAnswers.staging
|
|
171
|
+
},
|
|
172
|
+
deployment: {
|
|
173
|
+
strategy: "rolling",
|
|
174
|
+
healthCheckTimeout: 3e4
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
await saveConfig(options.file, config);
|
|
178
|
+
console.log(chalk.green(`
|
|
179
|
+
ā
Configuration saved to ${options.file}`));
|
|
180
|
+
console.log(chalk.dim("\nNext steps:"));
|
|
181
|
+
console.log(chalk.dim(` 1. Review and edit ${options.file} if needed`));
|
|
182
|
+
console.log(chalk.dim(" 2. Run: suthep setup"));
|
|
183
|
+
console.log(chalk.dim(" 3. Run: suthep deploy\n"));
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
initCommand
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sources":["../../src/commands/init.ts"],"sourcesContent":["import chalk from 'chalk'\nimport fs from 'fs-extra'\nimport inquirer from 'inquirer'\nimport type { DeployConfig } from '../types/config'\nimport { saveConfig } from '../utils/config-loader'\n\ninterface InitOptions {\n file: string\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n console.log(chalk.blue.bold('\\nš Suthep Deployment Configuration\\n'))\n\n // Check if file already exists\n if (await fs.pathExists(options.file)) {\n const { overwrite } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'overwrite',\n message: `File ${options.file} already exists. Overwrite?`,\n default: false,\n },\n ])\n\n if (!overwrite) {\n console.log(chalk.yellow('Aborted.'))\n return\n }\n }\n\n // Gather project information\n const projectAnswers = await inquirer.prompt([\n {\n type: 'input',\n name: 'projectName',\n message: 'Project name:',\n default: 'my-app',\n },\n {\n type: 'input',\n name: 'projectVersion',\n message: 'Project version:',\n default: '1.0.0',\n },\n ])\n\n // Gather service information\n const services = []\n let addMoreServices = true\n\n while (addMoreServices) {\n console.log(chalk.cyan(`\\nš¦ Service ${services.length + 1} Configuration`))\n\n const serviceAnswers = await inquirer.prompt([\n {\n type: 'input',\n name: 'name',\n message: 'Service name:',\n validate: (input) => input.trim() !== '' || 'Service name is required',\n },\n {\n type: 'number',\n name: 'port',\n message: 'Service port:',\n default: 3000,\n validate: (input: number | undefined) => {\n if (input === undefined) return 'Port is required'\n return (input > 0 && input < 65536) || 'Port must be between 1 and 65535'\n },\n },\n {\n type: 'input',\n name: 'domains',\n message: 'Domain names (comma-separated):',\n validate: (input) => input.trim() !== '' || 'At least one domain is required',\n filter: (input: string) => input.split(',').map((d: string) => d.trim()),\n },\n {\n type: 'confirm',\n name: 'useDocker',\n message: 'Use Docker?',\n default: false,\n },\n ])\n\n // Docker configuration\n let dockerConfig = undefined\n if (serviceAnswers.useDocker) {\n const dockerAnswers = await inquirer.prompt([\n {\n type: 'input',\n name: 'image',\n message: 'Docker image (leave empty to connect to existing container):',\n },\n {\n type: 'input',\n name: 'container',\n message: 'Container name:',\n validate: (input) => input.trim() !== '' || 'Container name is required',\n },\n {\n type: 'number',\n name: 'port',\n message: 'Container port:',\n default: serviceAnswers.port,\n },\n ])\n\n dockerConfig = {\n image: dockerAnswers.image || undefined,\n container: dockerAnswers.container,\n port: dockerAnswers.port,\n }\n }\n\n // Health check configuration\n const { addHealthCheck } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'addHealthCheck',\n message: 'Add health check?',\n default: true,\n },\n ])\n\n let healthCheck = undefined\n if (addHealthCheck) {\n const healthCheckAnswers = await inquirer.prompt([\n {\n type: 'input',\n name: 'path',\n message: 'Health check path:',\n default: '/health',\n },\n {\n type: 'number',\n name: 'interval',\n message: 'Health check interval (seconds):',\n default: 30,\n },\n ])\n\n healthCheck = healthCheckAnswers\n }\n\n services.push({\n name: serviceAnswers.name,\n port: serviceAnswers.port,\n domains: serviceAnswers.domains,\n docker: dockerConfig,\n healthCheck,\n })\n\n const { addMore } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'addMore',\n message: 'Add another service?',\n default: false,\n },\n ])\n\n addMoreServices = addMore\n }\n\n // Certbot configuration\n const certbotAnswers = await inquirer.prompt([\n {\n type: 'input',\n name: 'email',\n message: 'Email for SSL certificates:',\n validate: (input) => {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n return emailRegex.test(input) || 'Please enter a valid email address'\n },\n },\n {\n type: 'confirm',\n name: 'staging',\n message: 'Use Certbot staging environment? (for testing)',\n default: false,\n },\n ])\n\n // Build configuration object\n const config: DeployConfig = {\n project: {\n name: projectAnswers.projectName,\n version: projectAnswers.projectVersion,\n },\n services,\n nginx: {\n configPath: '/etc/nginx/sites-available',\n reloadCommand: 'sudo nginx -t && sudo systemctl reload nginx',\n },\n certbot: {\n email: certbotAnswers.email,\n staging: certbotAnswers.staging,\n },\n deployment: {\n strategy: 'rolling',\n healthCheckTimeout: 30000,\n },\n }\n\n // Save configuration\n await saveConfig(options.file, config)\n\n console.log(chalk.green(`\\nā
Configuration saved to ${options.file}`))\n console.log(chalk.dim('\\nNext steps:'))\n console.log(chalk.dim(` 1. Review and edit ${options.file} if needed`))\n console.log(chalk.dim(' 2. Run: suthep setup'))\n console.log(chalk.dim(' 3. Run: suthep deploy\\n'))\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
async function setupCommand(options) {
|
|
4
|
+
console.log(chalk.blue.bold("\nš§ Setting up prerequisites\n"));
|
|
5
|
+
const setupNginx = !options.certbotOnly;
|
|
6
|
+
const setupCertbot = !options.nginxOnly;
|
|
7
|
+
try {
|
|
8
|
+
if (setupNginx) {
|
|
9
|
+
console.log(chalk.cyan("š¦ Installing Nginx..."));
|
|
10
|
+
try {
|
|
11
|
+
await execa("nginx", ["-v"]);
|
|
12
|
+
console.log(chalk.green("ā
Nginx is already installed"));
|
|
13
|
+
} catch {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
if (platform === "linux") {
|
|
16
|
+
try {
|
|
17
|
+
await execa("apt-get", ["--version"]);
|
|
18
|
+
console.log(chalk.dim("Using apt-get..."));
|
|
19
|
+
await execa("sudo", ["apt-get", "update"], { stdio: "inherit" });
|
|
20
|
+
await execa("sudo", ["apt-get", "install", "-y", "nginx"], { stdio: "inherit" });
|
|
21
|
+
} catch {
|
|
22
|
+
try {
|
|
23
|
+
await execa("yum", ["--version"]);
|
|
24
|
+
console.log(chalk.dim("Using yum..."));
|
|
25
|
+
await execa("sudo", ["yum", "install", "-y", "nginx"], { stdio: "inherit" });
|
|
26
|
+
} catch {
|
|
27
|
+
throw new Error("Unsupported Linux distribution. Please install Nginx manually.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} else if (platform === "darwin") {
|
|
31
|
+
console.log(chalk.dim("Using Homebrew..."));
|
|
32
|
+
await execa("brew", ["install", "nginx"], { stdio: "inherit" });
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error(`Unsupported platform: ${platform}. Please install Nginx manually.`);
|
|
35
|
+
}
|
|
36
|
+
console.log(chalk.green("ā
Nginx installed successfully"));
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.cyan("š Starting Nginx service..."));
|
|
39
|
+
try {
|
|
40
|
+
await execa("sudo", ["systemctl", "start", "nginx"]);
|
|
41
|
+
await execa("sudo", ["systemctl", "enable", "nginx"]);
|
|
42
|
+
console.log(chalk.green("ā
Nginx service started"));
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.log(chalk.yellow("ā ļø Could not start Nginx via systemctl (might not be available)"));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (setupCertbot) {
|
|
48
|
+
console.log(chalk.cyan("\nš Installing Certbot..."));
|
|
49
|
+
try {
|
|
50
|
+
await execa("certbot", ["--version"]);
|
|
51
|
+
console.log(chalk.green("ā
Certbot is already installed"));
|
|
52
|
+
} catch {
|
|
53
|
+
const platform = process.platform;
|
|
54
|
+
if (platform === "linux") {
|
|
55
|
+
try {
|
|
56
|
+
await execa("apt-get", ["--version"]);
|
|
57
|
+
console.log(chalk.dim("Using apt-get..."));
|
|
58
|
+
await execa("sudo", ["apt-get", "update"], { stdio: "inherit" });
|
|
59
|
+
await execa("sudo", ["apt-get", "install", "-y", "certbot", "python3-certbot-nginx"], { stdio: "inherit" });
|
|
60
|
+
} catch {
|
|
61
|
+
try {
|
|
62
|
+
await execa("yum", ["--version"]);
|
|
63
|
+
console.log(chalk.dim("Using yum..."));
|
|
64
|
+
await execa("sudo", ["yum", "install", "-y", "certbot", "python3-certbot-nginx"], { stdio: "inherit" });
|
|
65
|
+
} catch {
|
|
66
|
+
throw new Error("Unsupported Linux distribution. Please install Certbot manually.");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else if (platform === "darwin") {
|
|
70
|
+
console.log(chalk.dim("Using Homebrew..."));
|
|
71
|
+
await execa("brew", ["install", "certbot"], { stdio: "inherit" });
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(`Unsupported platform: ${platform}. Please install Certbot manually.`);
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk.green("ā
Certbot installed successfully"));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
console.log(chalk.green.bold("\n⨠Setup completed successfully!\n"));
|
|
79
|
+
console.log(chalk.dim("Next steps:"));
|
|
80
|
+
console.log(chalk.dim(" 1. Create a configuration file: suthep init"));
|
|
81
|
+
console.log(chalk.dim(" 2. Deploy your services: suthep deploy\n"));
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(chalk.red("\nā Setup failed:"), error instanceof Error ? error.message : error);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
setupCommand
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sources":["../../src/commands/setup.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { execa } from 'execa';\n\ninterface SetupOptions {\n nginxOnly?: boolean;\n certbotOnly?: boolean;\n}\n\nexport async function setupCommand(options: SetupOptions): Promise<void> {\n console.log(chalk.blue.bold('\\nš§ Setting up prerequisites\\n'));\n\n const setupNginx = !options.certbotOnly;\n const setupCertbot = !options.nginxOnly;\n\n try {\n // Setup Nginx\n if (setupNginx) {\n console.log(chalk.cyan('š¦ Installing Nginx...'));\n\n // Check if Nginx is already installed\n try {\n await execa('nginx', ['-v']);\n console.log(chalk.green('ā
Nginx is already installed'));\n } catch {\n // Install Nginx based on OS\n const platform = process.platform;\n\n if (platform === 'linux') {\n // Detect Linux distribution\n try {\n await execa('apt-get', ['--version']);\n console.log(chalk.dim('Using apt-get...'));\n await execa('sudo', ['apt-get', 'update'], { stdio: 'inherit' });\n await execa('sudo', ['apt-get', 'install', '-y', 'nginx'], { stdio: 'inherit' });\n } catch {\n try {\n await execa('yum', ['--version']);\n console.log(chalk.dim('Using yum...'));\n await execa('sudo', ['yum', 'install', '-y', 'nginx'], { stdio: 'inherit' });\n } catch {\n throw new Error('Unsupported Linux distribution. Please install Nginx manually.');\n }\n }\n } else if (platform === 'darwin') {\n console.log(chalk.dim('Using Homebrew...'));\n await execa('brew', ['install', 'nginx'], { stdio: 'inherit' });\n } else {\n throw new Error(`Unsupported platform: ${platform}. Please install Nginx manually.`);\n }\n\n console.log(chalk.green('ā
Nginx installed successfully'));\n }\n\n // Start Nginx service\n console.log(chalk.cyan('š Starting Nginx service...'));\n try {\n await execa('sudo', ['systemctl', 'start', 'nginx']);\n await execa('sudo', ['systemctl', 'enable', 'nginx']);\n console.log(chalk.green('ā
Nginx service started'));\n } catch (error) {\n console.log(chalk.yellow('ā ļø Could not start Nginx via systemctl (might not be available)'));\n }\n }\n\n // Setup Certbot\n if (setupCertbot) {\n console.log(chalk.cyan('\\nš Installing Certbot...'));\n\n // Check if Certbot is already installed\n try {\n await execa('certbot', ['--version']);\n console.log(chalk.green('ā
Certbot is already installed'));\n } catch {\n const platform = process.platform;\n\n if (platform === 'linux') {\n // Install Certbot based on package manager\n try {\n await execa('apt-get', ['--version']);\n console.log(chalk.dim('Using apt-get...'));\n await execa('sudo', ['apt-get', 'update'], { stdio: 'inherit' });\n await execa('sudo', ['apt-get', 'install', '-y', 'certbot', 'python3-certbot-nginx'], { stdio: 'inherit' });\n } catch {\n try {\n await execa('yum', ['--version']);\n console.log(chalk.dim('Using yum...'));\n await execa('sudo', ['yum', 'install', '-y', 'certbot', 'python3-certbot-nginx'], { stdio: 'inherit' });\n } catch {\n throw new Error('Unsupported Linux distribution. Please install Certbot manually.');\n }\n }\n } else if (platform === 'darwin') {\n console.log(chalk.dim('Using Homebrew...'));\n await execa('brew', ['install', 'certbot'], { stdio: 'inherit' });\n } else {\n throw new Error(`Unsupported platform: ${platform}. Please install Certbot manually.`);\n }\n\n console.log(chalk.green('ā
Certbot installed successfully'));\n }\n }\n\n console.log(chalk.green.bold('\\n⨠Setup completed successfully!\\n'));\n console.log(chalk.dim('Next steps:'));\n console.log(chalk.dim(' 1. Create a configuration file: suthep init'));\n console.log(chalk.dim(' 2. Deploy your services: suthep deploy\\n'));\n\n } catch (error) {\n console.error(chalk.red('\\nā Setup failed:'), error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import { loadConfig } from "../utils/config-loader.js";
|
|
4
|
+
import { waitForService } from "../utils/deployment.js";
|
|
5
|
+
import { startDockerContainer, isContainerRunning } from "../utils/docker.js";
|
|
6
|
+
import { generateNginxConfig, generateMultiServiceNginxConfig, writeNginxConfig, enableSite, reloadNginx } from "../utils/nginx.js";
|
|
7
|
+
async function upCommand(options) {
|
|
8
|
+
console.log(chalk.blue.bold("\nš Bringing Up Services\n"));
|
|
9
|
+
try {
|
|
10
|
+
if (!await fs.pathExists(options.file)) {
|
|
11
|
+
throw new Error(`Configuration file not found: ${options.file}`);
|
|
12
|
+
}
|
|
13
|
+
console.log(chalk.cyan(`š Loading configuration from ${options.file}...`));
|
|
14
|
+
const config = await loadConfig(options.file);
|
|
15
|
+
console.log(chalk.green(`ā
Configuration loaded for project: ${config.project.name}`));
|
|
16
|
+
let servicesToUp = [];
|
|
17
|
+
if (options.all) {
|
|
18
|
+
servicesToUp = config.services;
|
|
19
|
+
console.log(
|
|
20
|
+
chalk.cyan(`š Bringing up all services: ${servicesToUp.map((s) => s.name).join(", ")}
|
|
21
|
+
`)
|
|
22
|
+
);
|
|
23
|
+
} else if (options.serviceName) {
|
|
24
|
+
const service = config.services.find((s) => s.name === options.serviceName);
|
|
25
|
+
if (!service) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Service "${options.serviceName}" not found in configuration. Available services: ${config.services.map((s) => s.name).join(", ")}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
servicesToUp = [service];
|
|
31
|
+
console.log(chalk.cyan(`š Bringing up service: ${options.serviceName}
|
|
32
|
+
`));
|
|
33
|
+
} else {
|
|
34
|
+
throw new Error("Either specify a service name or use --all flag");
|
|
35
|
+
}
|
|
36
|
+
const domainToServices = /* @__PURE__ */ new Map();
|
|
37
|
+
const allDomains = /* @__PURE__ */ new Set();
|
|
38
|
+
for (const service of servicesToUp) {
|
|
39
|
+
for (const domain of service.domains) {
|
|
40
|
+
allDomains.add(domain);
|
|
41
|
+
if (!domainToServices.has(domain)) {
|
|
42
|
+
domainToServices.set(domain, []);
|
|
43
|
+
}
|
|
44
|
+
domainToServices.get(domain).push(service);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const service of servicesToUp) {
|
|
48
|
+
if (service.docker) {
|
|
49
|
+
console.log(chalk.cyan(`
|
|
50
|
+
š³ Starting Docker container for service: ${service.name}`));
|
|
51
|
+
try {
|
|
52
|
+
await startDockerContainer(service);
|
|
53
|
+
console.log(chalk.green(` ā
Container started for service: ${service.name}`));
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(
|
|
56
|
+
chalk.red(` ā Failed to start container for service ${service.name}:`),
|
|
57
|
+
error instanceof Error ? error.message : error
|
|
58
|
+
);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const service of servicesToUp) {
|
|
64
|
+
if (service.healthCheck) {
|
|
65
|
+
console.log(chalk.cyan(`
|
|
66
|
+
š„ Waiting for service ${service.name} to be healthy...`));
|
|
67
|
+
const isHealthy = await waitForService(service, config.deployment.healthCheckTimeout);
|
|
68
|
+
if (isHealthy) {
|
|
69
|
+
console.log(chalk.green(` ā
Service ${service.name} is healthy`));
|
|
70
|
+
} else {
|
|
71
|
+
console.log(
|
|
72
|
+
chalk.yellow(` ā ļø Service ${service.name} health check timeout, continuing anyway...`)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (options.nginx && allDomains.size > 0) {
|
|
78
|
+
console.log(chalk.cyan(`
|
|
79
|
+
āļø Configuring Nginx reverse proxy...`));
|
|
80
|
+
const servicesBeingUpped = new Set(servicesToUp.map((s) => s.name));
|
|
81
|
+
for (const domain of allDomains) {
|
|
82
|
+
const configName = domain.replace(/\./g, "_");
|
|
83
|
+
const allServicesForDomain = config.services.filter((s) => s.domains.includes(domain));
|
|
84
|
+
const activeServices = [];
|
|
85
|
+
for (const service of allServicesForDomain) {
|
|
86
|
+
if (service.docker) {
|
|
87
|
+
const isRunning = await isContainerRunning(service.docker.container);
|
|
88
|
+
if (isRunning) {
|
|
89
|
+
activeServices.push(service);
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
if (servicesBeingUpped.has(service.name)) {
|
|
93
|
+
activeServices.push(service);
|
|
94
|
+
} else {
|
|
95
|
+
activeServices.push(service);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (activeServices.length === 0) {
|
|
100
|
+
console.log(
|
|
101
|
+
chalk.yellow(` ā ļø No active services found for ${domain}, skipping Nginx config`)
|
|
102
|
+
);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
if (activeServices.length > 1) {
|
|
107
|
+
console.log(
|
|
108
|
+
chalk.cyan(
|
|
109
|
+
` š Configuring ${domain} with ${activeServices.length} active service(s): ${activeServices.map((s) => s.name).join(", ")}`
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(
|
|
114
|
+
chalk.cyan(` š Configuring ${domain} for service: ${activeServices[0].name}`)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
let nginxConfigContent;
|
|
118
|
+
if (activeServices.length === 1) {
|
|
119
|
+
nginxConfigContent = generateNginxConfig(activeServices[0], false);
|
|
120
|
+
} else {
|
|
121
|
+
nginxConfigContent = generateMultiServiceNginxConfig(activeServices, domain, false);
|
|
122
|
+
}
|
|
123
|
+
await writeNginxConfig(configName, config.nginx.configPath, nginxConfigContent);
|
|
124
|
+
await enableSite(configName, config.nginx.configPath);
|
|
125
|
+
console.log(chalk.green(` ā
Nginx configured for ${domain}`));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(
|
|
128
|
+
chalk.red(` ā Failed to configure Nginx for ${domain}:`),
|
|
129
|
+
error instanceof Error ? error.message : error
|
|
130
|
+
);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (options.https) {
|
|
135
|
+
console.log(chalk.cyan(`
|
|
136
|
+
š Updating Nginx configs with HTTPS...`));
|
|
137
|
+
for (const domain of allDomains) {
|
|
138
|
+
const configName = domain.replace(/\./g, "_");
|
|
139
|
+
const allServicesForDomain = config.services.filter((s) => s.domains.includes(domain));
|
|
140
|
+
const activeServices = [];
|
|
141
|
+
for (const service of allServicesForDomain) {
|
|
142
|
+
if (service.docker) {
|
|
143
|
+
const isRunning = await isContainerRunning(service.docker.container);
|
|
144
|
+
if (isRunning) {
|
|
145
|
+
activeServices.push(service);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
if (servicesBeingUpped.has(service.name)) {
|
|
149
|
+
activeServices.push(service);
|
|
150
|
+
} else {
|
|
151
|
+
activeServices.push(service);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (activeServices.length === 0) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
let nginxConfigContent;
|
|
160
|
+
if (activeServices.length === 1) {
|
|
161
|
+
nginxConfigContent = generateNginxConfig(activeServices[0], true);
|
|
162
|
+
} else {
|
|
163
|
+
nginxConfigContent = generateMultiServiceNginxConfig(activeServices, domain, true);
|
|
164
|
+
}
|
|
165
|
+
await writeNginxConfig(configName, config.nginx.configPath, nginxConfigContent);
|
|
166
|
+
console.log(chalk.green(` ā
HTTPS config updated for ${domain}`));
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(
|
|
169
|
+
chalk.red(` ā Failed to update HTTPS config for ${domain}:`),
|
|
170
|
+
error instanceof Error ? error.message : error
|
|
171
|
+
);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
console.log(chalk.cyan(`
|
|
177
|
+
š Reloading Nginx...`));
|
|
178
|
+
try {
|
|
179
|
+
await reloadNginx(config.nginx.reloadCommand);
|
|
180
|
+
console.log(chalk.green(` ā
Nginx reloaded`));
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error(
|
|
183
|
+
chalk.red(` ā Failed to reload Nginx:`),
|
|
184
|
+
error instanceof Error ? error.message : error
|
|
185
|
+
);
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
console.log(chalk.green.bold("\nā
Services brought up successfully!\n"));
|
|
190
|
+
if (allDomains.size > 0) {
|
|
191
|
+
console.log(chalk.cyan("š Service URLs:"));
|
|
192
|
+
for (const service of servicesToUp) {
|
|
193
|
+
for (const domain of service.domains) {
|
|
194
|
+
const protocol = options.https ? "https" : "http";
|
|
195
|
+
const servicePath = service.path || "/";
|
|
196
|
+
const fullPath = servicePath === "/" ? "" : servicePath;
|
|
197
|
+
console.log(chalk.dim(` ${service.name}: ${protocol}://${domain}${fullPath}`));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
console.log();
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error(
|
|
204
|
+
chalk.red("\nā Failed to bring up services:"),
|
|
205
|
+
error instanceof Error ? error.message : error
|
|
206
|
+
);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export {
|
|
211
|
+
upCommand
|
|
212
|
+
};
|
|
213
|
+
//# sourceMappingURL=up.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"up.js","sources":["../../src/commands/up.ts"],"sourcesContent":["import chalk from 'chalk'\nimport fs from 'fs-extra'\nimport type { ServiceConfig } from '../types/config'\nimport { loadConfig } from '../utils/config-loader'\nimport { waitForService } from '../utils/deployment'\nimport { isContainerRunning, startDockerContainer } from '../utils/docker'\nimport {\n enableSite,\n generateMultiServiceNginxConfig,\n generateNginxConfig,\n reloadNginx,\n writeNginxConfig,\n} from '../utils/nginx'\n\ninterface UpOptions {\n file: string\n all: boolean\n serviceName?: string\n https: boolean\n nginx: boolean\n}\n\nexport async function upCommand(options: UpOptions): Promise<void> {\n console.log(chalk.blue.bold('\\nš Bringing Up Services\\n'))\n\n try {\n // Load configuration (this will also load .env files for variable substitution)\n if (!(await fs.pathExists(options.file))) {\n throw new Error(`Configuration file not found: ${options.file}`)\n }\n\n console.log(chalk.cyan(`š Loading configuration from ${options.file}...`))\n const config = await loadConfig(options.file)\n\n console.log(chalk.green(`ā
Configuration loaded for project: ${config.project.name}`))\n\n // Determine which services to bring up\n let servicesToUp: ServiceConfig[] = []\n\n if (options.all) {\n servicesToUp = config.services\n console.log(\n chalk.cyan(`š Bringing up all services: ${servicesToUp.map((s) => s.name).join(', ')}\\n`)\n )\n } else if (options.serviceName) {\n const service = config.services.find((s) => s.name === options.serviceName)\n if (!service) {\n throw new Error(\n `Service \"${\n options.serviceName\n }\" not found in configuration. Available services: ${config.services\n .map((s) => s.name)\n .join(', ')}`\n )\n }\n servicesToUp = [service]\n console.log(chalk.cyan(`š Bringing up service: ${options.serviceName}\\n`))\n } else {\n throw new Error('Either specify a service name or use --all flag')\n }\n\n // Group services by domain for nginx config management\n const domainToServices = new Map<string, ServiceConfig[]>()\n const allDomains = new Set<string>()\n\n for (const service of servicesToUp) {\n for (const domain of service.domains) {\n allDomains.add(domain)\n if (!domainToServices.has(domain)) {\n domainToServices.set(domain, [])\n }\n domainToServices.get(domain)!.push(service)\n }\n }\n\n // Start Docker containers\n for (const service of servicesToUp) {\n if (service.docker) {\n console.log(chalk.cyan(`\\nš³ Starting Docker container for service: ${service.name}`))\n try {\n await startDockerContainer(service)\n console.log(chalk.green(` ā
Container started for service: ${service.name}`))\n } catch (error) {\n console.error(\n chalk.red(` ā Failed to start container for service ${service.name}:`),\n error instanceof Error ? error.message : error\n )\n throw error\n }\n }\n }\n\n // Wait for services to be healthy\n for (const service of servicesToUp) {\n if (service.healthCheck) {\n console.log(chalk.cyan(`\\nš„ Waiting for service ${service.name} to be healthy...`))\n const isHealthy = await waitForService(service, config.deployment.healthCheckTimeout)\n if (isHealthy) {\n console.log(chalk.green(` ā
Service ${service.name} is healthy`))\n } else {\n console.log(\n chalk.yellow(` ā ļø Service ${service.name} health check timeout, continuing anyway...`)\n )\n }\n }\n }\n\n // Configure Nginx\n if (options.nginx && allDomains.size > 0) {\n console.log(chalk.cyan(`\\nāļø Configuring Nginx reverse proxy...`))\n\n // Create a set of service names being brought up for quick lookup\n const servicesBeingUpped = new Set(servicesToUp.map((s) => s.name))\n\n // For each domain, find all services that use it (from all config services)\n // and include all active services (both newly brought up and already running)\n for (const domain of allDomains) {\n const configName = domain.replace(/\\./g, '_')\n\n // Find all services that use this domain (from entire config)\n const allServicesForDomain = config.services.filter((s) => s.domains.includes(domain))\n\n // Check which services are actually running (have active containers)\n const activeServices: ServiceConfig[] = []\n for (const service of allServicesForDomain) {\n if (service.docker) {\n const isRunning = await isContainerRunning(service.docker.container)\n if (isRunning) {\n activeServices.push(service)\n }\n } else {\n // Service without docker - include if it's being brought up or assume it's running\n if (servicesBeingUpped.has(service.name)) {\n activeServices.push(service)\n } else {\n // For non-docker services, assume they're running if not explicitly being brought up\n // This is a best-effort approach\n activeServices.push(service)\n }\n }\n }\n\n if (activeServices.length === 0) {\n console.log(\n chalk.yellow(` ā ļø No active services found for ${domain}, skipping Nginx config`)\n )\n continue\n }\n\n try {\n // Log domain and services configuration\n if (activeServices.length > 1) {\n console.log(\n chalk.cyan(\n ` š Configuring ${domain} with ${\n activeServices.length\n } active service(s): ${activeServices.map((s) => s.name).join(', ')}`\n )\n )\n } else {\n console.log(\n chalk.cyan(` š Configuring ${domain} for service: ${activeServices[0].name}`)\n )\n }\n\n // Generate Nginx config for all active services on this domain\n let nginxConfigContent: string\n if (activeServices.length === 1) {\n nginxConfigContent = generateNginxConfig(activeServices[0], false)\n } else {\n nginxConfigContent = generateMultiServiceNginxConfig(activeServices, domain, false)\n }\n\n // Write config file\n await writeNginxConfig(configName, config.nginx.configPath, nginxConfigContent)\n await enableSite(configName, config.nginx.configPath)\n\n console.log(chalk.green(` ā
Nginx configured for ${domain}`))\n } catch (error) {\n console.error(\n chalk.red(` ā Failed to configure Nginx for ${domain}:`),\n error instanceof Error ? error.message : error\n )\n throw error\n }\n }\n\n // Update with HTTPS if enabled\n if (options.https) {\n console.log(chalk.cyan(`\\nš Updating Nginx configs with HTTPS...`))\n for (const domain of allDomains) {\n const configName = domain.replace(/\\./g, '_')\n\n // Find all active services for this domain again\n const allServicesForDomain = config.services.filter((s) => s.domains.includes(domain))\n const activeServices: ServiceConfig[] = []\n for (const service of allServicesForDomain) {\n if (service.docker) {\n const isRunning = await isContainerRunning(service.docker.container)\n if (isRunning) {\n activeServices.push(service)\n }\n } else {\n if (servicesBeingUpped.has(service.name)) {\n activeServices.push(service)\n } else {\n activeServices.push(service)\n }\n }\n }\n\n if (activeServices.length === 0) {\n continue\n }\n\n try {\n let nginxConfigContent: string\n if (activeServices.length === 1) {\n nginxConfigContent = generateNginxConfig(activeServices[0], true)\n } else {\n nginxConfigContent = generateMultiServiceNginxConfig(activeServices, domain, true)\n }\n await writeNginxConfig(configName, config.nginx.configPath, nginxConfigContent)\n console.log(chalk.green(` ā
HTTPS config updated for ${domain}`))\n } catch (error) {\n console.error(\n chalk.red(` ā Failed to update HTTPS config for ${domain}:`),\n error instanceof Error ? error.message : error\n )\n throw error\n }\n }\n }\n\n // Reload Nginx\n console.log(chalk.cyan(`\\nš Reloading Nginx...`))\n try {\n await reloadNginx(config.nginx.reloadCommand)\n console.log(chalk.green(` ā
Nginx reloaded`))\n } catch (error) {\n console.error(\n chalk.red(` ā Failed to reload Nginx:`),\n error instanceof Error ? error.message : error\n )\n throw error\n }\n }\n\n console.log(chalk.green.bold('\\nā
Services brought up successfully!\\n'))\n\n // Print service URLs\n if (allDomains.size > 0) {\n console.log(chalk.cyan('š Service URLs:'))\n for (const service of servicesToUp) {\n for (const domain of service.domains) {\n const protocol = options.https ? 'https' : 'http'\n const servicePath = service.path || '/'\n const fullPath = servicePath === '/' ? '' : servicePath\n console.log(chalk.dim(` ${service.name}: ${protocol}://${domain}${fullPath}`))\n }\n }\n console.log()\n }\n } catch (error) {\n console.error(\n chalk.red('\\nā Failed to bring up services:'),\n error instanceof Error ? error.message : error\n )\n process.exit(1)\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { deployCommand } from "./commands/deploy.js";
|
|
7
|
+
import { downCommand } from "./commands/down.js";
|
|
8
|
+
import { initCommand } from "./commands/init.js";
|
|
9
|
+
import { setupCommand } from "./commands/setup.js";
|
|
10
|
+
import { upCommand } from "./commands/up.js";
|
|
11
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname$1 = dirname(__filename$1);
|
|
13
|
+
const packageJsonPath = join(__dirname$1, "..", "package.json");
|
|
14
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program.name("suthep").description("CLI tool for deploying projects with automatic Nginx reverse proxy and HTTPS setup").version(packageJson.version);
|
|
17
|
+
program.command("init").description("Initialize a new deployment configuration file").option("-f, --file <path>", "Configuration file path", "suthep.yml").action(initCommand);
|
|
18
|
+
program.command("setup").description("Setup Nginx and Certbot on the system").option("--nginx-only", "Only setup Nginx").option("--certbot-only", "Only setup Certbot").action(setupCommand);
|
|
19
|
+
program.command("deploy").description("Deploy a project using the configuration file").option("-f, --file <path>", "Configuration file path", "suthep.yml").option("--no-https", "Skip HTTPS setup").option("--no-nginx", "Skip Nginx configuration").option(
|
|
20
|
+
"-e, --env <key=value>",
|
|
21
|
+
"Set environment variables (can be used multiple times, e.g., -e KEY1=value1 -e KEY2=value2)",
|
|
22
|
+
(value, previous = []) => {
|
|
23
|
+
return [...previous, value];
|
|
24
|
+
},
|
|
25
|
+
[]
|
|
26
|
+
).argument("[service-name]", "Name of the service to deploy (deploys all if not specified)").action((serviceName, options) => {
|
|
27
|
+
const cliEnvVars = {};
|
|
28
|
+
if (options.env && Array.isArray(options.env)) {
|
|
29
|
+
for (const envVar of options.env) {
|
|
30
|
+
const equalsIndex = envVar.indexOf("=");
|
|
31
|
+
if (equalsIndex === -1 || equalsIndex === 0) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Invalid environment variable format: ${envVar}. Expected KEY=VALUE (e.g., -e KEY=value)`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const key = envVar.substring(0, equalsIndex);
|
|
37
|
+
const value = envVar.substring(equalsIndex + 1);
|
|
38
|
+
cliEnvVars[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
deployCommand({
|
|
42
|
+
file: options.file || "suthep.yml",
|
|
43
|
+
https: options.https !== false,
|
|
44
|
+
nginx: options.nginx !== false,
|
|
45
|
+
serviceName,
|
|
46
|
+
cliEnvVars
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
program.command("down").description("Bring down services (stop containers and disable Nginx configs)").option("-f, --file <path>", "Configuration file path", "suthep.yml").option("--all", "Bring down all services", false).argument("[service-name]", "Name of the service to bring down").action((serviceName, options) => {
|
|
50
|
+
downCommand({
|
|
51
|
+
file: options.file || "suthep.yml",
|
|
52
|
+
all: options.all || false,
|
|
53
|
+
serviceName
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
program.command("up").description("Bring up services (start containers and enable Nginx configs)").option("-f, --file <path>", "Configuration file path", "suthep.yml").option("--all", "Bring up all services", false).option("--no-https", "Skip HTTPS setup").option("--no-nginx", "Skip Nginx configuration").argument("[service-name]", "Name of the service to bring up").action((serviceName, options) => {
|
|
57
|
+
upCommand({
|
|
58
|
+
file: options.file || "suthep.yml",
|
|
59
|
+
all: options.all || false,
|
|
60
|
+
serviceName,
|
|
61
|
+
https: options.https !== false,
|
|
62
|
+
nginx: options.nginx !== false
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
program.parse();
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { readFileSync } from 'fs'\nimport { dirname, join } from 'path'\nimport { fileURLToPath } from 'url'\nimport { deployCommand } from './commands/deploy'\nimport { downCommand } from './commands/down'\nimport { initCommand } from './commands/init'\nimport { setupCommand } from './commands/setup'\nimport { upCommand } from './commands/up'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJsonPath = join(__dirname, '..', 'package.json')\nconst packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))\n\nconst program = new Command()\n\nprogram\n .name('suthep')\n .description('CLI tool for deploying projects with automatic Nginx reverse proxy and HTTPS setup')\n .version(packageJson.version)\n\nprogram\n .command('init')\n .description('Initialize a new deployment configuration file')\n .option('-f, --file <path>', 'Configuration file path', 'suthep.yml')\n .action(initCommand)\n\nprogram\n .command('setup')\n .description('Setup Nginx and Certbot on the system')\n .option('--nginx-only', 'Only setup Nginx')\n .option('--certbot-only', 'Only setup Certbot')\n .action(setupCommand)\n\nprogram\n .command('deploy')\n .description('Deploy a project using the configuration file')\n .option('-f, --file <path>', 'Configuration file path', 'suthep.yml')\n .option('--no-https', 'Skip HTTPS setup')\n .option('--no-nginx', 'Skip Nginx configuration')\n .option(\n '-e, --env <key=value>',\n 'Set environment variables (can be used multiple times, e.g., -e KEY1=value1 -e KEY2=value2)',\n (value, previous: string[] = []) => {\n return [...previous, value]\n },\n []\n )\n .argument('[service-name]', 'Name of the service to deploy (deploys all if not specified)')\n .action((serviceName, options: any) => {\n // Parse environment variables from CLI\n const cliEnvVars: Record<string, string> = {}\n if (options.env && Array.isArray(options.env)) {\n for (const envVar of options.env) {\n const equalsIndex = envVar.indexOf('=')\n if (equalsIndex === -1 || equalsIndex === 0) {\n throw new Error(\n `Invalid environment variable format: ${envVar}. Expected KEY=VALUE (e.g., -e KEY=value)`\n )\n }\n const key = envVar.substring(0, equalsIndex)\n const value = envVar.substring(equalsIndex + 1)\n cliEnvVars[key] = value\n }\n }\n\n deployCommand({\n file: options.file || 'suthep.yml',\n https: options.https !== false,\n nginx: options.nginx !== false,\n serviceName: serviceName,\n cliEnvVars,\n })\n })\n\nprogram\n .command('down')\n .description('Bring down services (stop containers and disable Nginx configs)')\n .option('-f, --file <path>', 'Configuration file path', 'suthep.yml')\n .option('--all', 'Bring down all services', false)\n .argument('[service-name]', 'Name of the service to bring down')\n .action((serviceName, options) => {\n downCommand({\n file: options.file || 'suthep.yml',\n all: options.all || false,\n serviceName: serviceName,\n })\n })\n\nprogram\n .command('up')\n .description('Bring up services (start containers and enable Nginx configs)')\n .option('-f, --file <path>', 'Configuration file path', 'suthep.yml')\n .option('--all', 'Bring up all services', false)\n .option('--no-https', 'Skip HTTPS setup')\n .option('--no-nginx', 'Skip Nginx configuration')\n .argument('[service-name]', 'Name of the service to bring up')\n .action((serviceName, options) => {\n upCommand({\n file: options.file || 'suthep.yml',\n all: options.all || false,\n serviceName: serviceName,\n https: options.https !== false,\n nginx: options.nginx !== false,\n })\n })\n\nprogram.parse()\n"],"names":["__filename","__dirname"],"mappings":";;;;;;;;;AAUA,MAAMA,eAAa,cAAc,YAAY,GAAG;AAChD,MAAMC,cAAY,QAAQD,YAAU;AACpC,MAAM,kBAAkB,KAAKC,aAAW,MAAM,cAAc;AAC5D,MAAM,cAAc,KAAK,MAAM,aAAa,iBAAiB,OAAO,CAAC;AAErE,MAAM,UAAU,IAAI,QAAA;AAEpB,QACG,KAAK,QAAQ,EACb,YAAY,oFAAoF,EAChG,QAAQ,YAAY,OAAO;AAE9B,QACG,QAAQ,MAAM,EACd,YAAY,gDAAgD,EAC5D,OAAO,qBAAqB,2BAA2B,YAAY,EACnE,OAAO,WAAW;AAErB,QACG,QAAQ,OAAO,EACf,YAAY,uCAAuC,EACnD,OAAO,gBAAgB,kBAAkB,EACzC,OAAO,kBAAkB,oBAAoB,EAC7C,OAAO,YAAY;AAEtB,QACG,QAAQ,QAAQ,EAChB,YAAY,+CAA+C,EAC3D,OAAO,qBAAqB,2BAA2B,YAAY,EACnE,OAAO,cAAc,kBAAkB,EACvC,OAAO,cAAc,0BAA0B,EAC/C;AAAA,EACC;AAAA,EACA;AAAA,EACA,CAAC,OAAO,WAAqB,OAAO;AAClC,WAAO,CAAC,GAAG,UAAU,KAAK;AAAA,EAC5B;AAAA,EACA,CAAA;AACF,EACC,SAAS,kBAAkB,8DAA8D,EACzF,OAAO,CAAC,aAAa,YAAiB;AAErC,QAAM,aAAqC,CAAA;AAC3C,MAAI,QAAQ,OAAO,MAAM,QAAQ,QAAQ,GAAG,GAAG;AAC7C,eAAW,UAAU,QAAQ,KAAK;AAChC,YAAM,cAAc,OAAO,QAAQ,GAAG;AACtC,UAAI,gBAAgB,MAAM,gBAAgB,GAAG;AAC3C,cAAM,IAAI;AAAA,UACR,wCAAwC,MAAM;AAAA,QAAA;AAAA,MAElD;AACA,YAAM,MAAM,OAAO,UAAU,GAAG,WAAW;AAC3C,YAAM,QAAQ,OAAO,UAAU,cAAc,CAAC;AAC9C,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,gBAAc;AAAA,IACZ,MAAM,QAAQ,QAAQ;AAAA,IACtB,OAAO,QAAQ,UAAU;AAAA,IACzB,OAAO,QAAQ,UAAU;AAAA,IACzB;AAAA,IACA;AAAA,EAAA,CACD;AACH,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,iEAAiE,EAC7E,OAAO,qBAAqB,2BAA2B,YAAY,EACnE,OAAO,SAAS,2BAA2B,KAAK,EAChD,SAAS,kBAAkB,mCAAmC,EAC9D,OAAO,CAAC,aAAa,YAAY;AAChC,cAAY;AAAA,IACV,MAAM,QAAQ,QAAQ;AAAA,IACtB,KAAK,QAAQ,OAAO;AAAA,IACpB;AAAA,EAAA,CACD;AACH,CAAC;AAEH,QACG,QAAQ,IAAI,EACZ,YAAY,+DAA+D,EAC3E,OAAO,qBAAqB,2BAA2B,YAAY,EACnE,OAAO,SAAS,yBAAyB,KAAK,EAC9C,OAAO,cAAc,kBAAkB,EACvC,OAAO,cAAc,0BAA0B,EAC/C,SAAS,kBAAkB,iCAAiC,EAC5D,OAAO,CAAC,aAAa,YAAY;AAChC,YAAU;AAAA,IACR,MAAM,QAAQ,QAAQ;AAAA,IACtB,KAAK,QAAQ,OAAO;AAAA,IACpB;AAAA,IACA,OAAO,QAAQ,UAAU;AAAA,IACzB,OAAO,QAAQ,UAAU;AAAA,EAAA,CAC1B;AACH,CAAC;AAEH,QAAQ,MAAA;"}
|