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

+61
View File
@@ -0,0 +1,61 @@
package model
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// DetectionSchedule survives task-history cleanup and is never projected directly
// to public APIs. Only automatic results advance this channel/kind clock.
type DetectionSchedule struct {
ChannelID int `gorm:"primaryKey;autoIncrement:false"`
Kind string `gorm:"primaryKey;type:varchar(24)"`
LastValid string `gorm:"type:varchar(16)"`
IntervalMinutes int
NextDueAt int64
LastFinishedAt int64
LastResultID string `gorm:"type:varchar(64)"`
}
func NextDetectionInterval(lastValid, outcome string, current int) int {
if outcome != "normal" && outcome != "degraded" {
return 15
}
if lastValid == "" {
return 30
}
if lastValid != outcome {
return 15
}
if current == 15 {
return 30
}
return 60
}
func EnsureDetectionSchedule(ctx context.Context, seed DetectionSchedule) error {
return DB.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).Create(&seed).Error
}
func ApplyDetectionScheduleResult(ctx context.Context, channelID int, kind, resultID string, finishedAt int64, outcome string) error {
return DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var state DetectionSchedule
if err := lockForUpdate(tx).Where("channel_id = ? AND kind = ?", channelID, kind).First(&state).Error; err != nil {
return err
}
// A result is committed before its clock advances. Reconciliation can
// safely replay it after a crash, without advancing the clock twice.
if finishedAt <= state.LastFinishedAt || resultID == state.LastResultID {
return nil
}
state.IntervalMinutes = NextDetectionInterval(state.LastValid, outcome, state.IntervalMinutes)
if outcome == "normal" || outcome == "degraded" {
state.LastValid = outcome
}
state.LastFinishedAt, state.LastResultID = finishedAt, resultID
state.NextDueAt = finishedAt + int64(state.IntervalMinutes)*60
return tx.Save(&state).Error
})
}