- mail/internal/storage: LocalDriver/S3Driver (bewaehrtes Muster aus DMS FDN-03, bewusste Neuimplementierung - Mail kann DMS nicht importieren), ObjectKey mit festem Pfadschema - Service.Put/GetVerified: Pruefsummenverifikation AN DIESER SCHICHT (Erweiterung gegenueber FDN-03) - SHA-256-Sidecar, sofortige Ruecklese-Verifikation beim Schreiben, Erkennung manipulierter Objekte beim Lesen - HTTPUsageReporter: meldet an Core API-11 (resync-api/LIC-05), identisches Muster wie DMS FDN-03 - 4 Tests real bestanden: byteidentischer Read-back, manipuliertes Objekt erkannt, Lasttest (500 Objekte, 105.8us/Objekt), Nutzungsmeldung bei Schreiben+Loeschen - zusaetzlich echter End-zu-Ende-Beweis gegen den laufenden nexarch-resync-api.service: reales Service-Credential provisioniert, Put->GetVerified->Delete komplett durchlaufen, usage_counters zeigt reales +29/-29-Delta (beide Meldungen real angewendet) Pruefungen siehe mail/docs/ARC-01-PRUEFPROTOKOLL.md
76 lines
2.6 KiB
Go
76 lines
2.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// UsageMetric ist der Metrikname, unter dem Core (internal/usage,
|
|
// LIC-05) den Speicherverbrauch je Mandant führt — muss exakt
|
|
// internal/usage.StorageBytesMetric aus dem NEXARCH-Core-Modul
|
|
// entsprechen (Mail kann Core nicht importieren, daher hier gespiegelt
|
|
// — identisches Muster wie DMS FDN-03).
|
|
const UsageMetric = "storage_bytes"
|
|
|
|
// UsageReporter meldet Speicherverbrauchsänderungen an Core
|
|
// (Akzeptanzkriterium 4). Schmale Schnittstelle, damit Tests einen
|
|
// Fake statt eines echten HTTP-Aufrufs einsetzen können.
|
|
type UsageReporter interface {
|
|
Report(ctx context.Context, tenantSlug, metric string, delta int64) error
|
|
}
|
|
|
|
// usageDeltaDTO entspricht Core internal/resync.usageDeltaDTO
|
|
// (JSON-Vertrag: tenant_slug/metric/delta), über den API-11
|
|
// (resync-api) real erreichbar ist.
|
|
type usageDeltaDTO struct {
|
|
TenantSlug string `json:"tenant_slug"`
|
|
Metric string `json:"metric"`
|
|
Delta int64 `json:"delta"`
|
|
}
|
|
|
|
// HTTPUsageReporter meldet über Core API-11 (resync-api,
|
|
// internal/resync.Handler.UsageHandler), authentifiziert über
|
|
// dasselbe Service-Credential-Verfahren wie jeder andere Modul-Core-
|
|
// Aufruf (API-02).
|
|
type HTTPUsageReporter struct {
|
|
endpointURL string
|
|
clientID string
|
|
clientSecret string
|
|
httpClient *http.Client
|
|
}
|
|
|
|
func NewHTTPUsageReporter(endpointURL, clientID, clientSecret string, httpClient *http.Client) *HTTPUsageReporter {
|
|
if httpClient == nil {
|
|
httpClient = http.DefaultClient
|
|
}
|
|
return &HTTPUsageReporter{endpointURL: endpointURL, clientID: clientID, clientSecret: clientSecret, httpClient: httpClient}
|
|
}
|
|
|
|
func (r *HTTPUsageReporter) Report(ctx context.Context, tenantSlug, metric string, delta int64) error {
|
|
body, err := json.Marshal([]usageDeltaDTO{{TenantSlug: tenantSlug, Metric: metric, Delta: delta}})
|
|
if err != nil {
|
|
return fmt.Errorf("storage: nutzungsmeldung serialisieren: %w", err)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, r.endpointURL, bytes.NewReader(body))
|
|
if err != nil {
|
|
return fmt.Errorf("storage: nutzungsmeldungs-anfrage aufbauen: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("X-Nexarch-Client-Id", r.clientID)
|
|
req.Header.Set("X-Nexarch-Client-Secret", r.clientSecret)
|
|
|
|
resp, err := r.httpClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("storage: nutzungsmeldung senden: %w", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("storage: nutzungsmeldung von core abgelehnt: status %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|