e2cbb83397
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
66660364ec |
Cost-feature followups: libgssapi fix, daily CA, dashboard cost
Three small polish items on top of the cost-compute feature. (A) Dockerfile: install libgssapi-krb5-2 in the runtime stage. Silences the "Cannot load library libgssapi_krb5.so.2" warning that Npgsql logs when probing for Kerberos auth on Linux. We don't actually use Kerberos; the lib is ~3 MB and removes confusing log noise. (B) FleetTimescaleBootstrapper: add hierarchical fleet.daily_per_customer continuous aggregate on top of fleet.hourly_per_device. Per-customer daily totals (samples, kwh imported/exported, avg/max/min kW). Realtime, materialized_only=false, 365-day start_offset, hourly refresh. Available for long-range dashboards / billing summaries that don't need device-level or hourly detail. Not yet consumed by any query — exists as a primitive for the next dashboard / report that wants it. (C) Fleet dashboard: include per-customer cost today. - FleetQueryService.GetDashboardAsync invokes FleetCostService.ComputeAsync per customer for today's UTC window. Failures for one customer don't break the dashboard (leave their CostToday null and continue). - FleetCustomerSummary gains nullable CostToday; FleetDashboardDto gains TotalCostToday (sum across customers). - AdminFleetDashboardPage adds a 5th Statistic card "Cost today" in green, and a "Today (cost)" column in the customer summary table. - Perf note added in service comment: per-load cost compute scales with N customers; revisit with a materialised daily_cost view when N grows beyond ~100. Verified on the running stack - Admin container logs no longer contain "libgssapi" anywhere. - timescaledb_information.continuous_aggregates shows both hourly_per_device and daily_per_customer. - /api/fleet/dashboard returns totalCostToday=161.00, the single customer (DEV0001) row shows costToday=161.00 matching the cost endpoint's total for the same UTC day. Deliberately not in this commit: per-customer Postgres RLS for multi-Admin-user setups. That needs design decisions (claim source, fleet-admin bypass model, connection-pooling interaction with session variables) — I'd rather pose those questions and ship it right than sneak in a half-baked version. Wrap-up message has the design qs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e17921a122 |
Add portal: customer-facing white-labeled monitoring stack
New top-level portal/ project, peer to console/ and firmware/. Delivers a .NET 10 + React 18 + TimescaleDB + Grafana stack, one container set per customer behind Traefik. Built in 12 phases per FrontEndPrompt spec; no changes to existing console or firmware. Backend (src/Tau.Acuvim.Portal/): - .NET 10 minimal API, Serilog, ASP.NET Identity (cookie auth, lockout). - Single AppDbContext with identity / app / monitoring schemas. - MigrateAsync + TimescaleBootstrapper (idempotent hypertable creation) + IdentityBootstrapper (seeded admin + branding) on startup. - Pure CostCalculator + DB-backed RateService for tariffs (effective-dated, TOU periods, VAT, fixed charges, per-municipality timezone). - BrandingService with logo upload to mounted volume. - Time-series ingest + bucketed query services (time_bucket aggregates, ON CONFLICT for idempotent re-delivery). - ConfigOverviewService with redaction-by-construction (passwords never in payload). - DataProtection keys persisted to /data/keys volume for cookie survival across container restarts. Frontend (frontend/): - React 18 + TypeScript + Vite + Ant Design 5 + TanStack Query. - BrandingProvider + ThemedRoot for live re-themed white-labelling. - RequireAuth / RequireRole guards. - Pages: Login, Dashboard, Dashboards (embedded Grafana), Sites (admin), Settings tabs (Branding / Rates / Users / Grafana / App config). Infra: - Dev (docker-compose.yml) and prod (docker-compose.prod.yml) compose files. Three services per customer; Traefik subdomain + same-origin /grafana path-prefix routing wired with labels. - Grafana 11 with provisioned timescaledb datasource (uid pinned) and starter power-overview.json dashboard with device template variable. - Compose project name documented as lowercase (Compose v2 requirement). Tests (tests/Tau.Acuvim.Portal.Tests/): - xUnit, 40 tests. Covers CostCalculator (period match, TZ, overlap, VAT, fixed), ConnectionStringResolver (all 4 precedence branches incl. Production refusal), TariffValidator, DayOfWeekFlag. - All passing locally against .NET 10. Docs: - README.md (onboarding + 11 spec sections), OPERATIONS.md (per-customer provisioning, secret rotation, backup, troubleshooting), TESTING.md (manual integration scenarios, frontend test scaffolding recipe). Production safety guards: - Refuses to start if Authentication:DefaultAdminPassword is unchanged default in Production. - Refuses to start if Database:AutoProvisionLocalTimescaleDb=true in Production. - Prod Grafana ships with anonymous off and auth mode unset (three options documented in README Security) so iframe refuses to load until a deliberate prod auth choice is made. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |