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, ) }