mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-27 18:57:09 +00:00
fix(fleet): scope Stop-by-label to stack labels with a typed suggestion source (#1368)
* fix(fleet): scope Stop-by-label to stack labels with a typed suggestion source The Fleet Actions "Stop by label" card labelled its target field generically as "Label", so a same-named node label could look like a valid stop target in a destructive workflow. The action has always matched stack labels only, but nothing in the copy or the data flow made that explicit. Add a stack-label-only suggestions endpoint and make the scope unmistakable: - New GET /api/fleet/labels/suggestions aggregates the per-node stack labels into a name-keyed list with stack and node counts (admin-only, central DB, covers every configured node including offline remotes). Node labels are never folded in. - The card now sources its autocomplete from that endpoint and renders each suggestion with its stack and node counts via a typed FleetStopLabelSuggestion model, so node-label data cannot be fed into this destructive card. - Copy is explicit throughout: "Stack label" target field with a helper line that node labels are not used, a clear "0 matching stacks" readout and a "No stacks are assigned to this stack label" empty preview, and confirm and result copy that references stacks and the stack label. - Docs updated (fleet-actions, stack-labels) and tests added on both sides, including node-only exclusion, name collision, multi-node counts, the zero-stack preview, and the non-fatal suggestions-load path. * docs: correct stale Stop-by-label button and helper references The Stop-by-label walkthrough referenced a "Stop matching stacks" button and a warning callout that no longer exist on the card. Align the docs with the live card: the primary action is "Stop fleet", and the scope is stated by the helper line under the input.
This commit is contained in:
@@ -1570,6 +1570,38 @@ fleetRouter.post('/labels/match-preview', authMiddleware, async (req: Request, r
|
||||
}
|
||||
});
|
||||
|
||||
// Stack-label suggestions for the Stop-by-label target picker. Aggregates the
|
||||
// per-node stack-label rows (the `stack_labels` table, via getLabels) into one
|
||||
// name-keyed list with stack/node counts. This is the only source the card's
|
||||
// autocomplete consumes; node labels (the separate `/api/node-labels`
|
||||
// namespace) are deliberately never folded in, because fleet-stop targets stack
|
||||
// labels only. The `scope: 'stack'` tag and the counts make that explicit at the
|
||||
// type level and in the UI. Central-DB only, same as match-preview, so it covers
|
||||
// every configured node including offline remotes.
|
||||
fleetRouter.get('/labels/suggestions', authMiddleware, async (req: Request, res: Response): Promise<void> => {
|
||||
if (!requireAdmin(req, res)) return;
|
||||
try {
|
||||
const db = DatabaseService.getInstance();
|
||||
const agg = new Map<string, { nodeCount: number; stackCount: number }>();
|
||||
for (const node of db.getNodes()) {
|
||||
for (const label of db.getLabels(node.id)) {
|
||||
const stackCount = db.getStacksForLabel(label.id, node.id).length;
|
||||
const entry = agg.get(label.name) ?? { nodeCount: 0, stackCount: 0 };
|
||||
entry.nodeCount += 1;
|
||||
entry.stackCount += stackCount;
|
||||
agg.set(label.name, entry);
|
||||
}
|
||||
}
|
||||
const suggestions = Array.from(agg.entries())
|
||||
.map(([name, counts]) => ({ name, scope: 'stack' as const, nodeCount: counts.nodeCount, stackCount: counts.stackCount }))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
res.json({ suggestions });
|
||||
} catch (error) {
|
||||
console.error('[Fleet] label-suggestions error:', error);
|
||||
res.status(500).json({ error: getErrorMessage(error, 'Failed to load stack-label suggestions') });
|
||||
}
|
||||
});
|
||||
|
||||
// Fleet-wide prune size estimate. Local node uses the controller estimate
|
||||
// helper; remote nodes hit `/api/system/prune/estimate` per target. Same
|
||||
// fan-out shape as `/labels/fleet-prune` minus the locks (estimation is read
|
||||
|
||||
Reference in New Issue
Block a user