package event import ( "bytes" "context" "encoding/json" "fmt" "log" "net/http" "time" "tianyan-edge/internal/config" ) const maxBuffer = 500 type Uploader struct { cfg *config.Config events <-chan SuspectedEvent buf []SuspectedEvent } func NewUploader(cfg *config.Config, events <-chan SuspectedEvent) *Uploader { return &Uploader{cfg: cfg, events: events} } func (u *Uploader) Run(ctx context.Context) { client := &http.Client{Timeout: 10 * time.Second} ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return case ev := <-u.events: if !u.upload(client, ev) { u.buffer(ev) } case <-ticker.C: u.flushBuffer(client) } } } func (u *Uploader) buffer(ev SuspectedEvent) { if len(u.buf) < maxBuffer { u.buf = append(u.buf, ev) } else { log.Println("uploader: offline buffer full, dropping event") } } func (u *Uploader) flushBuffer(client *http.Client) { remaining := u.buf[:0] for _, ev := range u.buf { if !u.upload(client, ev) { remaining = append(remaining, ev) } } u.buf = remaining } func (u *Uploader) upload(client *http.Client, ev SuspectedEvent) bool { url := fmt.Sprintf("%s/api/v1/edge/events/suspected", u.cfg.CloudURL) body, _ := json.Marshal(ev) req, _ := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+u.cfg.EdgeToken) resp, err := client.Do(req) if err != nil { log.Printf("uploader: upload error: %v", err) return false } defer resp.Body.Close() ok := resp.StatusCode == 200 || resp.StatusCode == 201 if !ok { log.Printf("uploader: upload failed status=%d", resp.StatusCode) } return ok }