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

146 lines
4.2 KiB
Go

package app
import (
"net/http"
"time"
)
func executeTransportSingBoxProfileRenderLocked(
id string,
body SingBoxProfileRenderRequest,
checkBinary bool,
persist bool,
) (int, SingBoxProfileRenderResponse) {
st := loadSingBoxProfilesState()
idx := findSingBoxProfileIndex(st.Items, id)
if idx < 0 {
return http.StatusNotFound, SingBoxProfileRenderResponse{
OK: false,
Code: singBoxProfileCodeNotFound,
Message: "not found",
}
}
cur := st.Items[idx]
if body.BaseRevision > 0 && body.BaseRevision != cur.ProfileRevision {
return http.StatusConflict, SingBoxProfileRenderResponse{
OK: false,
Code: singBoxProfileCodeRevisionMismatch,
Message: "base_revision mismatch",
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
RenderRevision: cur.RenderRevision,
}
}
eval := evaluateSingBoxProfile(cur, checkBinary)
if !eval.Valid {
now := time.Now().UTC().Format(time.RFC3339Nano)
cur.LastError = joinSingBoxIssueMessages(eval.Errors)
cur.UpdatedAt = now
st.Items[idx] = cur
_ = saveSingBoxProfilesState(st)
_ = appendSingBoxHistory(singBoxProfileHistoryRecord{
At: now,
ProfileID: cur.ID,
Action: "render",
Status: "failed",
Code: singBoxProfileCodeRenderFailed,
Message: "render validation failed",
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": "render",
})
return http.StatusOK, SingBoxProfileRenderResponse{
OK: false,
Message: "render validation failed",
Code: singBoxProfileCodeRenderFailed,
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, SingBoxProfileRenderResponse{
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,
}
}
renderRevision := cur.RenderRevision
now := time.Now().UTC().Format(time.RFC3339Nano)
if persist {
renderRevision = cur.RenderRevision + 1
cur.RenderRevision = renderRevision
cur.LastError = ""
cur.UpdatedAt = now
st.Items[idx] = cur
if err := saveSingBoxProfilesState(st); err != nil {
return http.StatusInternalServerError, SingBoxProfileRenderResponse{
OK: false,
Code: singBoxProfileCodeSaveFailed,
Message: "save failed: " + err.Error(),
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
RenderRevision: cur.RenderRevision,
Valid: true,
Warnings: eval.Warnings,
Diff: eval.Diff,
}
}
} else {
renderRevision++
}
_ = appendSingBoxHistory(singBoxProfileHistoryRecord{
At: now,
ProfileID: cur.ID,
Action: "render",
Status: "success",
Message: "rendered",
ProfileRevision: cur.ProfileRevision,
RenderRevision: renderRevision,
RenderDigest: eval.Digest,
RenderPath: renderPath,
Warnings: eval.Warnings,
Diff: eval.Diff,
})
events.push("singbox_profile_rendered", map[string]any{
"id": cur.ID,
"render_revision": renderRevision,
})
return http.StatusOK, SingBoxProfileRenderResponse{
OK: true,
Message: "rendered",
ProfileID: cur.ID,
ProfileRevision: cur.ProfileRevision,
RenderRevision: renderRevision,
RenderPath: renderPath,
RenderDigest: eval.Digest,
Changed: eval.Changed,
Valid: true,
Warnings: eval.Warnings,
Diff: eval.Diff,
Config: eval.Config,
}
}