feat(stacks): per-service start/stop/restart lifecycle actions (#778)

* feat(stacks): add per-service start/stop/restart lifecycle routes

Adds POST /:stackName/services/:serviceName/{start,stop,restart} routes
that operate on containers belonging to a single Compose service, using
the same Engine API pattern as the existing stack-level lifecycle routes.
Includes isValidServiceName validator and audit-summary entries for the
three new paths.

* test(stacks): add per-service action route tests

* test(stacks): fix test quality issues in service action tests

* feat(stacks): add per-service lifecycle menu to container cards

* fix(stacks): handle paused container state in service action menu

* docs(stacks): add per-service lifecycle actions documentation

* docs(stacks): add validation screenshots for per-service lifecycle actions
This commit is contained in:
Anso
2026-04-25 17:26:04 -04:00
committed by GitHub
parent abee078741
commit 6986b927e3
10 changed files with 449 additions and 8 deletions
+6
View File
@@ -18,6 +18,12 @@ export const AUDIT_ROUTE_SUMMARIES: Record<string, string> = {
'POST /stacks/*/start': 'Started stack',
'POST /stacks/*/stop': 'Stopped stack',
'POST /stacks/*/restart': 'Restarted stack',
// Per-service lifecycle (resourceName = stack name; service name is in the path column)
'POST /stacks/*/services/*/start': 'Started stack service',
'POST /stacks/*/services/*/stop': 'Stopped stack service',
'POST /stacks/*/services/*/restart': 'Restarted stack service',
'POST /stacks/*/update': 'Updated stack images',
'POST /stacks/*/rollback': 'Rolled back stack',
+7
View File
@@ -72,6 +72,13 @@ export function isValidDockerResourceId(id: string): boolean {
return /^[a-f0-9]{12,64}$/i.test(id);
}
/**
* Compose service name. Allows dots in addition to the stack-name set
* (Compose spec permits `my.service`).
*/
export const isValidServiceName = (name: string): boolean =>
/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(name);
/**
* Asserts that a resolved file path stays within a given base directory.
* Returns true if the path is safe, false if it escapes the base.