forge-deploy-cli
Version:
Professional CLI for local deployments with automatic subdomain routing, SSL certificates, and infrastructure management
215 lines • 10.6 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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.logsCommand = void 0;
const commander_1 = require("commander");
const chalk_1 = __importDefault(require("chalk"));
const api_1 = require("../services/api");
const config_1 = require("../services/config");
const localDeployment_1 = require("../services/localDeployment");
const child_process_1 = require("child_process");
const path_1 = __importDefault(require("path"));
exports.logsCommand = new commander_1.Command('logs')
.description('View application logs')
.argument('[deployment-id]', 'Deployment ID (optional, defaults to current project)')
.option('-f, --follow', 'Follow log output')
.option('-t, --tail <lines>', 'Number of lines to show from the end', '100')
.option('--error', 'Show only error logs')
.option('--access', 'Show only access logs')
.option('--local', 'Show only local PM2 logs')
.option('--remote', 'Show only remote API logs')
.action(async (deploymentId, options) => {
try {
const configService = new config_1.ConfigService();
const config = await configService.loadProjectConfig();
if (!config) {
console.log(chalk_1.default.red('Error: Not in a Forge project directory'));
console.log('Run "forge init" to initialize a project');
process.exit(1);
}
const globalConfig = await configService.loadGlobalConfig();
if (!globalConfig?.apiKey) {
console.log(chalk_1.default.red('Error: Not authenticated'));
console.log('Run "forge login" to authenticate');
process.exit(1);
}
console.log(chalk_1.default.blue('Fetching logs...'));
const api = new api_1.ForgeApiService();
api.setApiKey(globalConfig.apiKey);
const logOptions = {
tail: parseInt(options.tail),
follow: options.follow,
type: options.error ? 'error' : options.access ? 'access' : 'all'
};
if (options.follow) {
console.log(chalk_1.default.yellow('Following logs (Press Ctrl+C to stop)...'));
console.log(chalk_1.default.gray('---'));
// Simulate log streaming (in a real implementation, this would be a WebSocket or SSE connection)
const interval = setInterval(async () => {
try {
if (config.deploymentId) {
const response = await api.getDeploymentLogs(config.deploymentId);
const logs = response.data?.logs || [];
logs.slice(-parseInt(options.tail)).forEach((log) => {
const timestamp = new Date(log.timestamp).toISOString();
const level = log.level === 'error' ? chalk_1.default.red(log.level) :
log.level === 'warn' ? chalk_1.default.yellow(log.level) :
chalk_1.default.white(log.level);
console.log(`${chalk_1.default.gray(timestamp)} ${level} ${log.message}`);
});
}
}
catch (error) {
console.log(chalk_1.default.red(`Error fetching logs: ${error}`));
clearInterval(interval);
}
}, 2000);
process.on('SIGINT', () => {
clearInterval(interval);
console.log(chalk_1.default.yellow('\nStopped following logs'));
process.exit(0);
});
}
else {
if (!config.deploymentId) {
console.log(chalk_1.default.yellow('No deployment found. Deploy your app first with "forge deploy"'));
return;
}
// Show both local PM2 logs and remote API logs
if (!options.remote) {
console.log(chalk_1.default.blue('📁 Local PM2 Logs:'));
await showLocalLogs(config.deploymentId, options);
}
if (!options.local) {
console.log(chalk_1.default.blue('🌐 Remote API Logs:'));
try {
const response = await api.getDeploymentLogs(config.deploymentId);
const logs = response.data?.logs || [];
if (logs.length === 0) {
console.log(chalk_1.default.yellow('No remote logs found'));
}
else {
const filteredLogs = logs.slice(-parseInt(options.tail));
console.log(chalk_1.default.gray('---'));
filteredLogs.forEach((log) => {
const timestamp = new Date(log.timestamp).toISOString();
const level = log.level === 'error' ? chalk_1.default.red(log.level) :
log.level === 'warn' ? chalk_1.default.yellow(log.level) :
chalk_1.default.white(log.level);
console.log(`${chalk_1.default.gray(timestamp)} ${level} ${log.message}`);
});
console.log(chalk_1.default.gray('---'));
console.log(chalk_1.default.green(`Showing last ${filteredLogs.length} remote log entries`));
}
}
catch (apiError) {
console.log(chalk_1.default.yellow(`⚠️ Remote logs unavailable: ${apiError}`));
console.log(chalk_1.default.gray('API might be down or deployment not found remotely'));
if (options.local) {
console.log(chalk_1.default.gray('Showing local logs only'));
}
}
}
}
}
catch (error) {
console.log(chalk_1.default.red(`Error: ${error}`));
process.exit(1);
}
});
async function showLocalLogs(deploymentId, options) {
try {
// Check if deployment exists locally
const deployment = await localDeployment_1.LocalDeploymentManager.getDeployment(deploymentId);
if (!deployment) {
console.log(chalk_1.default.yellow('Local deployment not found'));
return;
}
const appName = `forge-${deploymentId}`;
const logTail = options.tail || '100';
try {
// Try to get PM2 logs
console.log(chalk_1.default.gray(`PM2 Process: ${appName}`));
if (options.error) {
// Show error logs only
const errorLogs = (0, child_process_1.execSync)(`pm2 logs ${appName} --err --lines ${logTail}`, { encoding: 'utf8' });
console.log(errorLogs);
}
else {
// Show all logs
const allLogs = (0, child_process_1.execSync)(`pm2 logs ${appName} --lines ${logTail}`, { encoding: 'utf8' });
console.log(allLogs);
}
}
catch (pm2Error) {
// PM2 logs failed, try to show log files directly
console.log(chalk_1.default.yellow('PM2 logs not available, checking log files...'));
const logDir = path_1.default.join(process.cwd(), 'logs');
const errorFile = path_1.default.join(logDir, `${deploymentId}-error.log`);
const outFile = path_1.default.join(logDir, `${deploymentId}-out.log`);
const combinedFile = path_1.default.join(logDir, `${deploymentId}-combined.log`);
const fs = await Promise.resolve().then(() => __importStar(require('fs-extra')));
if (await fs.pathExists(combinedFile)) {
console.log(chalk_1.default.gray(`Reading log file: ${combinedFile}`));
const logs = await fs.readFile(combinedFile, 'utf8');
const lines = logs.split('\n').slice(-parseInt(logTail));
console.log(lines.join('\n'));
}
else if (await fs.pathExists(outFile)) {
console.log(chalk_1.default.gray(`Reading output log: ${outFile}`));
const logs = await fs.readFile(outFile, 'utf8');
const lines = logs.split('\n').slice(-parseInt(logTail));
console.log(lines.join('\n'));
if (await fs.pathExists(errorFile)) {
console.log(chalk_1.default.red('\nError Logs:'));
const errorLogs = await fs.readFile(errorFile, 'utf8');
const errorLines = errorLogs.split('\n').slice(-parseInt(logTail));
console.log(errorLines.join('\n'));
}
}
else {
console.log(chalk_1.default.yellow('No local log files found'));
console.log(chalk_1.default.gray(`Expected log files in: ${logDir}`));
}
}
}
catch (error) {
console.log(chalk_1.default.red(`Error reading local logs: ${error}`));
}
}
//# sourceMappingURL=logs.js.map