feat(stacks): state-aware sidebar context menu and Open App action (#368)

* feat(stacks): state-aware sidebar context menu and Open App action

- Context menu now adapts to stack state: running stacks show
  Stop/Restart/Update, stopped stacks show Deploy only
- Added "Open App" shortcut to open a stack's web UI directly
  from the sidebar (visible when running with a published port)
- Backend bulk status endpoint enriched with mainPort detection
- Reduced manual image update check cooldown from 10 to 2 minutes
- Rate limit error message now derives from the configured constant

* fix(stacks): use const for bulkPorts (prefer-const lint)
This commit is contained in:
Anso
2026-04-03 21:41:01 -04:00
committed by GitHub
parent f0d67a83a0
commit 55d3b8ca1d
9 changed files with 217 additions and 57 deletions
+5 -4
View File
@@ -2936,12 +2936,12 @@ app.get('/api/stacks/statuses', async (req: Request, res: Response) => {
const stacks = await FileSystemService.getInstance(req.nodeId).getStacks();
const stackNames = stacks.map((s: string) => s.replace(/\.(yml|yaml)$/, ''));
const dockerController = DockerController.getInstance(req.nodeId);
const statuses = await dockerController.getBulkStackStatuses(stackNames);
const bulkInfo = await dockerController.getBulkStackStatuses(stackNames);
// Map back to filenames to match frontend expectations
const result: Record<string, 'running' | 'exited' | 'unknown'> = {};
const result: Record<string, { status: 'running' | 'exited' | 'unknown'; mainPort?: number }> = {};
for (const stack of stacks) {
const name = stack.replace(/\.(yml|yaml)$/, '');
result[stack] = statuses[name] ?? 'unknown';
result[stack] = bulkInfo[name] ?? { status: 'unknown' };
}
res.json(result);
} catch (error) {
@@ -5203,7 +5203,8 @@ app.post('/api/image-updates/refresh', authMiddleware, (_req: Request, res: Resp
try {
const triggered = ImageUpdateService.getInstance().triggerManualRefresh();
if (!triggered) {
res.status(429).json({ error: 'Rate limited. Please wait at least 10 minutes between manual refreshes.' });
const mins = ImageUpdateService.manualCooldownMinutes;
res.status(429).json({ error: `Rate limited. Please wait at least ${mins} minute${mins !== 1 ? 's' : ''} between manual refreshes.` });
return;
}
res.json({ success: true, message: 'Image update check started in background.' });