Files
elmprodvpn/selective-vpn-api/app/transport_singbox_profiles_history.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)
}