@archlast/cli 0.0.1
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/README.md +141 -0
- package/dist/analyzer.d.ts +96 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +404 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +106 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +322875 -0
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +36 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +23 -0
- package/dist/commands/data.d.ts +6 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +300 -0
- package/dist/commands/deploy.d.ts +9 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +59 -0
- package/dist/commands/dev.d.ts +10 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +132 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +100 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.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 +38 -0
- package/dist/commands/pull.d.ts +16 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +415 -0
- package/dist/commands/restart.d.ts +11 -0
- package/dist/commands/restart.d.ts.map +1 -0
- package/dist/commands/restart.js +63 -0
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +74 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +69 -0
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +23 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +77 -0
- package/dist/docker/compose.d.ts +3 -0
- package/dist/docker/compose.d.ts.map +1 -0
- package/dist/docker/compose.js +47 -0
- package/dist/docker/config.d.ts +12 -0
- package/dist/docker/config.d.ts.map +1 -0
- package/dist/docker/config.js +183 -0
- package/dist/docker/manager.d.ts +19 -0
- package/dist/docker/manager.d.ts.map +1 -0
- package/dist/docker/manager.js +239 -0
- package/dist/docker/ports.d.ts +6 -0
- package/dist/docker/ports.d.ts.map +1 -0
- package/dist/docker/restart-on-deploy.d.ts +6 -0
- package/dist/docker/restart-on-deploy.d.ts.map +1 -0
- package/dist/docker/types.d.ts +36 -0
- package/dist/docker/types.d.ts.map +1 -0
- package/dist/docker/types.js +1 -0
- package/dist/events-listener.d.ts +19 -0
- package/dist/events-listener.d.ts.map +1 -0
- package/dist/events-listener.js +105 -0
- package/dist/generator.d.ts +44 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +1816 -0
- package/dist/generators/di.d.ts +21 -0
- package/dist/generators/di.d.ts.map +1 -0
- package/dist/generators/di.js +100 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/project.d.ts +18 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/protocol.d.ts +58 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +5 -0
- package/dist/uploader.d.ts +63 -0
- package/dist/uploader.d.ts.map +1 -0
- package/dist/uploader.js +255 -0
- package/dist/watcher.d.ts +13 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +38 -0
- package/package.json +58 -0
- package/scripts/postinstall.cjs +65 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface StartOptions {
|
|
2
|
+
port?: string;
|
|
3
|
+
path?: string;
|
|
4
|
+
image?: string;
|
|
5
|
+
tag?: string;
|
|
6
|
+
container?: string;
|
|
7
|
+
config?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function startCommand(options: StartOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AASA,UAAU,YAAY;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAwBD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DvE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import { DockerManager } from "../docker/manager";
|
|
6
|
+
import { generateComposeFile } from "../docker/compose";
|
|
7
|
+
import { loadDockerConfig } from "../docker/config";
|
|
8
|
+
const HEALTH_TIMEOUT_MS = 60000;
|
|
9
|
+
const HEALTH_INTERVAL_MS = 2000;
|
|
10
|
+
async function waitForHealth(port, timeoutMs) {
|
|
11
|
+
const deadline = Date.now() + timeoutMs;
|
|
12
|
+
const url = `http://localhost:${port}/health`;
|
|
13
|
+
while (Date.now() < deadline) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
16
|
+
if (response.ok) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// retry
|
|
22
|
+
}
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, HEALTH_INTERVAL_MS));
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
export async function startCommand(options) {
|
|
28
|
+
console.log(chalk.blue.bold("Archlast Docker Start\n"));
|
|
29
|
+
const config = loadDockerConfig({
|
|
30
|
+
path: options.path,
|
|
31
|
+
port: options.port,
|
|
32
|
+
image: options.image,
|
|
33
|
+
tag: options.tag,
|
|
34
|
+
containerName: options.container,
|
|
35
|
+
configFile: options.config,
|
|
36
|
+
});
|
|
37
|
+
const manager = new DockerManager();
|
|
38
|
+
const dockerAvailable = await manager.checkAvailable();
|
|
39
|
+
if (!dockerAvailable) {
|
|
40
|
+
console.error(chalk.red("Docker is not available."));
|
|
41
|
+
console.log(chalk.yellow("Install Docker Desktop or start Docker Engine before running this command."));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (process.env.DOCKER_HOST?.includes("podman") || process.env.PODMAN_SOCKET) {
|
|
45
|
+
console.log(chalk.yellow("Warning: Podman support is experimental. If issues arise, try Docker Desktop."));
|
|
46
|
+
}
|
|
47
|
+
const composeDir = path.dirname(config.composePath);
|
|
48
|
+
fs.mkdirSync(composeDir, { recursive: true });
|
|
49
|
+
fs.writeFileSync(config.composePath, generateComposeFile(config), "utf-8");
|
|
50
|
+
const spinner = ora(`Starting ${config.image}:${config.tag}...`).start();
|
|
51
|
+
try {
|
|
52
|
+
await manager.start(config);
|
|
53
|
+
const healthy = await waitForHealth(config.port, HEALTH_TIMEOUT_MS);
|
|
54
|
+
if (!healthy) {
|
|
55
|
+
spinner.warn("Container started, but health check did not pass within 60 seconds.");
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
spinner.succeed("Archlast server is running.");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
spinner.fail("Failed to start container.");
|
|
63
|
+
if (error instanceof Error) {
|
|
64
|
+
console.error(chalk.red(error.message));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.error(chalk.red(String(error)));
|
|
68
|
+
}
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log(chalk.green(`API: http://localhost:${config.port}`));
|
|
72
|
+
console.log(chalk.green(`Dashboard: http://localhost:${config.port}/_admin`));
|
|
73
|
+
console.log(chalk.gray(`Compose: ${config.composePath}`));
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,UAAU,aAAa;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AA2BD,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CzE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { DockerManager } from "../docker/manager";
|
|
3
|
+
import { loadDockerConfig } from "../docker/config";
|
|
4
|
+
function formatDuration(seconds) {
|
|
5
|
+
if (!seconds && seconds !== 0)
|
|
6
|
+
return "unknown";
|
|
7
|
+
const days = Math.floor(seconds / 86400);
|
|
8
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
9
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
10
|
+
const parts = [];
|
|
11
|
+
if (days)
|
|
12
|
+
parts.push(`${days}d`);
|
|
13
|
+
if (hours)
|
|
14
|
+
parts.push(`${hours}h`);
|
|
15
|
+
if (minutes)
|
|
16
|
+
parts.push(`${minutes}m`);
|
|
17
|
+
if (!parts.length)
|
|
18
|
+
parts.push(`${seconds}s`);
|
|
19
|
+
return parts.join(" ");
|
|
20
|
+
}
|
|
21
|
+
function formatBytes(bytes) {
|
|
22
|
+
if (!bytes && bytes !== 0)
|
|
23
|
+
return "unknown";
|
|
24
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
25
|
+
let value = bytes;
|
|
26
|
+
let unitIndex = 0;
|
|
27
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
28
|
+
value /= 1024;
|
|
29
|
+
unitIndex += 1;
|
|
30
|
+
}
|
|
31
|
+
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
32
|
+
}
|
|
33
|
+
export async function statusCommand(options) {
|
|
34
|
+
console.log(chalk.blue.bold("Archlast Docker Status\n"));
|
|
35
|
+
const config = loadDockerConfig({
|
|
36
|
+
path: options.path,
|
|
37
|
+
containerName: options.container,
|
|
38
|
+
configFile: options.config,
|
|
39
|
+
});
|
|
40
|
+
const manager = new DockerManager();
|
|
41
|
+
const dockerAvailable = await manager.checkAvailable();
|
|
42
|
+
if (!dockerAvailable) {
|
|
43
|
+
console.error(chalk.red("Docker is not available."));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const status = await manager.status(config.containerName);
|
|
47
|
+
if (!status.exists) {
|
|
48
|
+
console.log(chalk.yellow("Archlast container not found."));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(chalk.green(`Container: ${config.containerName}`));
|
|
52
|
+
console.log(`State: ${status.state ?? "unknown"}`);
|
|
53
|
+
console.log(`Health: ${status.health ?? "unknown"}`);
|
|
54
|
+
console.log(`Uptime: ${formatDuration(status.uptimeSeconds)}`);
|
|
55
|
+
if (status.ports && status.ports.length > 0) {
|
|
56
|
+
const ports = status.ports
|
|
57
|
+
.map((port) => port.hostPort ? `${port.hostIp ?? "0.0.0.0"}:${port.hostPort}->${port.containerPort}` : `${port.containerPort}`)
|
|
58
|
+
.join(", ");
|
|
59
|
+
console.log(`Ports: ${ports}`);
|
|
60
|
+
}
|
|
61
|
+
if (status.cpuPercent !== undefined || status.memoryUsageBytes !== undefined) {
|
|
62
|
+
const cpuText = status.cpuPercent !== undefined ? `${status.cpuPercent.toFixed(1)}%` : "unknown";
|
|
63
|
+
const memoryText = status.memoryUsageBytes !== undefined
|
|
64
|
+
? `${formatBytes(status.memoryUsageBytes)} / ${formatBytes(status.memoryLimitBytes)}`
|
|
65
|
+
: "unknown";
|
|
66
|
+
console.log(`CPU: ${cpuText}`);
|
|
67
|
+
console.log(`Memory: ${memoryText}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAIA,UAAU,WAAW;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBrE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { DockerManager } from "../docker/manager";
|
|
3
|
+
import { loadDockerConfig } from "../docker/config";
|
|
4
|
+
export async function stopCommand(options) {
|
|
5
|
+
console.log(chalk.blue.bold("Archlast Docker Stop\n"));
|
|
6
|
+
const config = loadDockerConfig({
|
|
7
|
+
path: options.path,
|
|
8
|
+
containerName: options.container,
|
|
9
|
+
configFile: options.config,
|
|
10
|
+
});
|
|
11
|
+
const manager = new DockerManager();
|
|
12
|
+
const dockerAvailable = await manager.checkAvailable();
|
|
13
|
+
if (!dockerAvailable) {
|
|
14
|
+
console.error(chalk.red("Docker is not available."));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const stopped = await manager.stop(config.containerName);
|
|
18
|
+
if (!stopped) {
|
|
19
|
+
console.log(chalk.yellow("No running Archlast container found."));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log(chalk.green("Archlast container stopped."));
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface UpgradeOptions {
|
|
2
|
+
path?: string;
|
|
3
|
+
image?: string;
|
|
4
|
+
tag?: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
container?: string;
|
|
7
|
+
port?: string;
|
|
8
|
+
config?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function upgradeCommand(options: UpgradeOptions): Promise<void>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=upgrade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AASA,UAAU,cAAc;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAwBD,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D3E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import { DockerManager } from "../docker/manager";
|
|
6
|
+
import { loadDockerConfig } from "../docker/config";
|
|
7
|
+
import { generateComposeFile } from "../docker/compose";
|
|
8
|
+
const HEALTH_TIMEOUT_MS = 60000;
|
|
9
|
+
const HEALTH_INTERVAL_MS = 2000;
|
|
10
|
+
async function waitForHealth(port, timeoutMs) {
|
|
11
|
+
const deadline = Date.now() + timeoutMs;
|
|
12
|
+
const url = `http://localhost:${port}/health`;
|
|
13
|
+
while (Date.now() < deadline) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
16
|
+
if (response.ok) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// retry
|
|
22
|
+
}
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, HEALTH_INTERVAL_MS));
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
export async function upgradeCommand(options) {
|
|
28
|
+
console.log(chalk.blue.bold("Archlast Docker Upgrade\n"));
|
|
29
|
+
const config = loadDockerConfig({
|
|
30
|
+
path: options.path,
|
|
31
|
+
port: options.port,
|
|
32
|
+
image: options.image,
|
|
33
|
+
tag: options.tag ?? options.version,
|
|
34
|
+
containerName: options.container,
|
|
35
|
+
configFile: options.config,
|
|
36
|
+
});
|
|
37
|
+
const manager = new DockerManager();
|
|
38
|
+
const dockerAvailable = await manager.checkAvailable();
|
|
39
|
+
if (!dockerAvailable) {
|
|
40
|
+
console.error(chalk.red("Docker is not available."));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const imageTag = `${config.image}:${config.tag}`;
|
|
44
|
+
const spinner = ora(`Pulling ${imageTag}...`).start();
|
|
45
|
+
try {
|
|
46
|
+
await manager.pullImage(imageTag);
|
|
47
|
+
spinner.succeed(`Pulled ${imageTag}.`);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
spinner.fail("Failed to pull image.");
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
console.error(chalk.red(error.message));
|
|
53
|
+
}
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const restartSpinner = ora(`Upgrading ${config.containerName}...`).start();
|
|
57
|
+
try {
|
|
58
|
+
await manager.stop(config.containerName);
|
|
59
|
+
await manager.start(config);
|
|
60
|
+
fs.mkdirSync(path.dirname(config.composePath), { recursive: true });
|
|
61
|
+
fs.writeFileSync(config.composePath, generateComposeFile(config), "utf-8");
|
|
62
|
+
const healthy = await waitForHealth(config.port, HEALTH_TIMEOUT_MS);
|
|
63
|
+
if (!healthy) {
|
|
64
|
+
restartSpinner.warn("Container upgraded, but health check did not pass within 60 seconds.");
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
restartSpinner.succeed(`Upgraded to ${config.tag}.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
restartSpinner.fail("Failed to upgrade container.");
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
console.error(chalk.red(error.message));
|
|
74
|
+
}
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/docker/compose.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAUvC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAuChE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { stringify } from "yaml";
|
|
3
|
+
function normalizeDockerPath(input) {
|
|
4
|
+
const resolved = path.resolve(input);
|
|
5
|
+
if (process.platform === "win32") {
|
|
6
|
+
return resolved.replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
return resolved;
|
|
9
|
+
}
|
|
10
|
+
export function generateComposeFile(config) {
|
|
11
|
+
const environment = {};
|
|
12
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
13
|
+
if (value !== undefined && value !== "") {
|
|
14
|
+
environment[key] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const compose = {
|
|
18
|
+
version: "3.8",
|
|
19
|
+
services: {
|
|
20
|
+
archlast: {
|
|
21
|
+
image: `${config.image}:${config.tag}`,
|
|
22
|
+
container_name: config.containerName,
|
|
23
|
+
restart: "unless-stopped",
|
|
24
|
+
ports: [`${config.port}:${config.port}`],
|
|
25
|
+
volumes: [
|
|
26
|
+
`${config.dataVolumeName}:/data`,
|
|
27
|
+
`${normalizeDockerPath(config.configDir)}:/config:ro`,
|
|
28
|
+
`${normalizeDockerPath(config.deployDir)}:/app/server/.archlast-deploy:ro`,
|
|
29
|
+
],
|
|
30
|
+
environment,
|
|
31
|
+
healthcheck: {
|
|
32
|
+
test: ["CMD", "curl", "-f", `http://localhost:${config.port}/health`],
|
|
33
|
+
interval: "30s",
|
|
34
|
+
timeout: "10s",
|
|
35
|
+
retries: 3,
|
|
36
|
+
start_period: "60s",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
volumes: {
|
|
41
|
+
[config.dataVolumeName]: {
|
|
42
|
+
name: config.dataVolumeName,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
return stringify(compose);
|
|
47
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DockerConfig } from "./types";
|
|
2
|
+
export interface DockerConfigOptions {
|
|
3
|
+
path?: string;
|
|
4
|
+
port?: string | number;
|
|
5
|
+
image?: string;
|
|
6
|
+
tag?: string;
|
|
7
|
+
containerName?: string;
|
|
8
|
+
configFile?: string;
|
|
9
|
+
restartOnDeploy?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadDockerConfig(options: DockerConfigOptions): DockerConfig;
|
|
12
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/docker/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,WAAW,mBAAmB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAwLD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAyH3E"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
const DEFAULTS = {
|
|
5
|
+
docker: {
|
|
6
|
+
image: "archlast/server",
|
|
7
|
+
tag: "latest",
|
|
8
|
+
containerName: "archlast-server",
|
|
9
|
+
volumeName: "archlast-data",
|
|
10
|
+
},
|
|
11
|
+
server: {
|
|
12
|
+
port: 4000,
|
|
13
|
+
},
|
|
14
|
+
paths: {
|
|
15
|
+
config: ".archlast/config",
|
|
16
|
+
deploy: ".archlast-deploy",
|
|
17
|
+
},
|
|
18
|
+
cors: {
|
|
19
|
+
origins: ["http://localhost:3000"],
|
|
20
|
+
},
|
|
21
|
+
env: {
|
|
22
|
+
NODE_ENV: "development",
|
|
23
|
+
ARCHLAST_LOG_LEVEL: "info",
|
|
24
|
+
ARCHLAST_DB_ROOT: "/data",
|
|
25
|
+
STORAGE_ROOT: "/data/storage",
|
|
26
|
+
ARCHLAST_DASHBOARD_PORT: "4001",
|
|
27
|
+
ARCHLAST_STORE_PORT: "7001",
|
|
28
|
+
ARCHLAST_STORE_NO_TLS: "false",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
const ENV_PREFIXES = ["ARCHLAST_", "S3_", "AWS_", "STORAGE_"];
|
|
32
|
+
const ENV_KEYS = ["NODE_ENV", "BETTER_AUTH_SECRET"];
|
|
33
|
+
function pickEnv(source) {
|
|
34
|
+
const result = {};
|
|
35
|
+
for (const [key, value] of Object.entries(source)) {
|
|
36
|
+
if (!value)
|
|
37
|
+
continue;
|
|
38
|
+
if (ENV_KEYS.includes(key)) {
|
|
39
|
+
result[key] = value;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
|
|
43
|
+
result[key] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function parseEnvFile(filePath) {
|
|
49
|
+
const env = {};
|
|
50
|
+
const contents = fs.readFileSync(filePath, "utf-8");
|
|
51
|
+
const lines = contents.split(/\r?\n/);
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
55
|
+
continue;
|
|
56
|
+
const index = trimmed.indexOf("=");
|
|
57
|
+
if (index === -1)
|
|
58
|
+
continue;
|
|
59
|
+
const key = trimmed.slice(0, index).trim();
|
|
60
|
+
let value = trimmed.slice(index + 1).trim();
|
|
61
|
+
if ((value.startsWith("\"") && value.endsWith("\"")) ||
|
|
62
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
63
|
+
value = value.slice(1, -1);
|
|
64
|
+
}
|
|
65
|
+
env[key] = value;
|
|
66
|
+
}
|
|
67
|
+
return env;
|
|
68
|
+
}
|
|
69
|
+
function findEnvFile(projectPath) {
|
|
70
|
+
const localEnv = path.join(projectPath, ".env.local");
|
|
71
|
+
if (fs.existsSync(localEnv)) {
|
|
72
|
+
return localEnv;
|
|
73
|
+
}
|
|
74
|
+
const envPath = path.join(projectPath, ".env");
|
|
75
|
+
if (fs.existsSync(envPath)) {
|
|
76
|
+
return envPath;
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
function loadConfigFile(configPath) {
|
|
81
|
+
const require = createRequire(import.meta.url);
|
|
82
|
+
try {
|
|
83
|
+
const loaded = require(configPath);
|
|
84
|
+
return (loaded && loaded.default) || loaded || {};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function resolvePath(projectPath, value) {
|
|
91
|
+
if (path.isAbsolute(value)) {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
return path.resolve(projectPath, value);
|
|
95
|
+
}
|
|
96
|
+
function resolveNumber(value, fallback) {
|
|
97
|
+
if (value === undefined || value === null) {
|
|
98
|
+
return fallback;
|
|
99
|
+
}
|
|
100
|
+
const parsed = typeof value === "number" ? value : parseInt(String(value), 10);
|
|
101
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
102
|
+
}
|
|
103
|
+
export function loadDockerConfig(options) {
|
|
104
|
+
const projectPath = path.resolve(options.path || ".");
|
|
105
|
+
const configFilePath = options.configFile
|
|
106
|
+
? resolvePath(projectPath, options.configFile)
|
|
107
|
+
: path.join(projectPath, "archlast.config.js");
|
|
108
|
+
const hasConfigFile = fs.existsSync(configFilePath);
|
|
109
|
+
const configFile = hasConfigFile ? loadConfigFile(configFilePath) : {};
|
|
110
|
+
const envFilePath = findEnvFile(projectPath);
|
|
111
|
+
const envFileVars = envFilePath ? parseEnvFile(envFilePath) : {};
|
|
112
|
+
const configDocker = configFile.docker || {};
|
|
113
|
+
const configServer = configFile.server || {};
|
|
114
|
+
const configPaths = configFile.paths || {};
|
|
115
|
+
const configCors = configFile.cors || {};
|
|
116
|
+
const configEnv = configFile.env || {};
|
|
117
|
+
const port = resolveNumber(options.port ??
|
|
118
|
+
process.env.ARCHLAST_PORT ??
|
|
119
|
+
configServer.port ??
|
|
120
|
+
envFileVars.ARCHLAST_PORT, DEFAULTS.server.port);
|
|
121
|
+
const image = options.image ??
|
|
122
|
+
process.env.ARCHLAST_DOCKER_IMAGE ??
|
|
123
|
+
process.env.ARCHLAST_IMAGE ??
|
|
124
|
+
configDocker.image ??
|
|
125
|
+
envFileVars.ARCHLAST_DOCKER_IMAGE ??
|
|
126
|
+
envFileVars.ARCHLAST_IMAGE ??
|
|
127
|
+
DEFAULTS.docker.image;
|
|
128
|
+
const tag = options.tag ??
|
|
129
|
+
process.env.ARCHLAST_VERSION ??
|
|
130
|
+
configDocker.tag ??
|
|
131
|
+
envFileVars.ARCHLAST_VERSION ??
|
|
132
|
+
DEFAULTS.docker.tag;
|
|
133
|
+
const containerName = options.containerName ??
|
|
134
|
+
process.env.ARCHLAST_CONTAINER_NAME ??
|
|
135
|
+
configDocker.containerName ??
|
|
136
|
+
envFileVars.ARCHLAST_CONTAINER_NAME ??
|
|
137
|
+
DEFAULTS.docker.containerName;
|
|
138
|
+
const dataVolumeName = process.env.ARCHLAST_DATA_VOLUME ??
|
|
139
|
+
configDocker.volumeName ??
|
|
140
|
+
envFileVars.ARCHLAST_DATA_VOLUME ??
|
|
141
|
+
DEFAULTS.docker.volumeName;
|
|
142
|
+
const configDir = resolvePath(projectPath, process.env.ARCHLAST_CONFIG_DIR ??
|
|
143
|
+
configPaths.config ??
|
|
144
|
+
envFileVars.ARCHLAST_CONFIG_DIR ??
|
|
145
|
+
DEFAULTS.paths.config);
|
|
146
|
+
const deployDir = resolvePath(projectPath, process.env.ARCHLAST_DEPLOY_DIR ??
|
|
147
|
+
configPaths.deploy ??
|
|
148
|
+
envFileVars.ARCHLAST_DEPLOY_DIR ??
|
|
149
|
+
DEFAULTS.paths.deploy);
|
|
150
|
+
const envFromDefaults = { ...DEFAULTS.env };
|
|
151
|
+
const envFromFile = pickEnv(envFileVars);
|
|
152
|
+
const envFromConfig = pickEnv(configEnv);
|
|
153
|
+
const envFromProcess = pickEnv(process.env);
|
|
154
|
+
const env = {
|
|
155
|
+
...envFromDefaults,
|
|
156
|
+
...envFromFile,
|
|
157
|
+
...envFromConfig,
|
|
158
|
+
...envFromProcess,
|
|
159
|
+
};
|
|
160
|
+
env.PORT = String(port);
|
|
161
|
+
env.ARCHLAST_PORT = env.ARCHLAST_PORT ?? String(port);
|
|
162
|
+
if (!env.ARCHLAST_ALLOWED_ORIGINS) {
|
|
163
|
+
const origins = configCors.origins?.length ? configCors.origins : DEFAULTS.cors.origins;
|
|
164
|
+
env.ARCHLAST_ALLOWED_ORIGINS = origins.join(",");
|
|
165
|
+
}
|
|
166
|
+
if (!env.NODE_ENV) {
|
|
167
|
+
env.NODE_ENV = DEFAULTS.env.NODE_ENV;
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
projectPath,
|
|
171
|
+
image,
|
|
172
|
+
tag,
|
|
173
|
+
containerName,
|
|
174
|
+
port,
|
|
175
|
+
dataVolumeName,
|
|
176
|
+
configDir,
|
|
177
|
+
deployDir,
|
|
178
|
+
composePath: path.join(projectPath, ".archlast", "docker-compose.yml"),
|
|
179
|
+
env,
|
|
180
|
+
configFilePath: hasConfigFile ? configFilePath : undefined,
|
|
181
|
+
envFilePath,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Docker from "dockerode";
|
|
2
|
+
import { DockerConfig, DockerContainerStatus, DockerLogsOptions } from "./types";
|
|
3
|
+
export declare class DockerManager {
|
|
4
|
+
private docker;
|
|
5
|
+
constructor(docker?: Docker);
|
|
6
|
+
checkAvailable(): Promise<boolean>;
|
|
7
|
+
pullImage(imageTag: string, onProgress?: (event: Record<string, unknown>) => void): Promise<void>;
|
|
8
|
+
ensureImage(imageTag: string): Promise<void>;
|
|
9
|
+
ensureVolume(volumeName: string): Promise<void>;
|
|
10
|
+
start(config: DockerConfig): Promise<void>;
|
|
11
|
+
stop(containerName: string): Promise<boolean>;
|
|
12
|
+
restart(config: DockerConfig): Promise<void>;
|
|
13
|
+
restartContainer(containerName: string): Promise<boolean>;
|
|
14
|
+
status(containerName: string): Promise<DockerContainerStatus>;
|
|
15
|
+
logs(containerName: string, options?: DockerLogsOptions): Promise<NodeJS.ReadableStream>;
|
|
16
|
+
demuxStream(stream: NodeJS.ReadableStream, stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream): void;
|
|
17
|
+
private fetchStats;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/docker/manager.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjF,qBAAa,aAAa;IACtB,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,CAAC,EAAE,MAAM;IAIrB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IASlC,SAAS,CACX,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GACtD,OAAO,CAAC,IAAI,CAAC;IAkBV,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/C,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiE1C,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiB7C,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAazD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA+B7D,IAAI,CACN,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,iBAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;IAwBjC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI;YAIhG,UAAU;CAmC3B"}
|