From ae8e03315270a1570950541fd9e6464d766ddebe Mon Sep 17 00:00:00 2001 From: tristan Date: Mon, 6 Apr 2026 16:05:04 +0200 Subject: [PATCH] docs : plan d'implementation phase 2b dashboard sante Co-Authored-By: Claude Opus 4.6 (1M context) --- .../2026-04-06-phase2b-dashboard-sante.md | 837 ++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-06-phase2b-dashboard-sante.md diff --git a/docs/superpowers/plans/2026-04-06-phase2b-dashboard-sante.md b/docs/superpowers/plans/2026-04-06-phase2b-dashboard-sante.md new file mode 100644 index 0000000..610ecc8 --- /dev/null +++ b/docs/superpowers/plans/2026-04-06-phase2b-dashboard-sante.md @@ -0,0 +1,837 @@ +# Phase 2b — Dashboard Sante Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a dashboard showing container health status (running/stopped) and version for all environments, plus detailed metrics (CPU, memory, uptime) on the application detail page. + +**Architecture:** A DockerService executes Docker CLI commands via Process to get container status and stats. Two new API Platform endpoints expose dashboard overview and per-environment health. Frontend adds a new dashboard page and enriches the detail page. + +**Tech Stack:** PHP 8.4, Symfony 8, API Platform 4, Symfony Process, Docker CLI, Nuxt 4, Vue 3 + +--- + +## File Structure + +### Backend — Create + +| File | Responsibility | +|------|---------------| +| `src/Service/DockerService.php` | Execute Docker CLI commands via Process | +| `src/ApiResource/Dashboard.php` | API Platform DTO for dashboard response | +| `src/ApiResource/EnvironmentHealth.php` | API Platform DTO for env health response | +| `src/State/DashboardProvider.php` | Provider that aggregates all apps + container status | +| `src/State/EnvironmentHealthProvider.php` | Provider for single env detailed health | + +### Frontend — Create + +| File | Responsibility | +|------|---------------| +| `frontend/services/dashboard.ts` | API calls for dashboard and env health | +| `frontend/services/dto/dashboard.ts` | TypeScript types | +| `frontend/pages/dashboard.vue` | Dashboard page | + +### Frontend — Modify + +| File | Change | +|------|--------| +| `frontend/pages/applications/[slug].vue` | Add health metrics block per env | +| `frontend/layouts/default.vue` | Add Dashboard sidebar link | +| `frontend/middleware/auth.global.ts` | Redirect `/` to `/dashboard` instead of `/applications` | +| `frontend/i18n/locales/fr.json` | Dashboard translation keys | + +--- + +## Task 1: DockerService + +**Files:** +- Create: `src/Service/DockerService.php` + +- [ ] **Step 1: Create the service** + +```php +setTimeout(10); + $process->run(); + + if (!$process->isSuccessful()) { + return [ + 'status' => 'not_found', + 'image' => '', + 'version' => '', + 'startedAt' => '', + ]; + } + + $parts = explode('||', trim($process->getOutput())); + + if (\count($parts) < 3) { + return [ + 'status' => 'not_found', + 'image' => '', + 'version' => '', + 'startedAt' => '', + ]; + } + + $image = $parts[1]; + $version = 'latest'; + if (str_contains($image, ':')) { + $version = substr($image, strrpos($image, ':') + 1); + } + + return [ + 'status' => $parts[0], + 'image' => $image, + 'version' => $version, + 'startedAt' => $parts[2], + ]; + } + + /** + * @return array{cpuPercent: float, memoryUsage: string, memoryLimit: string, memoryPercent: float} + */ + public function getContainerStats(string $containerName): array + { + $process = new Process([ + 'docker', 'stats', '--no-stream', + '--format', '{{.CPUPerc}}||{{.MemUsage}}||{{.MemPerc}}', + $containerName, + ]); + $process->setTimeout(10); + $process->run(); + + if (!$process->isSuccessful()) { + return [ + 'cpuPercent' => 0.0, + 'memoryUsage' => '', + 'memoryLimit' => '', + 'memoryPercent' => 0.0, + ]; + } + + $parts = explode('||', trim($process->getOutput())); + + if (\count($parts) < 3) { + return [ + 'cpuPercent' => 0.0, + 'memoryUsage' => '', + 'memoryLimit' => '', + 'memoryPercent' => 0.0, + ]; + } + + $memParts = explode(' / ', $parts[1]); + + return [ + 'cpuPercent' => (float) rtrim($parts[0], '%'), + 'memoryUsage' => $memParts[0] ?? '', + 'memoryLimit' => $memParts[1] ?? '', + 'memoryPercent' => (float) rtrim($parts[2], '%'), + ]; + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/Service/DockerService.php +git commit -m "feat : add DockerService for container status and stats" +``` + +--- + +## Task 2: API Platform DTOs + +**Files:** +- Create: `src/ApiResource/Dashboard.php` +- Create: `src/ApiResource/EnvironmentHealth.php` + +- [ ] **Step 1: Create Dashboard DTO** + +```php +}> */ + public array $applications = []; +} +``` + +- [ ] **Step 2: Create EnvironmentHealth DTO** + +```php +applicationRepository->findAll(); + + $dto = new Dashboard(); + + foreach ($applications as $app) { + $envs = []; + + foreach ($app->getEnvironments() as $env) { + $containerStatus = $this->dockerService->getContainerStatus($env->getContainerName()); + + $envs[] = [ + 'id' => $env->getId(), + 'name' => $env->getName(), + 'status' => $containerStatus['status'], + 'version' => $containerStatus['version'], + ]; + } + + $dto->applications[] = [ + 'name' => $app->getName(), + 'slug' => $app->getSlug(), + 'giteaUrl' => $app->getGiteaUrl(), + 'environments' => $envs, + ]; + } + + return $dto; + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/State/DashboardProvider.php +git commit -m "feat : add DashboardProvider for container status overview" +``` + +--- + +## Task 4: EnvironmentHealthProvider + +**Files:** +- Create: `src/State/EnvironmentHealthProvider.php` + +- [ ] **Step 1: Create the provider** + +```php +environmentRepository->find($id) : null; + + if (null === $environment) { + throw new NotFoundHttpException(sprintf('Environment "%s" not found.', $id)); + } + + $containerName = $environment->getContainerName(); + $status = $this->dockerService->getContainerStatus($containerName); + $stats = $this->dockerService->getContainerStats($containerName); + + $dto = new EnvironmentHealth(); + $dto->status = $status['status']; + $dto->version = $status['version']; + $dto->startedAt = $status['startedAt']; + $dto->cpuPercent = $stats['cpuPercent']; + $dto->memoryUsage = $stats['memoryUsage']; + $dto->memoryLimit = $stats['memoryLimit']; + $dto->memoryPercent = $stats['memoryPercent']; + + return $dto; + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/State/EnvironmentHealthProvider.php +git commit -m "feat : add EnvironmentHealthProvider for detailed env metrics" +``` + +--- + +## Task 5: Frontend types and service + +**Files:** +- Create: `frontend/services/dto/dashboard.ts` +- Create: `frontend/services/dashboard.ts` + +- [ ] **Step 1: Create dashboard types** + +```typescript +type DashboardEnvironment = { + id: number + name: string + status: string + version: string +} + +type DashboardApplication = { + name: string + slug: string + giteaUrl?: string + environments: DashboardEnvironment[] +} + +type DashboardResponse = { + applications: DashboardApplication[] +} + +type EnvironmentHealth = { + status: string + version: string + startedAt: string + cpuPercent: number + memoryUsage: string + memoryLimit: string + memoryPercent: number +} +``` + +- [ ] **Step 2: Create dashboard service** + +```typescript +import type { DashboardResponse, EnvironmentHealth } from './dto/dashboard' + +export function getDashboard(): Promise { + return useApi().get('/dashboard', undefined, { + toast: false, + }) +} + +export function getEnvironmentHealth(envId: number): Promise { + return useApi().get(`/environments/${envId}/health`, undefined, { + toast: false, + }) +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add frontend/services/dto/dashboard.ts frontend/services/dashboard.ts +git commit -m "feat : add frontend dashboard types and service" +``` + +--- + +## Task 6: i18n translations + +**Files:** +- Modify: `frontend/i18n/locales/fr.json` + +- [ ] **Step 1: Add dashboard translations** + +Add a new `dashboard` section (replace the existing one which only has `title`): + +```json +{ + "dashboard": { + "title": "Dashboard", + "description": "Vue d'ensemble du SI", + "refresh": "Actualiser", + "status": { + "running": "En ligne", + "exited": "Arrete", + "restarting": "Redemarrage", + "not_found": "Introuvable" + } + } +} +``` + +Add a `health` sub-object inside the `environments` section: + +```json +{ + "environments": { + "...existing keys...", + "health": { + "title": "Sante du container", + "status": "Statut", + "version": "Version", + "uptime": "Uptime", + "cpu": "CPU", + "memory": "Memoire", + "noData": "Aucune donnee disponible" + } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add frontend/i18n/locales/fr.json +git commit -m "feat : add i18n translations for dashboard and health" +``` + +--- + +## Task 7: Dashboard page + +**Files:** +- Create: `frontend/pages/dashboard.vue` + +- [ ] **Step 1: Create the dashboard page** + +```vue + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add frontend/pages/dashboard.vue +git commit -m "feat : add dashboard page with container status overview" +``` + +--- + +## Task 8: Sidebar link + redirect + +**Files:** +- Modify: `frontend/layouts/default.vue` +- Modify: `frontend/middleware/auth.global.ts` + +- [ ] **Step 1: Add Dashboard link to sidebar** + +In `frontend/layouts/default.vue`, find the existing `SidebarLink` for applications (line ~41) and add the Dashboard link BEFORE it: + +```vue + + +``` + +Note: move the `border-t` class from the applications link to the dashboard link (first item gets the border). + +- [ ] **Step 2: Update auth middleware redirect** + +In `frontend/middleware/auth.global.ts`, change the redirect from `/applications` to `/dashboard`: + +Change: +```typescript +if (auth.isAuthenticated && (isLogin || to.path === '/')) { + return navigateTo('/applications') +} +``` + +To: +```typescript +if (auth.isAuthenticated && (isLogin || to.path === '/')) { + return navigateTo('/dashboard') +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add frontend/layouts/default.vue frontend/middleware/auth.global.ts +git commit -m "feat : add Dashboard to sidebar and redirect / to /dashboard" +``` + +--- + +## Task 9: Health metrics on detail page + +**Files:** +- Modify: `frontend/pages/applications/[slug].vue` + +- [ ] **Step 1: Add imports and state** + +At the top of `