85 lines
2.4 KiB
Go
85 lines
2.4 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
transportcfgpkg "selective-vpn-api/app/transportcfg"
|
|
)
|
|
|
|
func appendSingBoxHistory(rec singBoxProfileHistoryRecord) error {
|
|
profileID := sanitizeID(rec.ProfileID)
|
|
if profileID == "" {
|
|
return fmt.Errorf("empty profile id")
|
|
}
|
|
rec.ProfileID = profileID
|
|
if strings.TrimSpace(rec.At) == "" {
|
|
rec.At = time.Now().UTC().Format(time.RFC3339Nano)
|
|
}
|
|
if strings.TrimSpace(rec.ID) == "" {
|
|
rec.ID = "h-" + newTransportToken(8)
|
|
}
|
|
if rec.Action == "" {
|
|
rec.Action = "unknown"
|
|
}
|
|
if rec.Status == "" {
|
|
rec.Status = "success"
|
|
}
|
|
|
|
data, err := json.MarshalIndent(rec, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
stamp := transportcfgpkg.SanitizeHistoryStamp(rec.At, time.Now().UTC())
|
|
fileName := fmt.Sprintf("%s-%s-%s-%s.json", stamp, rec.ProfileID, rec.Action, rec.ID)
|
|
return transportcfgpkg.WriteFileAtomic(filepath.Join(singBoxHistoryRootDir, fileName), append(data, '\n'), 0o644)
|
|
}
|
|
|
|
func loadSingBoxHistoryRecords(profileID string, limit int) ([]singBoxProfileHistoryRecord, error) {
|
|
rawRecords, err := transportcfgpkg.ReadJSONFiles(singBoxHistoryRootDir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id := sanitizeID(profileID)
|
|
out := make([]singBoxProfileHistoryRecord, 0, len(rawRecords))
|
|
for _, data := range rawRecords {
|
|
var rec singBoxProfileHistoryRecord
|
|
if err := json.Unmarshal(data, &rec); err != nil {
|
|
continue
|
|
}
|
|
if sanitizeID(rec.ProfileID) != id {
|
|
continue
|
|
}
|
|
out = append(out, rec)
|
|
}
|
|
transportcfgpkg.SortRecordsDescByAt(out, func(rec singBoxProfileHistoryRecord) string { return rec.At })
|
|
if limit > 0 && len(out) > limit {
|
|
out = out[:limit]
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func selectSingBoxRollbackCandidate(records []singBoxProfileHistoryRecord, historyID string) (singBoxProfileHistoryRecord, bool) {
|
|
return transportcfgpkg.SelectRecordCandidate(
|
|
records,
|
|
historyID,
|
|
func(rec singBoxProfileHistoryRecord) string { return rec.ID },
|
|
func(rec singBoxProfileHistoryRecord) bool { return rec.Action == "apply" && rec.Status == "success" },
|
|
)
|
|
}
|
|
|
|
func decodeHistoryPrevConfig(rec singBoxProfileHistoryRecord) ([]byte, bool, error) {
|
|
return transportcfgpkg.DecodeBase64Optional(rec.PrevConfigB64, rec.PrevConfigExist)
|
|
}
|
|
|
|
func joinSingBoxIssueMessages(issues []SingBoxProfileIssue) string {
|
|
parts := make([]string, 0, len(issues))
|
|
for _, it := range issues {
|
|
parts = append(parts, it.Message)
|
|
}
|
|
return transportcfgpkg.JoinMessages(parts)
|
|
}
|