Download internal/devcapture/store.go from a3216/ds2api: direct link, hf CLI and curl.
- Browser
- Download file 5.33 kB
-
https://huggingface.co/spaces/a3216/ds2api/resolve/ce5a019598af8a3a8fa70d3880098df3e850ecd7/internal/devcapture/store.go
- Command line
-
hf download hf://spaces/a3216/ds2api@ce5a019598af8a3a8fa70d3880098df3e850ecd7/internal/devcapture/store.go
-
curl -L -o store.go https://huggingface.co/spaces/a3216/ds2api/resolve/ce5a019598af8a3a8fa70d3880098df3e850ecd7/internal/devcapture/store.go
5.33 kB
| package devcapture | |
| import ( | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "os" | |
| "strconv" | |
| "strings" | |
| "sync" | |
| "time" | |
| "ds2api/internal/util" | |
| "github.com/google/uuid" | |
| ) | |
| const ( | |
| defaultLimit = 20 | |
| defaultMaxBodyBytes = 5 * 1024 * 1024 | |
| maxLimit = 50 | |
| ) | |
| type Entry struct { | |
| ID string `json:"id"` | |
| CreatedAt int64 `json:"created_at"` | |
| Label string `json:"label"` | |
| URL string `json:"url"` | |
| AccountID string `json:"account_id,omitempty"` | |
| StatusCode int `json:"status_code"` | |
| RequestBody string `json:"request_body"` | |
| ResponseBody string `json:"response_body"` | |
| ResponseTruncated bool `json:"response_truncated"` | |
| } | |
| type Store struct { | |
| mu sync.Mutex | |
| enabled bool | |
| limit int | |
| maxBodyBytes int | |
| items []Entry | |
| } | |
| type Session struct { | |
| store *Store | |
| id string | |
| createdAt int64 | |
| label string | |
| url string | |
| accountID string | |
| requestRaw string | |
| } | |
| type captureBody struct { | |
| rc io.ReadCloser | |
| s *Session | |
| statusCode int | |
| buf strings.Builder | |
| truncated bool | |
| finalized bool | |
| } | |
| var ( | |
| globalOnce sync.Once | |
| globalInst *Store | |
| ) | |
| func Global() *Store { | |
| globalOnce.Do(func() { | |
| globalInst = NewFromEnv() | |
| }) | |
| return globalInst | |
| } | |
| func NewFromEnv() *Store { | |
| // Security: dev packet capture stores full request/response bodies (up to | |
| // 5MB each) in memory and exposes them via /admin/dev/captures. Default | |
| // to disabled everywhere (including HF Spaces and local dev) to avoid | |
| // silently capturing user prompts and DeepSeek responses. Operators who | |
| // need this for debugging must opt in with DS2API_DEV_PACKET_CAPTURE=1. | |
| enabled := false | |
| if raw, ok := os.LookupEnv("DS2API_DEV_PACKET_CAPTURE"); ok { | |
| enabled = parseBool(raw) | |
| } | |
| limit := parseIntWithDefault(os.Getenv("DS2API_DEV_PACKET_CAPTURE_LIMIT"), defaultLimit) | |
| if limit < 1 { | |
| limit = defaultLimit | |
| } | |
| if limit > maxLimit { | |
| limit = maxLimit | |
| } | |
| maxBodyBytes := parseIntWithDefault(os.Getenv("DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES"), defaultMaxBodyBytes) | |
| if maxBodyBytes < 1024 { | |
| maxBodyBytes = defaultMaxBodyBytes | |
| } | |
| return &Store{ | |
| enabled: enabled, | |
| limit: limit, | |
| maxBodyBytes: maxBodyBytes, | |
| items: make([]Entry, 0, limit), | |
| } | |
| } | |
| func (s *Store) Enabled() bool { | |
| if s == nil { | |
| return false | |
| } | |
| return s.enabled | |
| } | |
| func (s *Store) Limit() int { | |
| if s == nil { | |
| return defaultLimit | |
| } | |
| return s.limit | |
| } | |
| func (s *Store) MaxBodyBytes() int { | |
| if s == nil { | |
| return defaultMaxBodyBytes | |
| } | |
| return s.maxBodyBytes | |
| } | |
| func (s *Store) Snapshot() []Entry { | |
| if s == nil { | |
| return nil | |
| } | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| out := make([]Entry, len(s.items)) | |
| copy(out, s.items) | |
| return out | |
| } | |
| func (s *Store) Clear() { | |
| if s == nil { | |
| return | |
| } | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| s.items = s.items[:0] | |
| } | |
| func (s *Store) Start(label, url, accountID string, requestPayload any) *Session { | |
| if s == nil || !s.enabled { | |
| return nil | |
| } | |
| return &Session{ | |
| store: s, | |
| id: "cap_" + strings.ReplaceAll(uuid.NewString(), "-", ""), | |
| createdAt: time.Now().Unix(), | |
| label: strings.TrimSpace(label), | |
| url: strings.TrimSpace(url), | |
| accountID: strings.TrimSpace(accountID), | |
| requestRaw: marshalPayload(requestPayload), | |
| } | |
| } | |
| func (s *Session) WrapBody(rc io.ReadCloser, statusCode int) io.ReadCloser { | |
| if s == nil || rc == nil { | |
| return rc | |
| } | |
| return &captureBody{ | |
| rc: rc, | |
| s: s, | |
| statusCode: statusCode, | |
| } | |
| } | |
| func (c *captureBody) Read(p []byte) (int, error) { | |
| n, err := c.rc.Read(p) | |
| if n > 0 { | |
| c.append(string(p[:n])) | |
| } | |
| if err == io.EOF { | |
| c.finalize() | |
| } | |
| return n, err | |
| } | |
| func (c *captureBody) Close() error { | |
| err := c.rc.Close() | |
| c.finalize() | |
| return err | |
| } | |
| func (c *captureBody) append(chunk string) { | |
| if chunk == "" || c.s == nil || c.s.store == nil { | |
| return | |
| } | |
| maxLen := c.s.store.maxBodyBytes | |
| current := c.buf.Len() | |
| if current >= maxLen { | |
| c.truncated = true | |
| return | |
| } | |
| remain := maxLen - current | |
| if len(chunk) > remain { | |
| truncated, _ := util.TruncateUTF8Bytes(chunk, remain) | |
| c.buf.WriteString(truncated) | |
| c.truncated = true | |
| return | |
| } | |
| c.buf.WriteString(chunk) | |
| } | |
| func (c *captureBody) finalize() { | |
| if c.finalized || c.s == nil || c.s.store == nil { | |
| return | |
| } | |
| c.finalized = true | |
| entry := Entry{ | |
| ID: c.s.id, | |
| CreatedAt: c.s.createdAt, | |
| Label: c.s.label, | |
| URL: c.s.url, | |
| AccountID: c.s.accountID, | |
| StatusCode: c.statusCode, | |
| RequestBody: c.s.requestRaw, | |
| ResponseBody: c.buf.String(), | |
| ResponseTruncated: c.truncated, | |
| } | |
| c.s.store.push(entry) | |
| } | |
| func (s *Store) push(entry Entry) { | |
| s.mu.Lock() | |
| defer s.mu.Unlock() | |
| s.items = append([]Entry{entry}, s.items...) | |
| if len(s.items) > s.limit { | |
| s.items = s.items[:s.limit] | |
| } | |
| } | |
| func marshalPayload(v any) string { | |
| b, err := json.Marshal(v) | |
| if err != nil { | |
| return fmt.Sprintf("%v", v) | |
| } | |
| return string(b) | |
| } | |
| func parseBool(v string) bool { | |
| switch strings.ToLower(strings.TrimSpace(v)) { | |
| case "1", "true", "yes", "on": | |
| return true | |
| default: | |
| return false | |
| } | |
| } | |
| func parseIntWithDefault(raw string, d int) int { | |
| raw = strings.TrimSpace(raw) | |
| if raw == "" { | |
| return d | |
| } | |
| n, err := strconv.Atoi(raw) | |
| if err != nil { | |
| return d | |
| } | |
| return n | |
| } | |