feat: sync deployed v1.0.0-rc.38-custom.42

Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
cneicy committed 2026-09-26 04:49:11 +08:00
1 parent aa6c3f50f4
commit ceddfd3d6b
828 files changed
+122253 -13702

No files matched your search

+83 -146
View File
@@ -2,9 +2,7 @@ package controller
import (
"context"
"errors"
"net/http"
"strconv"
"strings"
"sync"
"time"
@@ -15,29 +13,31 @@ import (
"github.com/QuantumNous/new-api/service"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
const (
requestTimeout = 30 * time.Second
httpTimeout = 10 * time.Second
uptimeKeySuffix = "_24"
apiStatusPath = "/api/status-page/"
apiHeartbeatPath = "/api/status-page/heartbeat/"
)
const requestTimeout = 30 * time.Second
type Monitor struct {
Metrics *perfmetrics.GroupResult `json:"metrics,omitempty"`
Name string `json:"name"`
Uptime float64 `json:"uptime"`
Status int `json:"status"`
Group string `json:"group,omitempty"`
Ping int `json:"ping,omitempty"`
LastChecked string `json:"last_checked,omitempty"`
History []MonitorSample `json:"history,omitempty"`
CacheStats *model.GroupCacheStats `json:"cache_stats,omitempty"`
ProbeStatus int `json:"probe_status"`
ChannelDisabled bool `json:"channel_disabled"`
ProbeHistory []service.ProbeHistoryPoint `json:"probe_history"`
ID string `json:"id"`
Models []string `json:"models"`
Alias string `json:"alias,omitempty"`
Hidden bool `json:"hidden,omitempty"`
Pelican *service.PelicanSummary `json:"pelican,omitempty"`
StatusSource string `json:"status_source"`
StatusCheckedAt int64 `json:"status_checked_at,omitempty"`
Fingerprint *service.AstraSamplingSummary `json:"fingerprint,omitempty"`
Metrics *perfmetrics.GroupResult `json:"metrics,omitempty"`
Name string `json:"name"`
Uptime float64 `json:"uptime"`
Status int `json:"status"`
Group string `json:"group,omitempty"`
Ping int `json:"ping,omitempty"`
LastChecked string `json:"last_checked,omitempty"`
History []MonitorSample `json:"history,omitempty"`
CacheStats *model.GroupCacheStats `json:"cache_stats,omitempty"`
ProbeStatus int `json:"probe_status"`
ChannelDisabled bool `json:"channel_disabled"`
}
type MonitorSample struct {
@@ -51,113 +51,6 @@ type UptimeGroupResult struct {
Monitors []Monitor `json:"monitors"`
}
func getAndDecode(ctx context.Context, client *http.Client, url string, dest any) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("non-200 status")
}
return common.DecodeJson(resp.Body, dest)
}
func fetchGroupData(ctx context.Context, client *http.Client, groupConfig map[string]any) UptimeGroupResult {
url, _ := groupConfig["url"].(string)
slug, _ := groupConfig["slug"].(string)
categoryName, _ := groupConfig["categoryName"].(string)
result := UptimeGroupResult{
CategoryName: categoryName,
Monitors: []Monitor{},
}
if url == "" || slug == "" {
return result
}
baseURL := strings.TrimSuffix(url, "/")
var statusData struct {
PublicGroupList []struct {
ID int `json:"id"`
Name string `json:"name"`
MonitorList []struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"monitorList"`
} `json:"publicGroupList"`
}
var heartbeatData struct {
HeartbeatList map[string][]struct {
Status int `json:"status"`
Ping int `json:"ping"`
Time string `json:"time"`
} `json:"heartbeatList"`
UptimeList map[string]float64 `json:"uptimeList"`
}
g, gCtx := errgroup.WithContext(ctx)
g.Go(func() error {
return getAndDecode(gCtx, client, baseURL+apiStatusPath+slug, &statusData)
})
g.Go(func() error {
return getAndDecode(gCtx, client, baseURL+apiHeartbeatPath+slug, &heartbeatData)
})
if g.Wait() != nil {
return result
}
for _, pg := range statusData.PublicGroupList {
if len(pg.MonitorList) == 0 {
continue
}
for _, m := range pg.MonitorList {
monitor := Monitor{
Name: m.Name,
Group: pg.Name,
Status: -1,
}
monitorID := strconv.Itoa(m.ID)
if uptime, exists := heartbeatData.UptimeList[monitorID+uptimeKeySuffix]; exists {
monitor.Uptime = uptime
}
if heartbeats, exists := heartbeatData.HeartbeatList[monitorID]; exists && len(heartbeats) > 0 {
latest := heartbeats[len(heartbeats)-1]
monitor.Status = latest.Status
monitor.Ping = latest.Ping
monitor.LastChecked = latest.Time
monitor.History = make([]MonitorSample, 0, len(heartbeats))
for _, heartbeat := range heartbeats {
monitor.History = append(monitor.History, MonitorSample{
Status: heartbeat.Status,
Ping: heartbeat.Ping,
Time: heartbeat.Time,
})
}
}
result.Monitors = append(result.Monitors, monitor)
}
}
return result
}
var groupCacheSnapshot struct {
sync.Mutex
updated time.Time
@@ -168,18 +61,35 @@ var groupCacheSnapshot struct {
const requestStatusFreshness = 2 * time.Hour
func currentGroupMonitors(ctx context.Context, results []UptimeGroupResult) ([]UptimeGroupResult, error) {
func currentGroupMonitors(ctx context.Context, admin bool) ([]UptimeGroupResult, error) {
channels, err := model.GetStatusChannels(ctx)
if err != nil {
return nil, err
}
settings, err := model.GetStatusPageGroups(ctx)
if err != nil {
return nil, err
}
models, err := model.GetStatusPageModels(ctx, channels)
if err != nil {
return nil, err
}
usable := service.GetUserUsableGroups("")
now := time.Now()
fingerprints, fingerprintErr := service.AstraSamplingSummaries(ctx, now, settings)
if fingerprintErr != nil {
return nil, fingerprintErr
}
detections, detectionErr := service.GroupDetectionSummaries(ctx, now, fingerprints, settings)
if detectionErr != nil {
return nil, detectionErr
}
active := make(map[string]bool)
order := make([]string, 0)
for _, channel := range channels {
for _, group := range strings.Split(channel.Group, ",") {
group = strings.TrimSpace(group)
if _, visible := usable[group]; !visible {
if _, visible := usable[group]; (!visible || settings[group].Hidden) && !admin {
continue
}
if _, exists := active[group]; !exists {
@@ -202,8 +112,8 @@ func currentGroupMonitors(ctx context.Context, results []UptimeGroupResult) ([]U
}
groupCacheSnapshot.stats = stats
names := []string{}
for _, item := range model.GetPricing() {
names = append(names, item.ModelName)
if err := model.DB.WithContext(ctx).Model(&model.Ability{}).Where("enabled = ?", true).Distinct().Pluck("model", &names).Error; err != nil {
return nil, err
}
performance, metricsErr := perfmetrics.QueryGroups(names, 24)
if metricsErr != nil {
@@ -216,21 +126,26 @@ func currentGroupMonitors(ctx context.Context, results []UptimeGroupResult) ([]U
groupCacheSnapshot.updated = now
groupCacheSnapshot.channels = string(encoded)
}
byName := make(map[string]Monitor)
for _, result := range results {
for _, monitor := range result.Monitors {
if previous, exists := byName[monitor.Name]; !exists || (monitor.Status == 1 && previous.Status != 1) {
byName[monitor.Name] = monitor
}
}
}
successes := perfmetrics.LatestUserSuccess(ctx, order)
monitors := make([]Monitor, 0, len(order))
for _, group := range order {
monitor, exists := byName[group]
if !exists {
monitor = Monitor{Name: group, Status: -1}
monitor := Monitor{ID: model.DetectionSecretHash(group)[:16], Name: group, Status: -1, Models: models[group]}
if monitor.Models == nil {
monitor.Models = []string{}
}
if settings[group].Alias != "" {
monitor.Name = settings[group].Alias
}
if admin {
monitor.Alias, monitor.Hidden = settings[group].Alias, settings[group].Hidden
}
if admin {
monitor.Group = group
}
monitor.Fingerprint = fingerprints[group]
if monitor.Fingerprint == nil {
monitor.Fingerprint = &service.AstraSamplingSummary{Enabled: service.AstraSamplingEnabled(), Available: fingerprintErr == nil, History: []service.AstraSamplingHistory{}}
}
monitor.Group = group
monitor.ProbeStatus = monitor.Status
monitor.ChannelDisabled = !active[group]
if !active[group] {
@@ -239,16 +154,30 @@ func currentGroupMonitors(ctx context.Context, results []UptimeGroupResult) ([]U
stats := groupCacheSnapshot.stats[group]
monitor.CacheStats = &stats
applyRequestMetrics(&monitor, groupCacheSnapshot.metrics[group])
if !admin && monitor.Metrics != nil {
monitor.Metrics.Group = monitor.Name
}
monitor.Pelican = detections[group].Pelican
monitor.StatusSource = "requests"
if monitor.ChannelDisabled {
monitor.StatusSource = "disabled"
} else if monitor.Status < 0 {
monitor.Status = detections[group].Status
monitor.StatusSource = "detection"
monitor.StatusCheckedAt = detections[group].At
}
monitor.ProbeHistory = detections[group].ProbeHistory
applyUserSuccess(&monitor, successes[group], detections[group].At, now.Unix())
monitors = append(monitors, monitor)
}
return []UptimeGroupResult{{CategoryName: "Groups", Monitors: monitors}}, nil
}
func GetUptimeKumaStatus(c *gin.Context) {
func GetGroupStatus(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), requestTimeout)
defer cancel()
results, err := currentGroupMonitors(ctx, nil)
results, err := currentGroupMonitors(ctx, false)
if err != nil {
common.SysError("group status statistics: " + err.Error())
c.JSON(http.StatusServiceUnavailable, gin.H{"success": false, "message": "Status data is temporarily unavailable"})
@@ -267,6 +196,14 @@ func requestStatus(rate float64) int {
return 0
}
func applyUserSuccess(monitor *Monitor, success, probe, now int64) {
if !monitor.ChannelDisabled && success > 0 && success >= now-900 && success >= probe {
monitor.Status = 1
monitor.StatusSource = "requests"
monitor.StatusCheckedAt = success
}
}
func applyRequestMetrics(monitor *Monitor, metrics perfmetrics.GroupResult) {
monitor.Status = -1
monitor.Uptime = 0