package app import ( "net/http" "time" ) func executeTransportSingBoxProfileApplyLocked( id string, body SingBoxProfileApplyRequest, checkBinary bool, restart bool, ) (int, SingBoxProfileApplyResponse) { st := loadSingBoxProfilesState() idx := findSingBoxProfileIndex(st.Items, id) if idx < 0 { return http.StatusNotFound, SingBoxProfileApplyResponse{ OK: false, Code: singBoxProfileCodeNotFound, Message: "not found", } } cur := st.Items[idx] if body.BaseRevision > 0 && body.BaseRevision != cur.ProfileRevision { return http.StatusConflict, SingBoxProfileApplyResponse{ OK: false, Code: singBoxProfileCodeRevisionMismatch, Message: "base_revision mismatch", ProfileID: cur.ID, ProfileRevision: cur.ProfileRevision, RenderRevision: cur.RenderRevision, } } eval := evaluateSingBoxProfile(cur, checkBinary) now := time.Now().UTC().Format(time.RFC3339Nano) if !eval.Valid { cur.LastValidatedAt = now cur.LastError = joinSingBoxIssueMessages(eval.Errors) cur.UpdatedAt = now st.Items[idx] = cur _ = saveSingBoxProfilesState(st) _ = appendSingBoxHistory(singBoxProfileHistoryRecord{ At: now, ProfileID: cur.ID, Action: "apply", Status: "failed", Code: singBoxProfileCodeValidationFailed, Message: "apply blocked by validation errors", ProfileRevision: cur.ProfileRevision, RenderRevision: cur.RenderRevision, Errors: eval.Errors, Warnings: eval.Warnings, Diff: eval.Diff, }) events.push("singbox_profile_failed", map[string]any{ "id": cur.ID, "step": "apply-validate", }) return http.StatusOK, SingBoxProfileApplyResponse{ OK: false, Message: "apply blocked by validation errors", Code: singBoxProfileCodeValidationFailed, ProfileID: cur.ID, ProfileRevision: cur.ProfileRevision, RenderRevision: cur.RenderRevision, Valid: false, Errors: eval.Errors, Warnings: eval.Warnings, Diff: eval.Diff, } } renderPath, err := writeSingBoxRenderedConfig(cur.ID, eval.Config) if err != nil { return http.StatusInternalServerError, SingBoxProfileApplyResponse{ OK: false, Code: singBoxProfileCodeRenderFailed, Message: "write rendered config failed: " + err.Error(), ProfileID: cur.ID, ProfileRevision: cur.ProfileRevision, RenderRevision: cur.RenderRevision, Valid: true, Warnings: eval.Warnings, Diff: eval.Diff, } } return executeTransportSingBoxProfileApplyRenderedLocked(st, idx, cur, body, eval, now, renderPath, restart) }