mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-09 18:32:52 +00:00
chore(stacks): gate informational console.log behind developer_mode (#1194)
Rebased onto current main (post H-1 / H-2 / L-3 / M-2 / M-6 merges).
Same intent as the original M-5 commit:
- Local dlog() helper in routes/stacks.ts wrapping console.log
behind isDebugEnabled().
- All informational console.log in stacks.ts replaced with dlog().
- The 3 Exec session-lifecycle console.log in DockerController.ts
wrapped with inline if (isDebugEnabled()) gates.
console.warn and console.error remain unconditional everywhere.
Resolves M-5 from the stack-management audit.
This commit is contained in:
@@ -30,6 +30,15 @@ import { getTerminalWs } from '../websocket/generic';
|
||||
// adversarial) file cannot exhaust heap during an env or service lookup.
|
||||
const MAX_COMPOSE_PARSE_BYTES = 1_048_576; // 1 MiB
|
||||
|
||||
/**
|
||||
* Diagnostic-mode log helper. Wraps console.log so non-essential informational
|
||||
* lines (deploys completed, files saved, stacks created) stay silent in
|
||||
* production and only appear when developer_mode is on. console.warn and
|
||||
* console.error remain unconditional - they are operational signal, not noise.
|
||||
*/
|
||||
function dlog(...args: Parameters<typeof console.log>): void {
|
||||
if (isDebugEnabled()) console.log(...args);
|
||||
}
|
||||
function notifyActionFailure(action: string, stackName: string, error: unknown): void {
|
||||
const message = getErrorMessage(error, `Failed to ${action} stack`);
|
||||
NotificationService.getInstance()
|
||||
@@ -480,7 +489,7 @@ stacksRouter.put('/:stackName', async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Compose file saved: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Compose file saved: ${sanitizeForLog(stackName)}`);
|
||||
res.setHeader('ETag', stackFileEtag(result.mtimeMs));
|
||||
res.json({ message: 'Stack saved successfully', mtimeMs: result.mtimeMs });
|
||||
} catch (error) {
|
||||
@@ -602,7 +611,7 @@ stacksRouter.put('/:stackName/env', async (req: Request, res: Response) => {
|
||||
}
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
const envFileName = path.basename(envPath);
|
||||
console.log(`[Stacks] Env file saved: ${sanitizeForLog(stackName)}/${sanitizeForLog(envFileName)}`);
|
||||
dlog(`[Stacks] Env file saved: ${sanitizeForLog(stackName)}/${sanitizeForLog(envFileName)}`);
|
||||
res.setHeader('ETag', stackFileEtag(result.mtimeMs));
|
||||
res.json({ message: 'Env file saved successfully', mtimeMs: result.mtimeMs });
|
||||
} catch (error) {
|
||||
@@ -623,7 +632,7 @@ stacksRouter.post('/', async (req: Request, res: Response) => {
|
||||
}
|
||||
await FileSystemService.getInstance(req.nodeId).createStack(stackName);
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Stack created: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Stack created: ${sanitizeForLog(stackName)}`);
|
||||
res.json({ message: 'Stack created successfully', name: stackName });
|
||||
} catch (error: unknown) {
|
||||
const message = getErrorMessage(error, '');
|
||||
@@ -721,7 +730,7 @@ stacksRouter.post('/from-git', async (req: Request, res: Response) => {
|
||||
: null;
|
||||
|
||||
if (fromGitDiag) {
|
||||
console.log(
|
||||
dlog(
|
||||
`[Stacks:diag] from-git start stack=${sanitizeForLog(stack_name)} nodeId=${req.nodeId ?? 'local'} host=${sanitizeForLog(gitRepoHost(repo_url))} branch=${sanitizeForLog(branch)} composePath=${sanitizeForLog(compose_path)} envPath=${sanitizeForLog(resolvedEnvPath ?? 'none')} authType=${sanitizeForLog(resolvedAuthType)} autoApplyOnWebhook=${autoApplyOnWebhook} autoDeployOnApply=${autoDeployOnApply} deployNow=${deploy_now === true}`
|
||||
);
|
||||
}
|
||||
@@ -763,9 +772,9 @@ stacksRouter.post('/from-git', async (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[Stacks] Stack created from Git: ${sanitizeForLog(stack_name)} at ${result.commitSha.slice(0, 7)}`);
|
||||
dlog(`[Stacks] Stack created from Git: ${sanitizeForLog(stack_name)} at ${result.commitSha.slice(0, 7)}`);
|
||||
if (fromGitDiag) {
|
||||
console.log(
|
||||
dlog(
|
||||
`[Stacks:diag] from-git ok stack=${sanitizeForLog(stack_name)} sha=${result.commitSha.slice(0, 7)} deployed=${deployed} envWritten=${result.envWritten} warnings=${result.warnings.length} elapsedMs=${Date.now() - fromGitStartedAt}`
|
||||
);
|
||||
}
|
||||
@@ -786,7 +795,7 @@ stacksRouter.post('/from-git', async (req: Request, res: Response) => {
|
||||
} catch (error) {
|
||||
if (fromGitDiag) {
|
||||
const code = error instanceof GitSourceError ? error.code : 'UNKNOWN';
|
||||
console.log(
|
||||
dlog(
|
||||
`[Stacks:diag] from-git fail stack=${sanitizeForLog(fromGitStackName)} code=${code} elapsedMs=${Date.now() - fromGitStartedAt}`
|
||||
);
|
||||
}
|
||||
@@ -816,7 +825,7 @@ stacksRouter.delete('/:stackName', async (req: Request, res: Response) => {
|
||||
if (pruneVolumes) {
|
||||
try {
|
||||
const result = await DockerController.getInstance().pruneManagedOnly('volumes', [stackName]);
|
||||
console.log(`[Stacks] Pruned volumes for ${sanitizeForLog(stackName)}: ${result.reclaimedBytes} bytes reclaimed`);
|
||||
dlog(`[Stacks] Pruned volumes for ${sanitizeForLog(stackName)}: ${result.reclaimedBytes} bytes reclaimed`);
|
||||
} catch (pruneErr) {
|
||||
console.warn('[Stacks] Volume prune failed for %s, continuing delete:', sanitizeForLog(stackName), pruneErr);
|
||||
}
|
||||
@@ -873,7 +882,7 @@ stacksRouter.delete('/:stackName', async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Stack deleted: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Stack deleted: ${sanitizeForLog(stackName)}`);
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
@@ -930,7 +939,7 @@ stacksRouter.post('/:stackName/deploy', async (req: Request, res: Response) => {
|
||||
const t0 = Date.now();
|
||||
await ComposeService.getInstance(req.nodeId).deployStack(stackName, getTerminalWs(), atomic);
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Deploy completed: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Deploy completed: ${sanitizeForLog(stackName)}`);
|
||||
if (debug) console.debug(`[Stacks:debug] Deploy finished in ${Date.now() - t0}ms`);
|
||||
res.json({ message: 'Deployed successfully' });
|
||||
notifyActionSuccess('deploy_success', `${stackName} deployed`, stackName, req.user?.username ?? 'system');
|
||||
@@ -972,7 +981,7 @@ stacksRouter.post('/:stackName/down', async (req: Request, res: Response) => {
|
||||
if (isDebugEnabled()) console.debug(`[Stacks:debug] Down starting`, { stackName: sanitizeForLog(stackName), nodeId: req.nodeId });
|
||||
await ComposeService.getInstance(req.nodeId).runCommand(stackName, 'down', getTerminalWs());
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Down completed: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Down completed: ${sanitizeForLog(stackName)}`);
|
||||
res.json({ status: 'Command started' });
|
||||
} catch (error: unknown) {
|
||||
console.error('[Stacks] Down failed: %s', sanitizeForLog(stackName), error);
|
||||
@@ -1080,7 +1089,7 @@ async function bulkContainerOp(
|
||||
}
|
||||
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] ${titleCase} completed: ${sanitizeForLog(stackName)} (${outcome.count} containers)`);
|
||||
dlog(`[Stacks] ${titleCase} completed: ${sanitizeForLog(stackName)} (${outcome.count} containers)`);
|
||||
res.json({ success: true, message: `${titleCase} completed via Engine API.` });
|
||||
const { category, pastTense } = CONTAINER_ACTION_META[action];
|
||||
notifyActionSuccess(category, `${stackName} ${pastTense}`, stackName, req.user?.username ?? 'system');
|
||||
@@ -1132,7 +1141,7 @@ async function handleServiceAction(
|
||||
: (id: string) => dockerController.restartContainer(id);
|
||||
await Promise.all(matching.map(c => op(c.Id)));
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(
|
||||
dlog(
|
||||
`[Stacks] Service ${sanitizeForLog(action)} completed: ${sanitizeForLog(stackName)}/${sanitizeForLog(serviceName)} (${matching.length} containers)`,
|
||||
);
|
||||
res.json({
|
||||
@@ -1188,7 +1197,7 @@ stacksRouter.post('/:stackName/update', async (req: Request, res: Response) => {
|
||||
action: 'stack-updated',
|
||||
ts: Date.now(),
|
||||
});
|
||||
console.log(`[Stacks] Update completed: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Update completed: ${sanitizeForLog(stackName)}`);
|
||||
if (debug) console.debug(`[Stacks:debug] Update finished in ${Date.now() - t0}ms`);
|
||||
res.json({ status: 'Update completed' });
|
||||
notifyActionSuccess('image_update_applied', `${stackName} updated`, stackName, req.user?.username ?? 'system');
|
||||
@@ -1229,12 +1238,12 @@ stacksRouter.post('/:stackName/rollback', async (req: Request, res: Response) =>
|
||||
if (!backupInfo.exists) {
|
||||
return res.status(404).json({ error: 'No backup available for this stack.' });
|
||||
}
|
||||
console.log(`[Stacks] Rollback initiated: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Rollback initiated: ${sanitizeForLog(stackName)}`);
|
||||
await fsSvc.restoreStackFiles(stackName);
|
||||
if (!(await runPolicyGate(req, res, stackName, req.nodeId))) return;
|
||||
await ComposeService.getInstance(req.nodeId).deployStack(stackName, getTerminalWs(), false);
|
||||
invalidateNodeCaches(req.nodeId);
|
||||
console.log(`[Stacks] Rollback completed: ${sanitizeForLog(stackName)}`);
|
||||
dlog(`[Stacks] Rollback completed: ${sanitizeForLog(stackName)}`);
|
||||
res.json({ message: 'Stack rolled back successfully.' });
|
||||
} catch (error: unknown) {
|
||||
console.error('[Stacks] Rollback failed: %s', sanitizeForLog(stackName), error);
|
||||
|
||||
@@ -1438,7 +1438,7 @@ class DockerController {
|
||||
}
|
||||
|
||||
if (isDebugEnabled()) console.debug('[Exec:diag] Creating exec', { containerId, shell: shellType });
|
||||
console.log('[Exec] Shell session started', { containerId, shell: shellType });
|
||||
if (isDebugEnabled()) console.log('[Exec] Shell session started', { containerId, shell: shellType });
|
||||
|
||||
// --- Downstream: container output → client ---
|
||||
stream.on('data', (chunk: Buffer) => {
|
||||
@@ -1452,7 +1452,7 @@ class DockerController {
|
||||
});
|
||||
|
||||
stream.on('end', () => {
|
||||
console.log('[Exec] Shell session ended', { containerId, reason: 'stream-end' });
|
||||
if (isDebugEnabled()) console.log('[Exec] Shell session ended', { containerId, reason: 'stream-end' });
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.close();
|
||||
}
|
||||
@@ -1492,7 +1492,7 @@ class DockerController {
|
||||
|
||||
// --- Cleanup: prevent zombie processes ---
|
||||
ws.on('close', () => {
|
||||
console.log('[Exec] Shell session ended', { containerId, reason: 'ws-close' });
|
||||
if (isDebugEnabled()) console.log('[Exec] Shell session ended', { containerId, reason: 'ws-close' });
|
||||
try {
|
||||
stream.destroy();
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user