// Package dsms provides a client for archiving compliance artifacts to the // DSMS gateway (IPFS-backed tamper-proof document storage). package dsms import ( "bytes" "encoding/json" "fmt" "io" "log" "mime/multipart" "net/http" "os" "time" ) // ArchiveResult is the response from a successful DSMS archive operation. type ArchiveResult struct { CID string `json:"cid"` Size int `json:"size"` GatewayURL string `json:"gateway_url"` } var gatewayURL = getEnv("DSMS_GATEWAY_URL", "http://dsms-gateway:8082") func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } // Archive stores content in DSMS and returns the CID. // Non-critical: returns nil on failure (logs warning). func Archive(content []byte, filename, documentType, documentID, version string) *ArchiveResult { var buf bytes.Buffer w := multipart.NewWriter(&buf) part, err := w.CreateFormFile("file", filename) if err != nil { log.Printf("[dsms] multipart error: %v", err) return nil } if _, err := part.Write(content); err != nil { log.Printf("[dsms] write error: %v", err) return nil } _ = w.WriteField("document_type", documentType) _ = w.WriteField("document_id", documentID) _ = w.WriteField("version", version) _ = w.WriteField("language", "de") w.Close() client := &http.Client{Timeout: 60 * time.Second} req, err := http.NewRequest("POST", gatewayURL+"/api/v1/documents", &buf) if err != nil { log.Printf("[dsms] request error: %v", err) return nil } req.Header.Set("Content-Type", w.FormDataContentType()) req.Header.Set("Authorization", "Bearer system-backend") resp, err := client.Do(req) if err != nil { log.Printf("[dsms] unavailable: %v", err) return nil } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { log.Printf("[dsms] archive failed (%d): %s", resp.StatusCode, string(body[:min(len(body), 200)])) return nil } var result ArchiveResult if err := json.Unmarshal(body, &result); err != nil { log.Printf("[dsms] parse error: %v", err) return nil } log.Printf("[dsms] archived: %s → CID %s (%d bytes)", filename, result.CID, result.Size) return &result } // Verify checks document integrity by CID. func Verify(cid string) (bool, error) { client := &http.Client{Timeout: 15 * time.Second} resp, err := client.Get(fmt.Sprintf("%s/api/v1/verify/%s", gatewayURL, cid)) if err != nil { return false, err } defer resp.Body.Close() return resp.StatusCode == 200, nil } func min(a, b int) int { if a < b { return a } return b }