- POST/GET /api/v1/support-cases[/{id}] and POST .../{id}/close in
SQLInsight Web, sharing the /support form's logic: same checks, finding
prefill without sensitive evidence, Idempotency-Key as the request id,
202 Accepted with Location, problem+json 403 without the support:write
scope or a support license. RequestedBy is the Windows user or
token:<name>.
- Closing: "Luk sag" on /support and the API record CloseRequested
(migration 0012, column-level UPDATE for the read-only web login); the
agent sends it to the hub with backoff and sets Closed when confirmed;
a pending case is closed once it has a number; an older hub list never
turns CloseRequested back to Open.
- Hub: POST /api/v1/cases/{reference}/close for the license's own cases
(others 404), idempotent, ClosedBy/ClosedComment (EF migration
CaseClosedBy). Portal closing records "SEOSoft: <user>"; the customer
page shows who closed and why; the support report and CSV get "Lukket af".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6.5 KiB
SQLInsight Hub (for SEOSoft)
SQLInsight Hub modtager fund fra betalende kunders agenter og samler dem i PostgreSQL. Portalen giver supporten overblik over kunder, kritiske fund, de mest udbredte problemer på tværs af kunder, supportsager og licenser. Hubben kan også servere opdateringsfeedet.
Kunder installerer ikke hubben. Den kører hos SEOSoft.
Krav
- PostgreSQL 14 eller nyere (testet med 17)
- .NET 10 runtime (ASP.NET Core), eller Docker
- En reverse proxy med TLS foran. Agenter sender kun til https-adresser, fordi licensen er deres adgangsnøgle.
Med Docker Compose
cd deploy/hub
export HUB_DB_PASSWORD='<langt tilfældigt kodeord>'
docker compose up -d
docker compose exec hub dotnet SQLInsight.Hub.dll add-staff <brugernavn> "<fulde navn>" # adgangskode på stdin, mindst 12 tegn
Ret docker-compose.yml:
SQLInsight__Hub__TrustedKeys__<kid>: den offentlige nøgle, licenserne er signeret med (frakeygen).SQLInsight__Hub__KnownProxies__0: reverse proxyens adresse, så login-begrænsningen ser klientens rigtige IP.
Volumen hubdata indeholder nøglerne til login-cookies (/data/keys) og opdateringsfeedet (/data/feed). Tag backup af den og af PostgreSQL.
Uden Docker
dotnet publish src/SQLInsight.Hub -c Release -o /opt/sqlinsight/hub
appsettings.json:
{
"ConnectionStrings": { "Hub": "Host=db01;Database=sqlinsight_hub;Username=sqlinsight_hub;Password=…" },
"SQLInsight": {
"Hub": {
"TrustedKeys": { "k20260929": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE…" },
"DataProtectionKeysPath": "/var/lib/sqlinsight-hub/keys",
"UpdateFeedPath": "/var/lib/sqlinsight-hub/feed",
"KnownProxies": [ "10.0.0.5" ],
"Retention": { "ResolvedFindings": "365.00:00:00", "Submissions": "90.00:00:00", "SilentInstances": "180.00:00:00" }
}
}
}
Hubben kan køre som Windows-tjeneste eller systemd-enhed (Type=notify) på samme måde som agenten. Databaseskemaet oprettes og opgraderes ved start.
Erfaring på tværs af kunder
Hubben registrerer, når en instans skifter build, og beregner, hvilke fund der forsvinder inden for 3 dage efter en opdatering, og hvilke der stadig findes efter 7 dage. Under Opgraderinger i portalen ses alle sammenhænge, også dem med få kunder. Sammenhænge med mindst 3 kunder, hvor fundet oftest forsvandt, hentes af agenterne (GET /api/v1/insights, med licens) og vises på fundet i kundens webbrugerflade.
"Insights": { "MinCustomers": 3, "ResolvedWithin": "3.00:00:00", "PersistedAfter": "7.00:00:00" }
Regler om selve versionen (SQLI-VER-*) er udeladt, fordi de altid forsvinder ved en opdatering.
Supportsager
Sagsnumre tildeles pr. kunde: SUP-000001, SUP-000002 … Portalen foreslår det næste nummer, når en sag oprettes fra et fund eller under Ny sag på kundens side. Feltet kan rettes (fx til et nummer fra et andet system), men et nummer kan kun bruges én gang pr. kunde. Håndskrevne numre, der ikke har formen SUP-<tal>, påvirker ikke rækkefølgen.
Kundens egne sager: betalende kunder (Professional/Enterprise) opretter sager i SQLInsight Web (se support.md). Agenten sender dem til POST /api/v1/cases med licensen og henter status for kundens sager med GET /api/v1/cases. Kundeoprettede sager er markeret Kunde under Supportsager med kontaktperson og beskrivelse, og er knyttet til fundet, hvis sagen blev oprettet fra et.
Mail til support, når en kunde opretter en sag (valgfri). Uden indstillingerne vises nye sager kun i portalen. Med Docker Compose/Forgejo sættes de som repo-secrets (SUPPORT_SMTP_HOST, SUPPORT_SMTP_PORT, SUPPORT_SMTP_USER, SUPPORT_SMTP_PASSWORD, SUPPORT_SMTP_FROM, SUPPORT_SMTP_TO kommasepareret, SUPPORT_PORTAL_URL), ellers i appsettings.json:
"Support": {
"Notify": {
"Enabled": true, "SmtpHost": "smtp.office365.com", "SmtpPort": 587, "StartTls": true,
"UserName": "support@seosoft.dk", "Password": "…", "From": "support@seosoft.dk",
"To": [ "support@seosoft.dk" ], "PortalUrl": "https://hub.sqlinsight.seosoft.dk/"
}
}
Microsoft 365 kræver, at Authenticated SMTP er slået til for postkassen; hubben logger ind med AUTH LOGIN. Svar på mailen går til kundens kontaktperson. En mail, der ikke kan sendes, logges; sagen er gemt alligevel.
Supportrapport (portalen → Supportrapport): for en periode (standard de seneste 90 dage) og alle kunder eller én: oprettede, lukkede og åbne sager, gennemsnitlig og median løsningstid (fra oprettelse til lukning), ældste åbne sag og andelen, kunden selv har oprettet, samt alle sager i perioden. Hent CSV giver listen til Excel (UTF-8, semikolon).
Licenser
# Én gang: nøglepar. Den private nøgle forlader aldrig SEOSofts maskine og må aldrig i git.
dotnet run --project tools/SQLInsight.LicenseTool -- keygen --out ~/sqlinsight-keys
# Pr. kunde
dotnet run --project tools/SQLInsight.LicenseTool -- issue --key ~/sqlinsight-keys/sqlinsight-k20260929.private.pem \
--kid k20260929 --customer "Kunde A/S" --plan Professional --expires 2027-09-30 --instances 10
Den offentlige nøgle lægges i src/SQLInsight.Licensing/trusted-keys.json før en release, så agenterne kan verificere licensen offline. Hubben får den via TrustedKeys. Licenser kan spærres i portalen under Licenser. Agenten får så 403 ved næste afsendelse.
Opbevaring og sletning
Hver nat sletter hubben:
| Data | Efter |
|---|---|
| Løste fund (medmindre de hører til en åben supportsag) | Retention:ResolvedFindings, 365 dage |
| Log over modtagelser | Retention:Submissions, 90 dage |
| Instanser, der ikke har rapporteret, med deres fund | Retention:SilentInstances, 180 dage |
Når en aftale ophører, eller en kunde beder om sletning:
dotnet SQLInsight.Hub.dll delete-customer "Kunde A/S" --confirm
Alle kundens instanser, fund, regelstatus, modtagelser og supportsager slettes. Licenserne spærres, så agenterne ikke kan sende igen, og kunden omdøbes til "Slettet kunde #id". Se også databehandling.md.
Kunden lukker en sag
POST /api/v1/cases/{reference}/close (licens som Authorization, kræver support i licensen) lukker en af licensens egne sager; andres sager giver 404. Body { comment?, requestedBy? }. Sagen får ClosedBy = "Kunde: <bruger>" og kommentaren; lukning i portalen giver ClosedBy = "SEOSoft: <medarbejder>". Kaldet er idempotent. Agenten sender lukninger, som kunden har bedt om i SQLInsight Web, og en senere liste fra hubben sætter ikke en ventende lukning tilbage til åben.