继续更新 update 门户站点界面和功能

This commit is contained in:
QWQLwToo
2026-06-26 14:33:43 +08:00
parent cd2fd435a2
commit 2171b933eb
5 changed files with 286 additions and 19 deletions
@@ -116,6 +116,8 @@ func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.auth.Require(http.HandlerFunc(r.handleAdminSources)).ServeHTTP(w, req)
case strings.HasPrefix(path, "/api/admin/endpoints"):
r.auth.Require(http.HandlerFunc(r.handleAdminEndpoints)).ServeHTTP(w, req)
case path == "/api/admin/events":
r.auth.Require(http.HandlerFunc(r.handleAdminEvents)).ServeHTTP(w, req)
case strings.HasPrefix(path, "/api/admin/legacy"):
r.auth.Require(http.HandlerFunc(r.handleAdminLegacy)).ServeHTTP(w, req)
case strings.HasPrefix(path, "/api/admin/database"):
@@ -637,6 +639,38 @@ func (r *router) handleAdminEndpoints(w http.ResponseWriter, req *http.Request)
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": items})
}
func (r *router) handleAdminEvents(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet {
writeError(w, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", errors.New("GET required"))
return
}
flusher, ok := w.(http.Flusher)
if !ok {
writeError(w, http.StatusInternalServerError, "SSE_UNSUPPORTED", errors.New("streaming is not supported"))
return
}
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
events := r.sources.Events()
ticker := time.NewTicker(15 * time.Second)
defer ticker.Stop()
writeSSE(w, "ready", map[string]any{"ok": true, "time": time.Now().UTC().Format(time.RFC3339)})
flusher.Flush()
for {
select {
case event := <-events:
writeSSE(w, event.Type, event.Data)
flusher.Flush()
case <-ticker.C:
writeSSE(w, "heartbeat", map[string]any{"time": time.Now().UTC().Format(time.RFC3339)})
flusher.Flush()
case <-req.Context().Done():
return
}
}
}
func (r *router) handleAdminReleases(w http.ResponseWriter, req *http.Request) {
path := cleanPath(req.URL.Path)
if strings.HasPrefix(path, "/api/admin/releases/notices") {
@@ -786,8 +820,17 @@ func (r *router) handleAdminSources(w http.ResponseWriter, req *http.Request) {
}
writeJSON(w, http.StatusOK, map[string]any{"ok": true})
case req.Method == http.MethodPost && path == "/api/admin/sources/check":
go r.sources.CheckDue(req.Context())
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "queued": true})
job := r.sources.QueueCheckAll()
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "queued": true, "jobId": job.ID, "job": job})
case req.Method == http.MethodGet && path == "/api/admin/sources/check/status":
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": r.sources.CheckJobs()})
case req.Method == http.MethodGet && strings.HasPrefix(path, "/api/admin/sources/check/status/"):
jobID := strings.TrimPrefix(path, "/api/admin/sources/check/status/")
if job, ok := r.sources.CheckJob(jobID); ok {
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "job": job})
return
}
writeError(w, http.StatusNotFound, "CHECK_JOB_NOT_FOUND", errors.New("check job not found"))
case req.Method == http.MethodPost && strings.HasPrefix(path, "/api/admin/sources/") && strings.HasSuffix(path, "/check"):
sourceID := strings.TrimSuffix(strings.TrimPrefix(path, "/api/admin/sources/"), "/check")
item, err := r.sources.CheckSourceID(req.Context(), sourceID)
@@ -967,6 +1010,12 @@ func writeJSON(w http.ResponseWriter, status int, payload any) {
_ = json.NewEncoder(w).Encode(payload)
}
func writeSSE(w http.ResponseWriter, event string, payload any) {
data, _ := json.Marshal(payload)
_, _ = w.Write([]byte("event: " + event + "\n"))
_, _ = w.Write([]byte("data: " + string(data) + "\n\n"))
}
func writeError(w http.ResponseWriter, status int, code string, err error) {
message := ""
if err != nil {