100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package pii
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/veylant/ia-gateway/internal/apierror"
|
|
)
|
|
|
|
// AnalyzeRequest is the JSON body accepted by POST /v1/pii/analyze.
|
|
type AnalyzeRequest struct {
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
// AnalyzeEntity is a single PII entity returned by the analyze endpoint.
|
|
type AnalyzeEntity struct {
|
|
Type string `json:"type"`
|
|
Start int `json:"start"`
|
|
End int `json:"end"`
|
|
Confidence float64 `json:"confidence"`
|
|
Layer string `json:"layer"`
|
|
}
|
|
|
|
// AnalyzeResponse is the JSON response of POST /v1/pii/analyze.
|
|
type AnalyzeResponse struct {
|
|
Anonymized string `json:"anonymized"`
|
|
Entities []AnalyzeEntity `json:"entities"`
|
|
}
|
|
|
|
// AnalyzeHandler wraps a pii.Client as an HTTP handler for the playground.
|
|
// It is safe to call when client is nil: returns the original text unchanged.
|
|
type AnalyzeHandler struct {
|
|
client *Client
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewAnalyzeHandler creates a new AnalyzeHandler.
|
|
// client may be nil (PII service disabled) — the handler degrades gracefully.
|
|
func NewAnalyzeHandler(client *Client, logger *zap.Logger) *AnalyzeHandler {
|
|
return &AnalyzeHandler{client: client, logger: logger}
|
|
}
|
|
|
|
func (h *AnalyzeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
var req AnalyzeRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
apierror.WriteError(w, apierror.NewBadRequestError("invalid JSON: "+err.Error()))
|
|
return
|
|
}
|
|
if req.Text == "" {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(AnalyzeResponse{Anonymized: "", Entities: []AnalyzeEntity{}})
|
|
return
|
|
}
|
|
|
|
// PII service disabled — return text unchanged.
|
|
if h.client == nil {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(AnalyzeResponse{
|
|
Anonymized: req.Text,
|
|
Entities: []AnalyzeEntity{},
|
|
})
|
|
return
|
|
}
|
|
|
|
resp, err := h.client.Detect(r.Context(), req.Text, "playground", "playground-analyze", true, false)
|
|
if err != nil {
|
|
h.logger.Warn("PII analyze failed", zap.Error(err))
|
|
// Fail-open: return text unchanged rather than erroring.
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(AnalyzeResponse{
|
|
Anonymized: req.Text,
|
|
Entities: []AnalyzeEntity{},
|
|
})
|
|
return
|
|
}
|
|
|
|
entities := make([]AnalyzeEntity, 0, len(resp.Entities))
|
|
for _, e := range resp.Entities {
|
|
entities = append(entities, AnalyzeEntity{
|
|
Type: e.EntityType,
|
|
Start: int(e.Start),
|
|
End: int(e.End),
|
|
Confidence: float64(e.Confidence),
|
|
Layer: e.DetectionLayer,
|
|
})
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(AnalyzeResponse{
|
|
Anonymized: resp.AnonymizedText,
|
|
Entities: entities,
|
|
})
|
|
}
|