From 27681f31be3714e328d8b74762e38ee53059cc1a Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 27 Oct 2026 23:29:11 +0100 Subject: [PATCH] mail: httpclient, loadtest, policyclient aus common v0.1.0 Der Waechtertest gegen http.DefaultClient liegt jetzt in internal/archtest. Co-Authored-By: Claude Sonnet 5.5 --- mail/go.mod | 1 + mail/go.sum | 2 + mail/internal/archtest/defaultclient_test.go | 51 +++++++ mail/internal/crypto/kekprovider.go | 2 +- mail/internal/httpclient/httpclient.go | 29 ---- mail/internal/httpclient/httpclient_test.go | 99 ------------ mail/internal/imap/loadtest_test.go | 2 +- mail/internal/indexworker/limits_test.go | 1 - .../indexworker/worker_limits_test.go | 1 - mail/internal/loadtest/loadtest.go | 141 ------------------ mail/internal/mailapi/mailapi.go | 2 +- mail/internal/mailapi/mailapi_policy_test.go | 2 +- .../mailer/ing11_header_injection_test.go | 26 ++-- mail/internal/mimeparse/mimeparse.go | 8 +- .../moduletypeclient/moduletypeclient.go | 2 +- mail/internal/policyclient/policyclient.go | 52 ------- mail/internal/pop3/loadtest_test.go | 2 +- mail/internal/smtp/loadtest_test.go | 2 +- mail/internal/storage/usagereport.go | 2 +- mail/internal/syncalert/dispatcher.go | 2 +- mail/internal/workflowevent/client.go | 2 +- 21 files changed, 81 insertions(+), 350 deletions(-) create mode 100644 mail/internal/archtest/defaultclient_test.go delete mode 100644 mail/internal/httpclient/httpclient.go delete mode 100644 mail/internal/httpclient/httpclient_test.go delete mode 100644 mail/internal/loadtest/loadtest.go delete mode 100644 mail/internal/policyclient/policyclient.go diff --git a/mail/go.mod b/mail/go.mod index 2e1682dd..60e492ac 100644 --- a/mail/go.mod +++ b/mail/go.mod @@ -5,6 +5,7 @@ go 1.25.0 toolchain go1.25.13 require ( + gitea.perlbach24.de/scripte/nexarch/common v0.1.0 github.com/aws/aws-sdk-go-v2 v1.45.1 github.com/aws/aws-sdk-go-v2/config v1.33.1 github.com/aws/aws-sdk-go-v2/credentials v1.20.1 diff --git a/mail/go.sum b/mail/go.sum index ac9b9be2..a00dfd69 100644 --- a/mail/go.sum +++ b/mail/go.sum @@ -1,3 +1,5 @@ +gitea.perlbach24.de/scripte/nexarch/common v0.1.0 h1:biNzFr2LOMZC7b1leY3smQtrMmSMp6IW80OvR4LSjLY= +gitea.perlbach24.de/scripte/nexarch/common v0.1.0/go.mod h1:CiEImG++QMt1xZqSMeDJhPiB7sfWTLZOwjS6OY/Iguc= github.com/aws/aws-sdk-go-v2 v1.45.1 h1:iIoG3NaLhV6UZpPXyPXlDj2I9oS8tV/nMcMnITCC6Ks= github.com/aws/aws-sdk-go-v2 v1.45.1/go.mod h1:bttEH6JqnUL8LepvDVfdrds/fZ5bCIxzpe3abyUrhDU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 h1:GPRlPwz40I2B2VrBEASOA3Bi77NyeqejNLkifosX0rs= diff --git a/mail/internal/archtest/defaultclient_test.go b/mail/internal/archtest/defaultclient_test.go new file mode 100644 index 00000000..92c29555 --- /dev/null +++ b/mail/internal/archtest/defaultclient_test.go @@ -0,0 +1,51 @@ +package archtest + +import ( + "io/fs" + "os" + "path/filepath" + "regexp" + "strings" + "testing" +) + +// Architekturtest: im Produktivcode dieses Moduls gibt es keine Aufrufe ueber +// den Standard-Client ohne Zeitlimit. +func TestNoUnboundedDefaultClientInProductionCode(t *testing.T) { + root, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + forbidden := regexp.MustCompile(`http\.DefaultClient|http\.(Get|Post|Head|PostForm)\(`) + err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + if n := d.Name(); n == "node_modules" || n == "testdata" || n == ".git" { + return filepath.SkipDir + } + return nil + } + if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return err + } + for i, line := range strings.Split(string(data), "\n") { + if t := strings.TrimSpace(line); strings.HasPrefix(t, "//") { + continue + } + if forbidden.MatchString(line) { + rel, _ := filepath.Rel(root, path) + t.Errorf("%s:%d nutzt den Standard-Client ohne Zeitlimit: %s", rel, i+1, strings.TrimSpace(line)) + } + } + return nil + }) + if err != nil { + t.Fatal(err) + } +} diff --git a/mail/internal/crypto/kekprovider.go b/mail/internal/crypto/kekprovider.go index 4a422c2d..2ed39da6 100644 --- a/mail/internal/crypto/kekprovider.go +++ b/mail/internal/crypto/kekprovider.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/httpclient" + "gitea.perlbach24.de/scripte/nexarch/common/httpclient" "net/http" "net/url" ) diff --git a/mail/internal/httpclient/httpclient.go b/mail/internal/httpclient/httpclient.go deleted file mode 100644 index 53c9a9f4..00000000 --- a/mail/internal/httpclient/httpclient.go +++ /dev/null @@ -1,29 +0,0 @@ -// Package httpclient liefert den HTTP-Client fuer ausgehende Dienstaufrufe -// (QA-15 F-11): Zeitlimit statt endlosem Warten auf einen haengenden -// Nachbardienst, und keine Weiterleitungen (ein Service-Token wuerde sonst an -// das Weiterleitungsziel mitgesendet). -package httpclient - -import ( - "net/http" - "time" -) - -// DefaultTimeout gilt fuer einen kompletten Aufruf (Verbindung bis Antwortende). -const DefaultTimeout = 15 * time.Second - -var shared = New(DefaultTimeout) - -// Default ist der gemeinsame Client mit DefaultTimeout. -func Default() *http.Client { return shared } - -// New liefert einen Client mit dem angegebenen Zeitlimit, der Weiterleitungen -// nicht folgt (die 3xx-Antwort wird zurueckgegeben). -func New(timeout time.Duration) *http.Client { - return &http.Client{ - Timeout: timeout, - CheckRedirect: func(*http.Request, []*http.Request) error { - return http.ErrUseLastResponse - }, - } -} diff --git a/mail/internal/httpclient/httpclient_test.go b/mail/internal/httpclient/httpclient_test.go deleted file mode 100644 index ea24c6b2..00000000 --- a/mail/internal/httpclient/httpclient_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package httpclient - -import ( - "io/fs" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "regexp" - "strings" - "testing" - "time" -) - -func TestDefaultHasTimeout(t *testing.T) { - if Default().Timeout != DefaultTimeout || DefaultTimeout <= 0 { - t.Fatalf("timeout = %s", Default().Timeout) - } -} - -func TestClientGivesUpOnHangingServer(t *testing.T) { - release := make(chan struct{}) - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { <-release })) - defer srv.Close() - defer close(release) - - start := time.Now() - resp, err := New(200 * time.Millisecond).Get(srv.URL) - if err == nil { - _ = resp.Body.Close() - t.Fatal("kein fehler bei haengendem server") - } - if elapsed := time.Since(start); elapsed > 5*time.Second { - t.Fatalf("aufruf dauerte %s", elapsed) - } -} - -func TestClientDoesNotFollowRedirects(t *testing.T) { - target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - t.Error("weiterleitungsziel wurde aufgerufen (Service-Token-Leck)") - })) - defer target.Close() - redirector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, target.URL, http.StatusTemporaryRedirect) - })) - defer redirector.Close() - - req, _ := http.NewRequest(http.MethodPost, redirector.URL, strings.NewReader("x")) - req.Header.Set("X-Service-Token", "geheim") - resp, err := Default().Do(req) - if err != nil { - t.Fatalf("do: %v", err) - } - defer func() { _ = resp.Body.Close() }() - if resp.StatusCode != http.StatusTemporaryRedirect { - t.Fatalf("status = %d, erwartet 307", resp.StatusCode) - } -} - -// Architekturtest: im Produktivcode dieses Moduls gibt es keine Aufrufe ueber -// den Standard-Client ohne Zeitlimit. -func TestNoUnboundedDefaultClientInProductionCode(t *testing.T) { - root, err := filepath.Abs(filepath.Join("..", "..")) - if err != nil { - t.Fatal(err) - } - forbidden := regexp.MustCompile(`http\.DefaultClient|http\.(Get|Post|Head|PostForm)\(`) - err = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - if n := d.Name(); n == "node_modules" || n == "testdata" || n == ".git" { - return filepath.SkipDir - } - return nil - } - if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { - return nil - } - data, err := os.ReadFile(path) - if err != nil { - return err - } - for i, line := range strings.Split(string(data), "\n") { - if t := strings.TrimSpace(line); strings.HasPrefix(t, "//") { - continue - } - if forbidden.MatchString(line) { - rel, _ := filepath.Rel(root, path) - t.Errorf("%s:%d nutzt den Standard-Client ohne Zeitlimit: %s", rel, i+1, strings.TrimSpace(line)) - } - } - return nil - }) - if err != nil { - t.Fatal(err) - } -} diff --git a/mail/internal/imap/loadtest_test.go b/mail/internal/imap/loadtest_test.go index 077f6882..6fefad47 100644 --- a/mail/internal/imap/loadtest_test.go +++ b/mail/internal/imap/loadtest_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/loadtest" + "gitea.perlbach24.de/scripte/nexarch/common/loadtest" "gitea.perlbach24.de/scripte/nexarch/mail/internal/testdb" ) diff --git a/mail/internal/indexworker/limits_test.go b/mail/internal/indexworker/limits_test.go index 9ab4982f..49722021 100644 --- a/mail/internal/indexworker/limits_test.go +++ b/mail/internal/indexworker/limits_test.go @@ -46,4 +46,3 @@ func TestCheckInputLimits_WithinLimits(t *testing.T) { t.Fatalf("unerwarteter fehler innerhalb der obergrenzen: %v", err) } } - diff --git a/mail/internal/indexworker/worker_limits_test.go b/mail/internal/indexworker/worker_limits_test.go index b32e8c99..21873b1d 100644 --- a/mail/internal/indexworker/worker_limits_test.go +++ b/mail/internal/indexworker/worker_limits_test.go @@ -208,4 +208,3 @@ func TestProcessOCR_SuccessInvokesResultHandler(t *testing.T) { t.Fatalf("erwartete status %q, habe %q", StatusSucceeded, job.Status) } } - diff --git a/mail/internal/loadtest/loadtest.go b/mail/internal/loadtest/loadtest.go deleted file mode 100644 index a6507d1e..00000000 --- a/mail/internal/loadtest/loadtest.go +++ /dev/null @@ -1,141 +0,0 @@ -// Package loadtest implementiert die für QA-07 gemeinsam genutzte -// Last- und Leistungsmessung: parallele Sessions ausführen, Latenzen -// aufzeichnen, Durchsatz und Ressourcenverbrauch bestimmen. k6/vegeta -// sind auf diesem Rechner nicht installierbar (Projektregel: keine -// zusätzlichen Toolchains/Dienste installieren) — echter Ersatz ohne -// externe Abhängigkeit: reale nebenläufige TCP-Sessions über die -// Go-Standardbibliothek, gegen die echten, in dieser Sitzung gebauten -// Protokollserver. -package loadtest - -import ( - "context" - "os" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "time" -) - -// Result fasst Latenz- und Durchsatzwerte eines Lasttest-Laufs -// zusammen (Akzeptanzkriterium 2). -type Result struct { - Protocol string - Concurrency int - Sessions int - Errors int - Duration time.Duration - Latencies []time.Duration // aufsteigend sortiert -} - -// Throughput liefert Sessions pro Sekunde über die Gesamtlaufzeit. -func (r Result) Throughput() float64 { - if r.Duration <= 0 { - return 0 - } - return float64(r.Sessions) / r.Duration.Seconds() -} - -// Percentile liefert die Latenz am p-ten Perzentil (0.0–1.0). -func (r Result) Percentile(p float64) time.Duration { - if len(r.Latencies) == 0 { - return 0 - } - idx := int(p * float64(len(r.Latencies)-1)) - if idx < 0 { - idx = 0 - } - if idx >= len(r.Latencies) { - idx = len(r.Latencies) - 1 - } - return r.Latencies[idx] -} - -// Max liefert die höchste gemessene Latenz. -func (r Result) Max() time.Duration { - if len(r.Latencies) == 0 { - return 0 - } - return r.Latencies[len(r.Latencies)-1] -} - -// Run führt totalSessions Sessions über maximal concurrency parallele -// Worker aus (Akzeptanzkriterium 1: realistische Anzahl paralleler -// Sessions) und misst je Session die Latenz von sessionFunc. -func Run(ctx context.Context, protocol string, concurrency, totalSessions int, sessionFunc func(ctx context.Context) error) Result { - var mu sync.Mutex - latencies := make([]time.Duration, 0, totalSessions) - errCount := 0 - - sem := make(chan struct{}, concurrency) - var wg sync.WaitGroup - start := time.Now() - for i := 0; i < totalSessions; i++ { - sem <- struct{}{} - wg.Add(1) - go func() { - defer wg.Done() - defer func() { <-sem }() - t0 := time.Now() - err := sessionFunc(ctx) - elapsed := time.Since(t0) - mu.Lock() - latencies = append(latencies, elapsed) - if err != nil { - errCount++ - } - mu.Unlock() - }() - } - wg.Wait() - duration := time.Since(start) - - sort.Slice(latencies, func(i, j int) bool { return latencies[i] < latencies[j] }) - return Result{ - Protocol: protocol, - Concurrency: concurrency, - Sessions: totalSessions, - Errors: errCount, - Duration: duration, - Latencies: latencies, - } -} - -// ResourceSample ist eine Momentaufnahme des Ressourcenverbrauchs -// DIESES Prozesses (Akzeptanzkriterium/Pflichtprüfung 3): Heap über die -// Go-Runtime, kumulierte CPU-Zeit über /proc/self/stat (Linux, kein -// externes Werkzeug nötig). -type ResourceSample struct { - HeapAllocBytes uint64 - CPUTimeSeconds float64 -} - -func SampleResources() ResourceSample { - var m runtime.MemStats - runtime.ReadMemStats(&m) - return ResourceSample{HeapAllocBytes: m.HeapAlloc, CPUTimeSeconds: readSelfCPUTimeSeconds()} -} - -// readSelfCPUTimeSeconds liefert die kumulierte User+System-CPU-Zeit -// dieses Prozesses in Sekunden. Liefert 0, wenn /proc/self/stat nicht -// lesbar ist (z. B. auf Nicht-Linux-Systemen) — Aufrufer behandeln das -// als "keine CPU-Messung verfügbar", kein harter Fehler. -func readSelfCPUTimeSeconds() float64 { - data, err := os.ReadFile("/proc/self/stat") - if err != nil { - return 0 - } - fields := strings.Fields(string(data)) - if len(fields) < 15 { - return 0 - } - utime, err1 := strconv.ParseFloat(fields[13], 64) - stime, err2 := strconv.ParseFloat(fields[14], 64) - if err1 != nil || err2 != nil { - return 0 - } - const clockTicksPerSecond = 100 // Linux-Standard-USER_HZ - return (utime + stime) / clockTicksPerSecond -} diff --git a/mail/internal/mailapi/mailapi.go b/mail/internal/mailapi/mailapi.go index 3565ec4b..034f7352 100644 --- a/mail/internal/mailapi/mailapi.go +++ b/mail/internal/mailapi/mailapi.go @@ -31,8 +31,8 @@ import ( "net/http" "strconv" + "gitea.perlbach24.de/scripte/nexarch/common/policyclient" "gitea.perlbach24.de/scripte/nexarch/mail/internal/moduleauth" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/policyclient" "gitea.perlbach24.de/scripte/nexarch/mail/internal/search" "gitea.perlbach24.de/scripte/nexarch/mail/internal/storage" ) diff --git a/mail/internal/mailapi/mailapi_policy_test.go b/mail/internal/mailapi/mailapi_policy_test.go index 2a2f56aa..60672f2d 100644 --- a/mail/internal/mailapi/mailapi_policy_test.go +++ b/mail/internal/mailapi/mailapi_policy_test.go @@ -6,8 +6,8 @@ import ( "net/http/httptest" "testing" + "gitea.perlbach24.de/scripte/nexarch/common/policyclient" "gitea.perlbach24.de/scripte/nexarch/mail/internal/moduleauth" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/policyclient" "gitea.perlbach24.de/scripte/nexarch/mail/internal/search" ) diff --git a/mail/internal/mailer/ing11_header_injection_test.go b/mail/internal/mailer/ing11_header_injection_test.go index bfcf3e98..bda9c07c 100644 --- a/mail/internal/mailer/ing11_header_injection_test.go +++ b/mail/internal/mailer/ing11_header_injection_test.go @@ -147,17 +147,17 @@ func TestBuild_RegularValuesRoundTrip(t *testing.T) { // fälschlich blockiert werden. func TestValidateHeaderValue_ControlCharacterFuzz(t *testing.T) { rejectCases := map[string]string{ - "CR allein": "wert\rende", - "LF allein": "wert\nende", - "CRLF": "wert\r\nende", + "CR allein": "wert\rende", + "LF allein": "wert\nende", + "CRLF": "wert\r\nende", "CRLFCRLF (Header-Doppelung)": "wert\r\n\r\nende", - "NUL": "wert\x00ende", - "BEL": "wert\x07ende", - "ESC": "wert\x1bende", - "vertical tab": "wert\x0bende", - "form feed": "wert\x0cende", + "NUL": "wert\x00ende", + "BEL": "wert\x07ende", + "ESC": "wert\x1bende", + "vertical tab": "wert\x0bende", + "form feed": "wert\x0cende", "DEL (0x7f, kein C0, aber Steuerzeichen-Grenzfall)": "wert\x7fende", - "alle C0-Steuerzeichen außer Tab kombiniert": "a\x01\x02\x03\x04\x05b", + "alle C0-Steuerzeichen außer Tab kombiniert": "a\x01\x02\x03\x04\x05b", } for name, v := range rejectCases { t.Run("reject/"+name, func(t *testing.T) { @@ -168,11 +168,11 @@ func TestValidateHeaderValue_ControlCharacterFuzz(t *testing.T) { } acceptCases := map[string]string{ - "leer": "", + "leer": "", "tab erlaubt (gefaltete header)": "wert\twert2", - "druckbares ascii": "Normaler Betreff 123", - "unicode/umlaute": "Bericht für Mandant äöü ÄÖÜ ß", - "emoji": "Bericht 📄", + "druckbares ascii": "Normaler Betreff 123", + "unicode/umlaute": "Bericht für Mandant äöü ÄÖÜ ß", + "emoji": "Bericht 📄", } for name, v := range acceptCases { t.Run("accept/"+name, func(t *testing.T) { diff --git a/mail/internal/mimeparse/mimeparse.go b/mail/internal/mimeparse/mimeparse.go index 9e66f433..968bb61e 100644 --- a/mail/internal/mimeparse/mimeparse.go +++ b/mail/internal/mimeparse/mimeparse.go @@ -37,10 +37,10 @@ var ErrAttachmentTooLarge = errors.New("mimeparse: anhang überschreitet die max // Part ist EIN zerlegter MIME-Teil — sowohl Textteile (IsAttachment // == false) als auch Anhänge (Akzeptanzkriterium 1/2). type Part struct { - ContentType string - Filename string - Size int64 - Content []byte + ContentType string + Filename string + Size int64 + Content []byte IsAttachment bool } diff --git a/mail/internal/moduletypeclient/moduletypeclient.go b/mail/internal/moduletypeclient/moduletypeclient.go index 22eb7b0e..5a4e4d00 100644 --- a/mail/internal/moduletypeclient/moduletypeclient.go +++ b/mail/internal/moduletypeclient/moduletypeclient.go @@ -12,7 +12,7 @@ import ( "fmt" "net/http" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/httpclient" + "gitea.perlbach24.de/scripte/nexarch/common/httpclient" ) const moduleName = "mail" diff --git a/mail/internal/policyclient/policyclient.go b/mail/internal/policyclient/policyclient.go deleted file mode 100644 index 16c67f95..00000000 --- a/mail/internal/policyclient/policyclient.go +++ /dev/null @@ -1,52 +0,0 @@ -// Package policyclient fragt Core (RBAC-06, POST /authorize) nach der -// Rechteentscheidung; Fehler und Ausfall fuehren immer zur Ablehnung. -package policyclient - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "time" -) - -type Client struct { - BaseURL string - ServiceToken string - HTTP *http.Client -} - -func New(baseURL, serviceToken string) *Client { - return &Client{BaseURL: strings.TrimRight(baseURL, "/"), ServiceToken: serviceToken, HTTP: &http.Client{Timeout: 5 * time.Second}} -} - -func (c *Client) Allowed(ctx context.Context, tenant, role, permission string) (bool, error) { - body := map[string]string{"role": role, "permission": permission} - if tenant != "" { - body["tenant"] = tenant - } - payload, _ := json.Marshal(body) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.BaseURL+"/authorize", bytes.NewReader(payload)) - if err != nil { - return false, err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-Service-Token", c.ServiceToken) - resp, err := c.HTTP.Do(req) - if err != nil { - return false, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return false, fmt.Errorf("policy: status %d", resp.StatusCode) - } - var out struct { - Allowed *bool `json:"allowed"` - } - if err := json.NewDecoder(http.MaxBytesReader(nil, resp.Body, 1<<16)).Decode(&out); err != nil || out.Allowed == nil { - return false, fmt.Errorf("policy: antwort ohne entscheidung") - } - return *out.Allowed, nil -} diff --git a/mail/internal/pop3/loadtest_test.go b/mail/internal/pop3/loadtest_test.go index 4493b5f9..43de6fae 100644 --- a/mail/internal/pop3/loadtest_test.go +++ b/mail/internal/pop3/loadtest_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/loadtest" + "gitea.perlbach24.de/scripte/nexarch/common/loadtest" "gitea.perlbach24.de/scripte/nexarch/mail/internal/testdb" ) diff --git a/mail/internal/smtp/loadtest_test.go b/mail/internal/smtp/loadtest_test.go index f274285a..5af13705 100644 --- a/mail/internal/smtp/loadtest_test.go +++ b/mail/internal/smtp/loadtest_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/loadtest" + "gitea.perlbach24.de/scripte/nexarch/common/loadtest" "gitea.perlbach24.de/scripte/nexarch/mail/internal/testdb" ) diff --git a/mail/internal/storage/usagereport.go b/mail/internal/storage/usagereport.go index a8a5c14d..8deb3299 100644 --- a/mail/internal/storage/usagereport.go +++ b/mail/internal/storage/usagereport.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/httpclient" + "gitea.perlbach24.de/scripte/nexarch/common/httpclient" "net/http" ) diff --git a/mail/internal/syncalert/dispatcher.go b/mail/internal/syncalert/dispatcher.go index 30188a9b..800df3da 100644 --- a/mail/internal/syncalert/dispatcher.go +++ b/mail/internal/syncalert/dispatcher.go @@ -12,7 +12,7 @@ import ( "context" "encoding/json" "fmt" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/httpclient" + "gitea.perlbach24.de/scripte/nexarch/common/httpclient" "net/http" ) diff --git a/mail/internal/workflowevent/client.go b/mail/internal/workflowevent/client.go index f93c6867..9b51fc01 100644 --- a/mail/internal/workflowevent/client.go +++ b/mail/internal/workflowevent/client.go @@ -8,7 +8,7 @@ import ( "io" "net/http" - "gitea.perlbach24.de/scripte/nexarch/mail/internal/httpclient" + "gitea.perlbach24.de/scripte/nexarch/common/httpclient" ) // Config steuert Ziel und Zugangsdaten des Workflow-Modul-Adapters