mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-27 20:29:10 +00:00
Merge pull request #61 from AnsoCode/fix/managed-container-classification-by-working-dir
fix(stats): classify managed containers by working_dir instead of project name
This commit is contained in:
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- **Managed container count wrong when stacks launched from COMPOSE_DIR root:** The `GET /api/stats` endpoint classified containers as managed by matching `com.docker.compose.project` against known stack directory names. When stacks are launched from the COMPOSE_DIR root (rather than each subdirectory), Docker assigns the root folder name as the project name for all of them — causing every such container to appear as "external". Fixed by classifying via `com.docker.compose.project.working_dir`: a container is managed if its working directory falls within COMPOSE_DIR, regardless of the project name.
|
||||
- **Blank page on HTTP deployments (root cause — Helmet 8 default CSP):** Helmet 8 merges custom `directives` with its built-in defaults, which include `upgrade-insecure-requests`. The previous fix (PR #59) omitted the directive from the custom object, but Helmet silently re-added it from defaults. The correct fix is `upgradeInsecureRequests: null`, which is the Helmet 8 API for explicitly removing a default directive.
|
||||
- **Login loop caused by remote node auth failure:** When a configured remote node had an expired or invalid API token, every proxied request to that node returned 401. Both `apiFetch` and `fetchForNode` treated any 401 as a user session failure and dispatched `sencho-unauthorized`, immediately logging the user out and sending them back to the login page — even after a successful login. Fixed by adding a `proxyRes` handler that stamps all remote-proxied responses with `x-sencho-proxy: 1`; the frontend now only fires `sencho-unauthorized` when this header is absent (i.e., it's a genuine local session failure).
|
||||
- **Missing `authMiddleware` on notifications endpoints:** `GET /api/notifications`, `POST /api/notifications/read`, `DELETE /api/notifications/:id`, `DELETE /api/notifications`, and `POST /api/notifications/test` were all missing `authMiddleware`, violating the default-deny policy. Added to all five.
|
||||
|
||||
+15
-14
@@ -1156,24 +1156,25 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
||||
// Get all containers stats for dashboard
|
||||
app.get('/api/stats', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const [dockerController, knownStacks] = [
|
||||
DockerController.getInstance(req.nodeId),
|
||||
await FileSystemService.getInstance(req.nodeId).getStacks(),
|
||||
];
|
||||
const allContainers = await dockerController.getAllContainers();
|
||||
const knownSet = new Set(knownStacks);
|
||||
const composeDir = path.resolve(NodeRegistry.getInstance().getComposeDir(req.nodeId));
|
||||
const allContainers = await DockerController.getInstance(req.nodeId).getAllContainers();
|
||||
|
||||
// A container is "managed" if Docker started it from within COMPOSE_DIR.
|
||||
// We use com.docker.compose.project.working_dir rather than project name because
|
||||
// stacks launched from the COMPOSE_DIR root (not a subdirectory) all share the
|
||||
// project name of the root folder — causing false "external" classification.
|
||||
const isManagedByComposeDir = (c: any): boolean => {
|
||||
const workingDir: string | undefined = c.Labels?.['com.docker.compose.project.working_dir'];
|
||||
if (!workingDir) return false;
|
||||
const resolved = path.resolve(workingDir);
|
||||
return resolved === composeDir || resolved.startsWith(composeDir + path.sep);
|
||||
};
|
||||
|
||||
const active = allContainers.filter((c: any) => c.State === 'running').length;
|
||||
const exited = allContainers.filter((c: any) => c.State === 'exited').length;
|
||||
const total = allContainers.length;
|
||||
const managed = allContainers.filter((c: any) => {
|
||||
const project: string | undefined = c.Labels?.['com.docker.compose.project'];
|
||||
return project && knownSet.has(project) && c.State === 'running';
|
||||
}).length;
|
||||
const unmanaged = allContainers.filter((c: any) => {
|
||||
const project: string | undefined = c.Labels?.['com.docker.compose.project'];
|
||||
return project && !knownSet.has(project) && c.State === 'running';
|
||||
}).length;
|
||||
const managed = allContainers.filter((c: any) => c.State === 'running' && isManagedByComposeDir(c)).length;
|
||||
const unmanaged = allContainers.filter((c: any) => c.State === 'running' && !isManagedByComposeDir(c)).length;
|
||||
|
||||
res.json({ active, managed, unmanaged, exited, total });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user