feat: complete 2.0.7.12 platform overhaul

This commit is contained in:
2026-08-16 19:33:03 +08:00
parent 73555cd04c
commit c9fa6f7a88
159 changed files with 13243 additions and 2539 deletions
@@ -4,17 +4,21 @@ import (
"context"
"encoding/json"
"errors"
"log"
"net"
"net/http"
"strings"
"time"
"ymhut-box/server/unified-management/internal/adminassets"
"ymhut-box/server/unified-management/internal/auth"
"ymhut-box/server/unified-management/internal/config"
"ymhut-box/server/unified-management/internal/db"
"ymhut-box/server/unified-management/internal/feedback"
"ymhut-box/server/unified-management/internal/health"
"ymhut-box/server/unified-management/internal/legacy"
"ymhut-box/server/unified-management/internal/notices"
"ymhut-box/server/unified-management/internal/reference"
"ymhut-box/server/unified-management/internal/releases"
"ymhut-box/server/unified-management/internal/sources"
"ymhut-box/server/unified-management/internal/synclegacy"
@@ -29,8 +33,12 @@ type router struct {
sources *sources.Service
legacy *legacy.Service
notices *notices.Service
referenceData *reference.Service
syncer *synclegacy.Service
publicSnapshots *publicSnapshotService
adminAssets *adminassets.Service
health *health.Service
dashboardCache *dashboardSnapshotCache
}
const loginRequestTimeout = 8 * time.Second
@@ -44,7 +52,17 @@ func NewRouter(cfg *config.Config, store *db.Store, authService *auth.Service, f
releases: releaseService,
sources: sourceService,
legacy: legacyService,
referenceData: reference.NewService(cfg.UpdatePublicDir),
publicSnapshots: newPublicSnapshotService(60 * time.Second),
adminAssets: adminassets.New(cfg.AdminAssetMode, cfg.AdminWebDir, config.AdminBuildID),
health: health.NewService(cfg, store),
dashboardCache: newDashboardSnapshotCache(5 * time.Second),
}
assetStatus := r.adminAssets.Diagnostics()
if assetStatus.Ready {
log.Printf("admin assets: mode=%s build=%s manifest=%s entries=%d", assetStatus.Mode, assetStatus.BuildID, assetStatus.ManifestStatus, assetStatus.ManifestEntries)
} else {
log.Printf("admin assets unavailable: mode=%s source=%s error=%s", assetStatus.Mode, assetStatus.Source, assetStatus.ValidationError)
}
for _, item := range optional {
switch typed := item.(type) {
@@ -54,11 +72,32 @@ func NewRouter(cfg *config.Config, store *db.Store, authService *auth.Service, f
r.syncer = typed
}
}
if releaseService != nil {
releaseService.SetChangeCallback(func() {
r.publicSnapshots.Invalidate()
r.dashboardCache.Invalidate()
})
}
if sourceService != nil {
sourceService.SetChangeCallback(r.dashboardCache.Invalidate)
}
return withSecurity(r)
}
func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.RemoteAddr = clientAddress(req)
path := cleanPath(req.URL.Path)
if strings.HasPrefix(path, "/api/admin/") {
w.Header().Set("X-Admin-Build-ID", r.adminAssets.Diagnostics().BuildID)
if path != "/api/admin/events" {
diagnostics := &diagnosticResponseWriter{ResponseWriter: w, status: http.StatusOK}
w = diagnostics
started := time.Now()
defer func() {
log.Printf("admin_api method=%s path=%s status=%d duration_ms=%d cache=%s build=%s", req.Method, path, diagnostics.status, time.Since(started).Milliseconds(), firstNonEmpty(w.Header().Get("X-Admin-Cache"), "n/a"), r.adminAssets.Diagnostics().BuildID)
}()
}
}
if strings.HasPrefix(path, "/api/admin/") &&
path != "/api/admin/auth/login" &&
path != "/api/admin/auth/logout" &&
@@ -70,6 +109,7 @@ func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
r.publicSnapshots.Invalidate()
r.dashboardCache.Invalidate()
if r.sources != nil {
r.sources.PublishEvent(adminMutationEvent(path), map[string]any{"path": path, "method": req.Method, "time": time.Now().UTC().Format(time.RFC3339)})
}
@@ -110,6 +150,8 @@ func (r *router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.handleClientEndpoints(w, req)
case path == "/api/client/notices" || strings.HasPrefix(path, "/api/client/notices/"):
r.handleClientNotices(w, req)
case strings.HasPrefix(path, "/api/client/reference-data/"):
r.handleClientReferenceData(w, req)
case path == "/api/client/endpoint-calls" || path == "/api/client/source-calls":
r.handleSourceCall(w, req)
case path == "/update-info.json" || path == "/update-info":
@@ -160,6 +202,16 @@ type mutationResponseWriter struct {
status int
}
type diagnosticResponseWriter struct {
http.ResponseWriter
status int
}
func (w *diagnosticResponseWriter) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
}
func (w *mutationResponseWriter) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
@@ -181,11 +233,13 @@ func adminMutationEvent(path string) string {
}
func (r *router) handleAuthBootstrap(w http.ResponseWriter, req *http.Request) {
payload, err := r.auth.Bootstrap(req.Context())
_, _, authenticated := r.auth.UserForRequest(req)
payload, err := r.auth.Bootstrap(req.Context(), authenticated)
if err != nil {
writeError(w, http.StatusInternalServerError, "BOOTSTRAP_FAILED", err)
return
}
w.Header().Set("Cache-Control", "no-store")
writeJSON(w, http.StatusOK, payload)
}
@@ -264,6 +318,35 @@ func remoteHost(remoteAddress string) string {
return strings.TrimSpace(remoteAddress)
}
func clientAddress(req *http.Request) string {
if req == nil {
return ""
}
peer := remoteHost(req.RemoteAddr)
peerIP := net.ParseIP(strings.Trim(peer, "[]"))
if peerIP == nil || !peerIP.IsLoopback() {
return peer
}
if forwarded := validClientIP(req.Header.Get("X-Real-IP")); forwarded != "" {
return forwarded
}
values := strings.Split(req.Header.Get("X-Forwarded-For"), ",")
for index := len(values) - 1; index >= 0; index-- {
if forwarded := validClientIP(values[index]); forwarded != "" {
return forwarded
}
}
return peer
}
func validClientIP(value string) string {
value = strings.Trim(strings.TrimSpace(value), "[]")
if parsed := net.ParseIP(value); parsed != nil {
return parsed.String()
}
return ""
}
func (r *router) recordLoginAudit(username, remoteAddr, userAgent string) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()