Files
elmprodvpn/selective-vpn-api/app/transport_singbox_profiles_flow_validate.go

120 lines
3.1 KiB
Go

package app
import (
"encoding/json"
"io"
"net/http"
"time"
)
func handleTransportSingBoxProfileValidate(w http.ResponseWriter, r *http.Request, id string) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var body SingBoxProfileValidateRequest
if r.Body != nil {
defer r.Body.Close()
if err := json.NewDecoder(io.LimitReader(r.Body, 1<<20)).Decode(&body); err != nil && err != io.EOF {
http.Error(w, "bad json", http.StatusBadRequest)
return
}
}
checkBinary := true
if body.CheckBinary != nil {
checkBinary = *body.CheckBinary
}
singBoxProfilesMu.Lock()
defer singBoxProfilesMu.Unlock()
st := loadSingBoxProfilesState()
idx := findSingBoxProfileIndex(st.Items, id)
if idx < 0 {
writeJSON(w, http.StatusNotFound, SingBoxProfileValidateResponse{
OK: false,
Code: singBoxProfileCodeNotFound,
Message: "not found",
})
return
}
cur := st.Items[idx]
if body.BaseRevision > 0 && body.BaseRevision != cur.ProfileRevision {
writeJSON(w, http.StatusConflict, SingBoxProfileValidateResponse{
OK: false,
Code: singBoxProfileCodeRevisionMismatch,
Message: "base_revision mismatch",
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
})
return
}
eval := evaluateSingBoxProfile(cur, checkBinary)
now := time.Now().UTC().Format(time.RFC3339Nano)
cur.LastValidatedAt = now
cur.UpdatedAt = now
if eval.Valid {
cur.LastError = ""
} else {
cur.LastError = joinSingBoxIssueMessages(eval.Errors)
}
st.Items[idx] = cur
if err := saveSingBoxProfilesState(st); err != nil {
writeJSON(w, http.StatusInternalServerError, SingBoxProfileValidateResponse{
OK: false,
Code: singBoxProfileCodeSaveFailed,
Message: "save failed: " + err.Error(),
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
})
return
}
status := "success"
eventKind := "singbox_profile_validated"
respCode := ""
respMsg := "valid"
if !eval.Valid {
status = "failed"
eventKind = "singbox_profile_failed"
respCode = singBoxProfileCodeValidationFailed
respMsg = "validation failed"
}
_ = appendSingBoxHistory(singBoxProfileHistoryRecord{
At: now,
ProfileID: cur.ID,
Action: "validate",
Status: status,
Code: respCode,
Message: respMsg,
ProfileRevision: cur.ProfileRevision,
RenderRevision: cur.RenderRevision,
RenderDigest: eval.Digest,
Diff: eval.Diff,
Errors: eval.Errors,
Warnings: eval.Warnings,
})
events.push(eventKind, map[string]any{
"id": cur.ID,
"valid": eval.Valid,
"errors": len(eval.Errors),
"warning": len(eval.Warnings),
})
writeJSON(w, http.StatusOK, SingBoxProfileValidateResponse{
OK: eval.Valid,
Message: respMsg,
Code: respCode,
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
Valid: eval.Valid,
Errors: eval.Errors,
Warnings: eval.Warnings,
RenderDigest: eval.Digest,
Diff: eval.Diff,
})
}