redirector-cli
Version:
Global CLI tool for managing Redirector backend services with Docker Compose
243 lines • 9.04 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockerManager = void 0;
const child_process_1 = require("child_process");
const util_1 = require("util");
const path = __importStar(require("path"));
const logger_1 = require("./logger");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class DockerManager {
constructor(config, projectPath = process.cwd()) {
this.config = config;
this.projectPath = projectPath;
}
getComposeCommand() {
const composePath = path.join(this.projectPath, "docker-compose.yml");
return ["docker", "compose", "-f", composePath];
}
getEnvVars() {
return {
...process.env,
BACKEND_PORT: this.config.backendPort.toString(),
POSTGRES_PORT: this.config.postgresPort.toString(),
DOCKER_USERNAME: this.config.dockerUsername,
NODE_ENV: this.config.environment,
};
}
async pullImages() {
logger_1.Logger.startSpinner("Pulling Docker images...");
try {
const cmd = [...this.getComposeCommand(), "pull"];
await this.execCommand(cmd);
logger_1.Logger.succeedSpinner("Docker images pulled successfully");
}
catch (error) {
logger_1.Logger.failSpinner("Failed to pull Docker images");
throw error;
}
}
async startServices(detached = true) {
logger_1.Logger.startSpinner("Starting services...");
try {
const cmd = [...this.getComposeCommand(), "up"];
if (detached) {
cmd.push("-d");
}
await this.execCommand(cmd);
logger_1.Logger.succeedSpinner("Services started successfully");
// Wait a moment for services to initialize
await this.waitForServices();
}
catch (error) {
logger_1.Logger.failSpinner("Failed to start services");
throw error;
}
}
async stopServices() {
logger_1.Logger.startSpinner("Stopping services...");
try {
const cmd = [...this.getComposeCommand(), "down"];
await this.execCommand(cmd);
logger_1.Logger.succeedSpinner("Services stopped successfully");
}
catch (error) {
logger_1.Logger.failSpinner("Failed to stop services");
throw error;
}
}
async restartServices() {
logger_1.Logger.startSpinner("Restarting services...");
try {
await this.stopServices();
await this.startServices();
logger_1.Logger.succeedSpinner("Services restarted successfully");
}
catch (error) {
logger_1.Logger.failSpinner("Failed to restart services");
throw error;
}
}
async resetServices() {
logger_1.Logger.startSpinner("Resetting services and data...");
try {
const cmd = [
...this.getComposeCommand(),
"down",
"-v",
"--remove-orphans",
];
await this.execCommand(cmd);
logger_1.Logger.succeedSpinner("Services reset successfully");
}
catch (error) {
logger_1.Logger.failSpinner("Failed to reset services");
throw error;
}
}
async getStatus() {
try {
const cmd = [...this.getComposeCommand(), "ps", "--format", "json"];
const { stdout } = await this.execCommand(cmd);
if (!stdout.trim()) {
return [];
}
// Parse each line as separate JSON object
const lines = stdout.trim().split("\n");
const containers = [];
for (const line of lines) {
try {
const container = JSON.parse(line);
containers.push({
name: container.Name || container.Service,
status: container.State || container.Status,
ports: container.Publishers?.map((p) => `${p.PublishedPort}:${p.TargetPort}`).join(", ") || "",
image: container.Image,
});
}
catch {
// Skip invalid JSON lines
}
}
return containers;
}
catch (error) {
logger_1.Logger.debug(`Failed to get container status: ${error}`);
return [];
}
}
async getLogs(service, follow = false) {
try {
const cmd = [...this.getComposeCommand(), "logs"];
if (follow) {
cmd.push("-f");
}
if (service) {
cmd.push(service);
}
if (follow) {
// For follow mode, use spawn to stream output
const child = (0, child_process_1.spawn)(cmd[0], cmd.slice(1), {
stdio: "inherit",
env: this.getEnvVars(),
cwd: this.projectPath,
});
// Handle process termination
process.on("SIGINT", () => {
child.kill("SIGINT");
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0) {
resolve();
}
else {
reject(new Error(`Logs command failed with code ${code}`));
}
});
});
}
else {
const { stdout } = await this.execCommand(cmd);
console.log(stdout);
}
}
catch (error) {
throw new Error(`Failed to get logs: ${error}`);
}
}
async checkHealth() {
try {
const containers = await this.getStatus();
const backend = containers.some((c) => c.name.includes("backend") && c.status.includes("running"));
const postgres = containers.some((c) => c.name.includes("postgres") && c.status.includes("running"));
return { backend, postgres };
}
catch {
return { backend: false, postgres: false };
}
}
async waitForServices(timeout = 30000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const health = await this.checkHealth();
if (health.backend && health.postgres) {
logger_1.Logger.debug("Services are healthy");
return;
}
logger_1.Logger.debug("Waiting for services to be ready...");
await new Promise((resolve) => setTimeout(resolve, 2000));
}
throw new Error("Services failed to start within timeout period");
}
async execCommand(cmd) {
const command = cmd.join(" ");
logger_1.Logger.debug(`Executing: ${command}`);
try {
const result = await execAsync(command, {
env: this.getEnvVars(),
cwd: this.projectPath,
});
logger_1.Logger.debug(`Command output: ${result.stdout}`);
return result;
}
catch (error) {
logger_1.Logger.debug(`Command failed: ${error.message}`);
throw new Error(`Docker command failed: ${error.message}`);
}
}
}
exports.DockerManager = DockerManager;
//# sourceMappingURL=docker.js.map