09cd435c2b
ci / build-test (push) Successful in 1m19s
Three requested phases.
1) Admin section (SDD §8.7) — MudBlazor inline-dialog CRUD, consistent pattern,
delete guards, snackbar feedback, shared Confirm helper:
- Energy types: create/edit/delete (blocks delete when meters reference it).
- Meters: create/edit/delete; recomputes consumption when mode/baseline
changes (NormalizationService over a fresh factory context, in a tx);
delete cascades data (consumption+readings are Restrict → removed first).
- A meter's ingest sources: manage on the meter-detail Sources tab
(add/edit/delete MQTT/Tasmota/HA sources with typed config).
- Tariffs: full CRUD (scope/component/value/validity).
- Cost categories: CRUD + member management (meter or energy-type members).
- Connectors: ingestion_endpoint CRUD (MQTT broker + Home Assistant);
secrets referenced by env-var name only, never stored.
- Settings: read-only effective-config view (settings are env-driven and
reproducible, so an editable form would change nothing — kept honest).
PV role is now editable on meters (MeterMeta.SetRole can clear a role).
2) Read Home Assistant — extracted a shared public HaEndpointConfig (was a
private record in the worker), added HaConnectionTester (powers the connector
"Test connection": checks base URL + env-resolved token, optionally reads one
entity). Configuring an HA connector + an HA source on a meter drives the
existing REST-poll worker end to end. (WebSocket push stays a future
optimization; REST poll already reads HA.)
3) Wiring/placeholder audit — swept every OnClick/Href: all handlers are real,
all internal links resolve to real routes, no TODO/stub/placeholder code.
Fixed one genuine gap: MainLayout had no drawer toggle, so the nav was
unreachable on narrow screens — added a hamburger button.
Tests: +6 (MeterMeta.SetRole role-removal; HaConnectionTester fail-closed
guard branches with a throwing HttpClientFactory proving no network on bad
config); render test now covers all admin routes. 69 Core + 45 Integration =
114 green. Live-verified in Docker: all admin pages 200, drawer toggle present,
Settings shows real effective config.
Claude-Session: https://claude.ai/code/session_01Lz2RqAsnQhetqWNoCDfexK
74 lines
3.8 KiB
Plaintext
74 lines
3.8 KiB
Plaintext
@page "/admin/settings"
|
|
@rendermode InteractiveServer
|
|
@inject Microsoft.Extensions.Options.IOptions<MeterVault.Infrastructure.Options.MeterVaultOptions> Options
|
|
@using MudBlazor
|
|
|
|
<PageTitle>MeterVault — Settings</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-2">Settings</MudText>
|
|
<MudAlert Severity="Severity.Info" Class="mb-4" Dense="true">
|
|
These are the <b>effective</b> settings the running instance is using. They are configured via environment
|
|
variables (<code>MeterVault__Key</code> / <code>Section__Key</code>) or Docker/compose, not stored in the
|
|
database — so config stays reproducible and secrets never land in the DB. Change them in your compose/env and restart.
|
|
</MudAlert>
|
|
|
|
<MudGrid>
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="2" Style="height:100%">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Locale & time</MudText>
|
|
<MudSimpleTable Dense="true">
|
|
<tbody>
|
|
<tr><td>Timezone</td><td style="text-align:right"><code>@_o.TimeZone</code></td></tr>
|
|
<tr><td>Locale</td><td style="text-align:right"><code>@_o.Locale</code></td></tr>
|
|
<tr><td>Currency</td><td style="text-align:right"><code>@_o.Currency</code></td></tr>
|
|
<tr><td>Raw-reading retention</td><td style="text-align:right">@_o.RawRetentionDays days</td></tr>
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="mt-2">
|
|
Env keys: <code>MeterVault__TimeZone</code>, <code>MeterVault__Locale</code>,
|
|
<code>MeterVault__Currency</code>, <code>MeterVault__RawRetentionDays</code>.
|
|
</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<MudItem xs="12" md="6">
|
|
<MudPaper Class="pa-4" Elevation="2" Style="height:100%">
|
|
<MudText Typo="Typo.h6" Class="mb-2">Access & ingestion</MudText>
|
|
<MudSimpleTable Dense="true">
|
|
<tbody>
|
|
<tr>
|
|
<td>REST API</td>
|
|
<td style="text-align:right">
|
|
@if (_o.ApiKeys.Count > 0)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Success">@_o.ApiKeys.Count key(s) configured</MudChip>
|
|
}
|
|
else if (_o.AllowAnonymousApi)
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Warning">open (anonymous)</MudChip>
|
|
}
|
|
else
|
|
{
|
|
<MudChip T="string" Size="Size.Small" Color="Color.Default">closed (401)</MudChip>
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr><td>Reverse-proxy trust</td><td style="text-align:right">@(_o.ReverseProxyTrust ? "on" : "off")</td></tr>
|
|
<tr><td>Live ingestion workers</td><td style="text-align:right">@(_o.EnableLiveIngestion ? "on" : "off")</td></tr>
|
|
<tr><td>Seed reference data on start</td><td style="text-align:right">@(_o.SeedReferenceData ? "on" : "off")</td></tr>
|
|
</tbody>
|
|
</MudSimpleTable>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="mt-2">
|
|
Set API keys with <code>MeterVault__ApiKeys__0</code>. Keys themselves are never shown here.
|
|
API docs at <MudLink Href="/swagger" Target="_blank">/swagger</MudLink>.
|
|
</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
@code {
|
|
private MeterVault.Infrastructure.Options.MeterVaultOptions _o = new();
|
|
|
|
protected override void OnInitialized() => _o = Options.Value;
|
|
}
|