- Go service bridging deepseek.de/es/fr V4 models behind an OpenAI-compatible API - /v1/chat/completions, /v1/responses, /v1/messages (Anthropic) with tool calling - web admin console: dashboard, models/sites, keys, proxies, Turnstile, playground, live logs - Turnstile solvers: solver API / local browser via CDP / manual cookie import - MIT license, README with screenshots, Dockerfile and Makefile
102 lines
1.9 KiB
Go
102 lines
1.9 KiB
Go
package admin
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/nyoungo/dsfree2api/internal/logbuf"
|
|
)
|
|
|
|
// handleLogs serves the ring buffer either as a one-shot JSON payload or as a
|
|
// live SSE stream (?stream=1).
|
|
func (s *Server) handleLogs(w http.ResponseWriter, r *http.Request) {
|
|
q := r.URL.Query()
|
|
level := q.Get("level")
|
|
if level == "" {
|
|
level = "INFO"
|
|
}
|
|
limit := 200
|
|
if v := q.Get("limit"); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 500 {
|
|
limit = n
|
|
}
|
|
}
|
|
|
|
if q.Get("stream") != "1" {
|
|
writeJSON(w, http.StatusOK, map[string]any{"entries": s.logs.Recent(level, limit)})
|
|
return
|
|
}
|
|
|
|
flusher, ok := w.(http.Flusher)
|
|
if !ok {
|
|
writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "streaming unsupported"})
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
|
|
w.Header().Set("Cache-Control", "no-cache")
|
|
w.Header().Set("X-Accel-Buffering", "no")
|
|
|
|
for _, e := range s.logs.Recent(level, limit) {
|
|
writeLog(w, e)
|
|
}
|
|
flusher.Flush()
|
|
|
|
ch, unsub := s.logs.Subscribe()
|
|
defer unsub()
|
|
minLevel := level
|
|
ticker := time.NewTicker(15 * time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-r.Context().Done():
|
|
return
|
|
case e, ok := <-ch:
|
|
if !ok {
|
|
return
|
|
}
|
|
if !levelOK(e.Level, minLevel) {
|
|
continue
|
|
}
|
|
writeLog(w, e)
|
|
flusher.Flush()
|
|
case <-ticker.C:
|
|
fmt.Fprint(w, ": ping\n\n")
|
|
flusher.Flush()
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeLog(w http.ResponseWriter, e logbuf.Entry) {
|
|
raw, err := json.Marshal(e)
|
|
if err != nil {
|
|
return
|
|
}
|
|
fmt.Fprintf(w, "data: %s\n\n", raw)
|
|
}
|
|
|
|
func levelOK(actual, min string) bool {
|
|
return levelVal(actual) >= levelVal(min)
|
|
}
|
|
|
|
func levelVal(l string) int {
|
|
switch l {
|
|
case "DEBUG":
|
|
return -4
|
|
case "INFO":
|
|
return 0
|
|
case "WARN":
|
|
return 4
|
|
case "ERROR":
|
|
return 8
|
|
default:
|
|
return -100
|
|
}
|
|
}
|
|
|
|
func randRead(b []byte) (int, error) { return rand.Read(b) }
|