diff --git a/CHANGELOG.md b/CHANGELOG.md index 1881de9e2..c2969816f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ All notable changes to this project are documented in this file. The format is b - Scaffold new modules with `core/` and `web/` areas and mirrored nested Python tests; existing modules migrate individually. See `docs/module-layout.md`. - `mail` follows the module layout: `mail.core.{constants,settings,state,data_compatibility}` and `mail.delivery.{sender,runtime}`. `mail.settings` and `mail.constants` still import, with a `DeprecationWarning`, until 0.0.46. - `site_lock` follows the module layout: `site_lock.core.{constants,settings,state}` and `site_lock.web.{middleware,page,rate_limit}` (the unlock template moves with the page). `site_lock.settings` still imports, with a `DeprecationWarning`, until 0.0.46. +- `dashboard` follows the module layout: `dashboard.core.constants`, `dashboard.web.{endpoints,stats,doctor}`, and the page components move from `pages/components/` to `ui/components/`. Nothing outside the module imported the old paths, so no aliases remain. ### Fixed diff --git a/README.md b/README.md index 497839320..c48aa8b86 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,7 @@ The frontend uses an emerald + teal design system mirrored as Tailwind 4 tokens. - Reach for `StatCard` / `SectionTitle` / `FilterPills` before rolling new layouts. - Use lucide-react icons (already a dependency) and the existing `Badge` / `Button` variants — emerald primary for the main CTA, outline / ghost for secondary actions. -The 300-line file cap (enforced by CI) usually pushes you to factor row-level components into `pages/components/` — see `modules/users/users/admin/components/UserRow.tsx` and `modules/dashboard/dashboard/pages/components/doctor/report.ts` for the pattern. +The 300-line file cap (enforced by CI) usually pushes you to factor row-level components into `pages/components/` — see `modules/users/users/admin/components/UserRow.tsx` and `modules/dashboard/dashboard/ui/components/doctor/report.ts` for the pattern. ## User management diff --git a/docs/module-layout.md b/docs/module-layout.md index d5caae551..1140f906b 100644 --- a/docs/module-layout.md +++ b/docs/module-layout.md @@ -201,7 +201,7 @@ when a later module PR moves files. | auth | 8 | 20 | flat | | background_tasks | 49 | 83 | flat | | branding | 13 | 20 | flat | -| dashboard | 5 | 8 | flat | +| dashboard | 2 | 0 | follows this layout | | feature_flags | 8 | 9 | flat | | file_storage | 39 | 65 | flat | | keycloak | 13 | 19 | flat | diff --git a/docs/modules/dashboard.md b/docs/modules/dashboard.md index 0e10193c0..778ffaaa5 100644 --- a/docs/modules/dashboard.md +++ b/docs/modules/dashboard.md @@ -59,7 +59,7 @@ the role editor to change that. The three user figures come from the app's [user directory](/framework/user-directory) (`stats()`), so they count whichever auth provider holds the accounts. A figure the provider cannot give is `null` and its tile is hidden: under `keycloak` `users_created_this_month` is `null` (the cache only learns of a user at first sign-in), and with no auth provider all three are. -The result is cached process-wide for 30 seconds. If you mutate something that should change the numbers (e.g. seed users from a script), call `dashboard.stats.invalidate_stats_cache()` to clear it on the next call. +The result is cached process-wide for 30 seconds. If you mutate something that should change the numbers (e.g. seed users from a script), call `dashboard.web.stats.invalidate_stats_cache()` to clear it on the next call. ## Menu diff --git a/framework/cli/tests/test_module_css_build.py b/framework/cli/tests/test_module_css_build.py index 41f40c009..4547a49e0 100644 --- a/framework/cli/tests/test_module_css_build.py +++ b/framework/cli/tests/test_module_css_build.py @@ -85,7 +85,7 @@ def test_module_class_is_emitted_by_vite_build(self): PROBE_PAGE = PROBE_MODULE_PAGES / "CrossModuleImportProbe.tsx" PROBE_SOURCE = """\ // Generated by test_module_css_build.py — safe to delete. -import { ModuleTile } from '@simple-module-py/dashboard/pages/components/ModuleTile'; +import { ModuleTile } from '@simple-module-py/dashboard/ui/components/ModuleTile'; export default function CrossModuleImportProbe() { return ( diff --git a/host/tests/test_module_layout.py b/host/tests/test_module_layout.py index 5adbcc8a9..0da4777cc 100644 --- a/host/tests/test_module_layout.py +++ b/host/tests/test_module_layout.py @@ -74,11 +74,6 @@ services.py settings.py shared_props.py tenant_branding.py tenant_definitions.py """.split() ), - "dashboard": frozenset( - """ - constants.py doctor.py endpoints stats.py - """.split() - ), "feature_flags": frozenset( """ constants.py deps.py endpoints invalidation.py service.py tenant_cleanup.py diff --git a/modules/dashboard/dashboard/core/__init__.py b/modules/dashboard/dashboard/core/__init__.py new file mode 100644 index 000000000..5b63e8923 --- /dev/null +++ b/modules/dashboard/dashboard/core/__init__.py @@ -0,0 +1 @@ +"""What the dashboard builds on: its constants.""" diff --git a/modules/dashboard/dashboard/constants.py b/modules/dashboard/dashboard/core/constants.py similarity index 100% rename from modules/dashboard/dashboard/constants.py rename to modules/dashboard/dashboard/core/constants.py diff --git a/modules/dashboard/dashboard/module.py b/modules/dashboard/dashboard/module.py index d9619e939..f932e8c94 100644 --- a/modules/dashboard/dashboard/module.py +++ b/modules/dashboard/dashboard/module.py @@ -10,7 +10,7 @@ from simple_module_core.module import ModuleBase, ModuleMeta from simple_module_core.permissions import PermissionRegistry -from dashboard.constants import PERM_GROUP, PERM_VIEW +from dashboard.core.constants import PERM_GROUP, PERM_VIEW _URL_DASHBOARD = "/dashboard/" _URL_DOCTOR = "/admin/doctor/" @@ -29,14 +29,14 @@ class DashboardModule(ModuleBase): ) def register_routes(self, api_router: APIRouter, view_router: APIRouter) -> None: - from dashboard.endpoints.api import router as api - from dashboard.endpoints.views import router as views + from dashboard.web.endpoints.api import router as api + from dashboard.web.endpoints.views import router as views api_router.include_router(api) view_router.include_router(views) def register_admin_routes(self, admin_router: APIRouter) -> None: - from dashboard.endpoints.views import admin_router as doctor_views + from dashboard.web.endpoints.views import admin_router as doctor_views admin_router.include_router(doctor_views) diff --git a/modules/dashboard/dashboard/pages/Doctor.tsx b/modules/dashboard/dashboard/pages/Doctor.tsx index 904b2bac7..256e9fb72 100644 --- a/modules/dashboard/dashboard/pages/Doctor.tsx +++ b/modules/dashboard/dashboard/pages/Doctor.tsx @@ -5,15 +5,15 @@ import { Button } from '@simple-module-py/ui/components/ui/button'; import { AdminLayout } from '@simple-module-py/ui/layouts/AdminLayout'; import { RefreshCw } from 'lucide-react'; import { toast } from 'sonner'; -import { ChecksCard } from './components/doctor/ChecksCard'; -import { useCheckLabels } from './components/doctor/check-copy'; -import { copyToClipboard } from './components/doctor/copy'; -import { DevServerCard } from './components/doctor/DevServerCard'; -import { MigrationsCard } from './components/doctor/MigrationsCard'; -import { buildDoctorReport } from './components/doctor/report'; -import { StatsRow } from './components/doctor/StatsRow'; -import { TerminalPanel } from './components/doctor/TerminalPanel'; -import type { DoctorProps } from './components/doctor/types'; +import { ChecksCard } from '../ui/components/doctor/ChecksCard'; +import { useCheckLabels } from '../ui/components/doctor/check-copy'; +import { copyToClipboard } from '../ui/components/doctor/copy'; +import { DevServerCard } from '../ui/components/doctor/DevServerCard'; +import { MigrationsCard } from '../ui/components/doctor/MigrationsCard'; +import { buildDoctorReport } from '../ui/components/doctor/report'; +import { StatsRow } from '../ui/components/doctor/StatsRow'; +import { TerminalPanel } from '../ui/components/doctor/TerminalPanel'; +import type { DoctorProps } from '../ui/components/doctor/types'; const RERUN_URL = '/admin/doctor/rerun'; diff --git a/modules/dashboard/dashboard/pages/Home.tsx b/modules/dashboard/dashboard/pages/Home.tsx index c68db5703..1cbd82c4c 100644 --- a/modules/dashboard/dashboard/pages/Home.tsx +++ b/modules/dashboard/dashboard/pages/Home.tsx @@ -6,8 +6,8 @@ import { Card, CardContent } from '@simple-module-py/ui/components/ui/card'; import { usePageProps } from '@simple-module-py/ui/hooks/usePageProps'; import { AuthenticatedLayout } from '@simple-module-py/ui/layouts/AuthenticatedLayout'; import { Activity, Box, Stethoscope, Users } from 'lucide-react'; -import { type ModuleHealth, ModuleTile } from './components/ModuleTile'; -import { moduleTargetResolver } from './components/module-target'; +import { type ModuleHealth, ModuleTile } from '../ui/components/ModuleTile'; +import { moduleTargetResolver } from '../ui/components/module-target'; interface SystemModule { name: string; diff --git a/modules/dashboard/dashboard/pages/components/ModuleTile.tsx b/modules/dashboard/dashboard/ui/components/ModuleTile.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/ModuleTile.tsx rename to modules/dashboard/dashboard/ui/components/ModuleTile.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/ChecksCard.tsx b/modules/dashboard/dashboard/ui/components/doctor/ChecksCard.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/ChecksCard.tsx rename to modules/dashboard/dashboard/ui/components/doctor/ChecksCard.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/DevServerCard.tsx b/modules/dashboard/dashboard/ui/components/doctor/DevServerCard.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/DevServerCard.tsx rename to modules/dashboard/dashboard/ui/components/doctor/DevServerCard.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/MigrationsCard.tsx b/modules/dashboard/dashboard/ui/components/doctor/MigrationsCard.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/MigrationsCard.tsx rename to modules/dashboard/dashboard/ui/components/doctor/MigrationsCard.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/StatsRow.tsx b/modules/dashboard/dashboard/ui/components/doctor/StatsRow.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/StatsRow.tsx rename to modules/dashboard/dashboard/ui/components/doctor/StatsRow.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/TerminalPanel.tsx b/modules/dashboard/dashboard/ui/components/doctor/TerminalPanel.tsx similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/TerminalPanel.tsx rename to modules/dashboard/dashboard/ui/components/doctor/TerminalPanel.tsx diff --git a/modules/dashboard/dashboard/pages/components/doctor/check-copy.ts b/modules/dashboard/dashboard/ui/components/doctor/check-copy.ts similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/check-copy.ts rename to modules/dashboard/dashboard/ui/components/doctor/check-copy.ts diff --git a/modules/dashboard/dashboard/pages/components/doctor/copy.ts b/modules/dashboard/dashboard/ui/components/doctor/copy.ts similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/copy.ts rename to modules/dashboard/dashboard/ui/components/doctor/copy.ts diff --git a/modules/dashboard/dashboard/pages/components/doctor/report.ts b/modules/dashboard/dashboard/ui/components/doctor/report.ts similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/report.ts rename to modules/dashboard/dashboard/ui/components/doctor/report.ts diff --git a/modules/dashboard/dashboard/pages/components/doctor/types.ts b/modules/dashboard/dashboard/ui/components/doctor/types.ts similarity index 100% rename from modules/dashboard/dashboard/pages/components/doctor/types.ts rename to modules/dashboard/dashboard/ui/components/doctor/types.ts diff --git a/modules/dashboard/dashboard/pages/components/module-target.ts b/modules/dashboard/dashboard/ui/components/module-target.ts similarity index 100% rename from modules/dashboard/dashboard/pages/components/module-target.ts rename to modules/dashboard/dashboard/ui/components/module-target.ts diff --git a/modules/dashboard/dashboard/web/__init__.py b/modules/dashboard/dashboard/web/__init__.py new file mode 100644 index 000000000..0fa0881a4 --- /dev/null +++ b/modules/dashboard/dashboard/web/__init__.py @@ -0,0 +1 @@ +"""The HTTP side: the home and doctor endpoints and the stats and doctor reads they share.""" diff --git a/modules/dashboard/dashboard/doctor.py b/modules/dashboard/dashboard/web/doctor.py similarity index 100% rename from modules/dashboard/dashboard/doctor.py rename to modules/dashboard/dashboard/web/doctor.py diff --git a/modules/dashboard/dashboard/endpoints/__init__.py b/modules/dashboard/dashboard/web/endpoints/__init__.py similarity index 100% rename from modules/dashboard/dashboard/endpoints/__init__.py rename to modules/dashboard/dashboard/web/endpoints/__init__.py diff --git a/modules/dashboard/dashboard/endpoints/api.py b/modules/dashboard/dashboard/web/endpoints/api.py similarity index 88% rename from modules/dashboard/dashboard/endpoints/api.py rename to modules/dashboard/dashboard/web/endpoints/api.py index ff9dc2ab4..06d357404 100644 --- a/modules/dashboard/dashboard/endpoints/api.py +++ b/modules/dashboard/dashboard/web/endpoints/api.py @@ -7,8 +7,8 @@ from simple_module_hosting.permissions import RequiresPermission from sqlalchemy.ext.asyncio import AsyncSession -from dashboard.constants import PERM_VIEW -from dashboard.stats import fetch_dashboard_stats +from dashboard.core.constants import PERM_VIEW +from dashboard.web.stats import fetch_dashboard_stats router = APIRouter() diff --git a/modules/dashboard/dashboard/endpoints/views.py b/modules/dashboard/dashboard/web/endpoints/views.py similarity index 95% rename from modules/dashboard/dashboard/endpoints/views.py rename to modules/dashboard/dashboard/web/endpoints/views.py index 82143ffcf..a29a3265c 100644 --- a/modules/dashboard/dashboard/endpoints/views.py +++ b/modules/dashboard/dashboard/web/endpoints/views.py @@ -18,8 +18,8 @@ from sqlalchemy.ext.asyncio import AsyncSession from starlette.responses import RedirectResponse -from dashboard.constants import PERM_VIEW -from dashboard.stats import fetch_dashboard_stats +from dashboard.core.constants import PERM_VIEW +from dashboard.web.stats import fetch_dashboard_stats router = APIRouter() @@ -66,7 +66,7 @@ async def dashboard( @admin_router.get("/", response_model=None) async def doctor(request: Request, inertia: InertiaDep) -> InertiaResponse: """`make doctor` mirror — checks, migrations, dev server, all live.""" - from dashboard.doctor import doctor_props + from dashboard.web.doctor import doctor_props # doctor_props walks the alembic script directory and every module's pages # dir, so it is blocking filesystem work: keep it off the event loop rather diff --git a/modules/dashboard/dashboard/stats.py b/modules/dashboard/dashboard/web/stats.py similarity index 100% rename from modules/dashboard/dashboard/stats.py rename to modules/dashboard/dashboard/web/stats.py diff --git a/modules/dashboard/tests-js/dashboard_Doctor.test.tsx b/modules/dashboard/tests-js/dashboard_Doctor.test.tsx index a69bf4bf4..47c32aede 100644 --- a/modules/dashboard/tests-js/dashboard_Doctor.test.tsx +++ b/modules/dashboard/tests-js/dashboard_Doctor.test.tsx @@ -10,8 +10,8 @@ vi.mock('@inertiajs/react', () => ({ usePage: () => ({ props: {} }), })); -import type { DoctorProps } from '../dashboard/pages/components/doctor/types'; import Doctor from '../dashboard/pages/Doctor'; +import type { DoctorProps } from '../dashboard/ui/components/doctor/types'; const props: DoctorProps = { checks: [{ id: 'pages', status: 'pass', command: null, findings: [] }], diff --git a/modules/dashboard/tests-js/doctor-stats.test.tsx b/modules/dashboard/tests-js/doctor-stats.test.tsx index 93c658a29..417c23532 100644 --- a/modules/dashboard/tests-js/doctor-stats.test.tsx +++ b/modules/dashboard/tests-js/doctor-stats.test.tsx @@ -15,9 +15,9 @@ configureI18n({ }, }); -import { buildDoctorReport } from '../dashboard/pages/components/doctor/report'; -import { StatsRow } from '../dashboard/pages/components/doctor/StatsRow'; -import type { DoctorProps } from '../dashboard/pages/components/doctor/types'; +import { buildDoctorReport } from '../dashboard/ui/components/doctor/report'; +import { StatsRow } from '../dashboard/ui/components/doctor/StatsRow'; +import type { DoctorProps } from '../dashboard/ui/components/doctor/types'; const STATS = { checks_passing: 0, diff --git a/modules/dashboard/tests/test_dashboard.py b/modules/dashboard/tests/core/module/test_dashboard.py similarity index 98% rename from modules/dashboard/tests/test_dashboard.py rename to modules/dashboard/tests/core/module/test_dashboard.py index 93f6d2726..5ec6c7d35 100644 --- a/modules/dashboard/tests/test_dashboard.py +++ b/modules/dashboard/tests/core/module/test_dashboard.py @@ -5,7 +5,7 @@ import httpx import pytest from dashboard.module import DashboardModule -from dashboard.stats import invalidate_stats_cache +from dashboard.web.stats import invalidate_stats_cache @pytest.fixture(autouse=True) @@ -35,7 +35,7 @@ async def test_module_meta(self): class TestFetchDashboardStats: @pytest.fixture async def stats(self, app): - from dashboard.stats import fetch_dashboard_stats + from dashboard.web.stats import fetch_dashboard_stats async with app.state.sm.db.session_factory() as db: return await fetch_dashboard_stats(db, app) diff --git a/modules/dashboard/tests/test_doctor_migration_chips.py b/modules/dashboard/tests/web/doctor/test_doctor_migration_chips.py similarity index 95% rename from modules/dashboard/tests/test_doctor_migration_chips.py rename to modules/dashboard/tests/web/doctor/test_doctor_migration_chips.py index ef819b5d8..720a49b5d 100644 --- a/modules/dashboard/tests/test_doctor_migration_chips.py +++ b/modules/dashboard/tests/web/doctor/test_doctor_migration_chips.py @@ -10,7 +10,7 @@ from __future__ import annotations -from dashboard.doctor import HOST_MIGRATION_CHIP, _module_chip +from dashboard.web.doctor import HOST_MIGRATION_CHIP, _module_chip class TestMigrationChips: diff --git a/modules/dashboard/tests/test_doctor_props.py b/modules/dashboard/tests/web/doctor/test_doctor_props.py similarity index 98% rename from modules/dashboard/tests/test_doctor_props.py rename to modules/dashboard/tests/web/doctor/test_doctor_props.py index 2bd1b6207..89311b48f 100644 --- a/modules/dashboard/tests/test_doctor_props.py +++ b/modules/dashboard/tests/web/doctor/test_doctor_props.py @@ -13,8 +13,8 @@ import httpx import pytest -from dashboard.doctor import CHECKS, MIGRATION_CHECK_ID -from dashboard.stats import invalidate_stats_cache +from dashboard.web.doctor import CHECKS, MIGRATION_CHECK_ID +from dashboard.web.stats import invalidate_stats_cache from simple_module_core.diagnostics import Diagnostic, DiagnosticLevel from simple_module_test.fixtures import forge_session_cookie diff --git a/modules/dashboard/tests/test_dashboard_permission.py b/modules/dashboard/tests/web/endpoints/test_dashboard_permission.py similarity index 96% rename from modules/dashboard/tests/test_dashboard_permission.py rename to modules/dashboard/tests/web/endpoints/test_dashboard_permission.py index bd53ed52f..78ef23559 100644 --- a/modules/dashboard/tests/test_dashboard_permission.py +++ b/modules/dashboard/tests/web/endpoints/test_dashboard_permission.py @@ -8,8 +8,8 @@ from __future__ import annotations import pytest -from dashboard.constants import PERM_VIEW -from dashboard.stats import invalidate_stats_cache +from dashboard.core.constants import PERM_VIEW +from dashboard.web.stats import invalidate_stats_cache from simple_module_core.tenancy import TenantRole, tenant_role _STATS = "/api/dashboard/stats" diff --git a/modules/dashboard/tests/test_view_routes.py b/modules/dashboard/tests/web/endpoints/test_dashboard_view_routes.py similarity index 100% rename from modules/dashboard/tests/test_view_routes.py rename to modules/dashboard/tests/web/endpoints/test_dashboard_view_routes.py diff --git a/modules/dashboard/tests/test_dashboard_month_delta.py b/modules/dashboard/tests/web/stats/test_dashboard_month_delta.py similarity index 97% rename from modules/dashboard/tests/test_dashboard_month_delta.py rename to modules/dashboard/tests/web/stats/test_dashboard_month_delta.py index aa56ad683..088975307 100644 --- a/modules/dashboard/tests/test_dashboard_month_delta.py +++ b/modules/dashboard/tests/web/stats/test_dashboard_month_delta.py @@ -12,7 +12,7 @@ import httpx import pytest -from dashboard.stats import fetch_dashboard_stats, invalidate_stats_cache +from dashboard.web.stats import fetch_dashboard_stats, invalidate_stats_cache from users.models import User diff --git a/modules/dashboard/tests/test_dashboard_single_tenant.py b/modules/dashboard/tests/web/stats/test_dashboard_single_tenant.py similarity index 96% rename from modules/dashboard/tests/test_dashboard_single_tenant.py rename to modules/dashboard/tests/web/stats/test_dashboard_single_tenant.py index 46caf48be..0684c435e 100644 --- a/modules/dashboard/tests/test_dashboard_single_tenant.py +++ b/modules/dashboard/tests/web/stats/test_dashboard_single_tenant.py @@ -9,8 +9,8 @@ from __future__ import annotations import pytest -from dashboard.constants import PERM_VIEW -from dashboard.stats import invalidate_stats_cache +from dashboard.core.constants import PERM_VIEW +from dashboard.web.stats import invalidate_stats_cache from simple_module_hosting.settings import Settings from simple_module_test.tenant_client import session_client diff --git a/modules/dashboard/tests/test_dashboard_user_directory.py b/modules/dashboard/tests/web/stats/test_dashboard_user_directory.py similarity index 94% rename from modules/dashboard/tests/test_dashboard_user_directory.py rename to modules/dashboard/tests/web/stats/test_dashboard_user_directory.py index d485469e5..11aa054ff 100644 --- a/modules/dashboard/tests/test_dashboard_user_directory.py +++ b/modules/dashboard/tests/web/stats/test_dashboard_user_directory.py @@ -15,7 +15,7 @@ @pytest.fixture(autouse=True) def _fresh_cache(): - from dashboard.stats import invalidate_stats_cache + from dashboard.web.stats import invalidate_stats_cache invalidate_stats_cache() yield @@ -34,7 +34,7 @@ async def stats(self, db, *, active_window): async def _stats(app) -> dict: - from dashboard.stats import fetch_dashboard_stats + from dashboard.web.stats import fetch_dashboard_stats async with app.state.sm.db.session_factory() as db: return await fetch_dashboard_stats(db, app) diff --git a/modules/settings/settings/endpoints/views.py b/modules/settings/settings/endpoints/views.py index 98250c4a2..8a1967848 100644 --- a/modules/settings/settings/endpoints/views.py +++ b/modules/settings/settings/endpoints/views.py @@ -268,7 +268,7 @@ async def test_connection(package: str, request: Request) -> dict: # Run independently of one another — a module can register several checks # (e.g. SMTP + primary storage + backup storage), and sequentially awaiting # each one would block the request for the sum of their latencies instead - # of the slowest one, the same reasoning dashboard.stats._run_health_checks + # of the slowest one, the same reasoning dashboard.web.stats._run_health_checks # already applies to probe checks. async def _run_one(check) -> dict: try: