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

109 lines
2.9 KiB
Go

package app
import (
"encoding/json"
"io"
"net/http"
"strings"
)
func handleTransportSingBoxProfiles(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
enabledOnly := strings.EqualFold(strings.TrimSpace(r.URL.Query().Get("enabled_only")), "true")
modeRaw := strings.TrimSpace(r.URL.Query().Get("mode"))
modeFilter := SingBoxProfileMode("")
if modeRaw != "" {
parsed, ok := normalizeSingBoxProfileMode(SingBoxProfileMode(modeRaw))
if !ok {
writeJSON(w, http.StatusBadRequest, SingBoxProfilesResponse{
OK: false,
Code: singBoxProfileCodeBadMode,
Message: "mode must be typed|raw",
})
return
}
modeFilter = parsed
}
protocolFilter := normalizeSingBoxProtocol(r.URL.Query().Get("protocol"))
singBoxProfilesMu.Lock()
st := loadSingBoxProfilesState()
singBoxProfilesMu.Unlock()
items := make([]SingBoxProfile, 0, len(st.Items))
for _, it := range st.Items {
if enabledOnly && !it.Enabled {
continue
}
if modeFilter != "" && it.Mode != modeFilter {
continue
}
if protocolFilter != "" && it.Protocol != protocolFilter {
continue
}
items = append(items, it)
}
writeJSON(w, http.StatusOK, SingBoxProfilesResponse{
OK: true,
Message: "ok",
Count: len(items),
ActiveProfileID: st.ActiveProfileID,
Items: items,
})
case http.MethodPost:
var body SingBoxProfileCreateRequest
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
}
}
singBoxProfilesMu.Lock()
defer singBoxProfilesMu.Unlock()
st := loadSingBoxProfilesState()
item, code, err := createSingBoxProfileLocked(&st, body)
if err != nil {
status := http.StatusBadRequest
switch code {
case singBoxProfileCodeIDConflict:
status = http.StatusConflict
case singBoxProfileCodeSecretsFailed:
status = http.StatusInternalServerError
}
writeJSON(w, status, SingBoxProfilesResponse{
OK: false,
Code: code,
Message: err.Error(),
})
return
}
if err := saveSingBoxProfilesState(st); err != nil {
if strings.TrimSpace(item.ID) != "" {
_ = writeSingBoxSecrets(item.ID, nil)
}
writeJSON(w, http.StatusInternalServerError, SingBoxProfilesResponse{
OK: false,
Code: singBoxProfileCodeSaveFailed,
Message: "save failed: " + err.Error(),
})
return
}
events.push("singbox_profile_saved", map[string]any{
"id": item.ID,
"revision": item.ProfileRevision,
})
writeJSON(w, http.StatusOK, SingBoxProfilesResponse{
OK: true,
Message: "created",
ActiveProfileID: st.ActiveProfileID,
Item: &item,
})
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}