platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
transportcfgpkg "selective-vpn-api/app/transportcfg"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func executeTransportSingBoxProfileRollbackLocked(
|
||||
id string,
|
||||
body SingBoxProfileRollbackRequest,
|
||||
restart bool,
|
||||
) (int, SingBoxProfileRollbackResponse) {
|
||||
st := loadSingBoxProfilesState()
|
||||
idx := findSingBoxProfileIndex(st.Items, id)
|
||||
if idx < 0 {
|
||||
return http.StatusNotFound, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeNotFound,
|
||||
Message: "not found",
|
||||
}
|
||||
}
|
||||
cur := st.Items[idx]
|
||||
if body.BaseRevision > 0 && body.BaseRevision != cur.ProfileRevision {
|
||||
return http.StatusConflict, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeRevisionMismatch,
|
||||
Message: "base_revision mismatch",
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
}
|
||||
}
|
||||
|
||||
records, err := loadSingBoxHistoryRecords(cur.ID, 0)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeRollbackFailed,
|
||||
Message: "read history failed: " + err.Error(),
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
}
|
||||
}
|
||||
cand, ok := selectSingBoxRollbackCandidate(records, strings.TrimSpace(body.HistoryID))
|
||||
if !ok {
|
||||
return http.StatusOK, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeHistoryMissing,
|
||||
Message: "rollback snapshot not found",
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
}
|
||||
}
|
||||
|
||||
clientID := strings.TrimSpace(body.ClientID)
|
||||
if clientID == "" {
|
||||
clientID = strings.TrimSpace(cand.ClientID)
|
||||
}
|
||||
configPath := strings.TrimSpace(body.ConfigPath)
|
||||
if configPath == "" {
|
||||
configPath = strings.TrimSpace(cand.ConfigPath)
|
||||
}
|
||||
|
||||
client, code, msg := resolveSingBoxApplyClient(clientID, "")
|
||||
if !body.SkipRuntime {
|
||||
if client == nil {
|
||||
return http.StatusOK, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: code,
|
||||
Message: msg,
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
HistoryID: cand.ID,
|
||||
}
|
||||
}
|
||||
if configPath == "" {
|
||||
configPath = transportSingBoxConfigPath(*client)
|
||||
}
|
||||
}
|
||||
if configPath == "" {
|
||||
configPath = defaultSingBoxAppliedConfigPath(cur.ID)
|
||||
}
|
||||
|
||||
prevData, prevExists, err := decodeHistoryPrevConfig(cand)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeRollbackFailed,
|
||||
Message: "decode rollback snapshot failed: " + err.Error(),
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
HistoryID: cand.ID,
|
||||
ClientID: clientID,
|
||||
ConfigPath: configPath,
|
||||
}
|
||||
}
|
||||
|
||||
currentData, currentExists, _ := transportcfgpkg.ReadFileOptional(configPath)
|
||||
if err := transportcfgpkg.RestoreFileOptional(configPath, prevData, prevExists); err != nil {
|
||||
return http.StatusInternalServerError, SingBoxProfileRollbackResponse{
|
||||
OK: false,
|
||||
Code: singBoxProfileCodeRollbackFailed,
|
||||
Message: "restore config failed: " + err.Error(),
|
||||
ProfileID: cur.ID,
|
||||
ProfileRevision: cur.ProfileRevision,
|
||||
HistoryID: cand.ID,
|
||||
ClientID: clientID,
|
||||
ConfigPath: configPath,
|
||||
}
|
||||
}
|
||||
return executeTransportSingBoxProfileRollbackRestoredLocked(
|
||||
st,
|
||||
idx,
|
||||
cur,
|
||||
body,
|
||||
cand,
|
||||
clientID,
|
||||
configPath,
|
||||
client,
|
||||
currentData,
|
||||
currentExists,
|
||||
restart,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user