143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/prism/proxy/internal/ruleio"
|
|
)
|
|
|
|
func (s *Server) exportRules(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
items, err := s.app.Store.ListRules(ctx, "manual")
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
format := strings.ToLower(c.DefaultQuery("format", "json"))
|
|
filename := "prism-rules"
|
|
switch format {
|
|
case "text", "txt", "clash":
|
|
c.Header("Content-Type", "text/plain; charset=utf-8")
|
|
c.Header("Content-Disposition", `attachment; filename="`+filename+`.txt"`)
|
|
c.String(http.StatusOK, ruleio.ExportText(items))
|
|
default:
|
|
data, err := ruleio.ExportJSON(items)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.Header("Content-Type", "application/json; charset=utf-8")
|
|
c.Header("Content-Disposition", `attachment; filename="`+filename+`.json"`)
|
|
c.Data(http.StatusOK, "application/json; charset=utf-8", data)
|
|
}
|
|
}
|
|
|
|
type importRulesReq struct {
|
|
Mode string `json:"mode"`
|
|
Text string `json:"text"`
|
|
Rules []ruleio.ExportRule `json:"rules"`
|
|
}
|
|
|
|
func (s *Server) importRules(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
mode := strings.ToLower(strings.TrimSpace(c.DefaultQuery("mode", "")))
|
|
|
|
var parsed []ruleio.ExportRule
|
|
var parseErrors []string
|
|
|
|
contentType := c.GetHeader("Content-Type")
|
|
if strings.Contains(contentType, "application/json") {
|
|
var req importRulesReq
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if mode == "" {
|
|
mode = strings.ToLower(strings.TrimSpace(req.Mode))
|
|
}
|
|
if len(req.Rules) > 0 {
|
|
parsed = req.Rules
|
|
} else if strings.TrimSpace(req.Text) != "" {
|
|
parsed, parseErrors = parseImportText(req.Text)
|
|
} else {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "rules or text required"})
|
|
return
|
|
}
|
|
} else {
|
|
body, err := io.ReadAll(c.Request.Body)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if mode == "" {
|
|
mode = "append"
|
|
}
|
|
parsed, parseErrors = parseImportText(string(body))
|
|
}
|
|
|
|
if mode == "" {
|
|
mode = "append"
|
|
}
|
|
if mode != "append" && mode != "replace" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "mode must be append or replace"})
|
|
return
|
|
}
|
|
if len(parsed) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no valid rules", "errors": parseErrors})
|
|
return
|
|
}
|
|
|
|
rules, valErrors := ruleio.ToStoreRules(parsed)
|
|
allErrors := append(parseErrors, valErrors...)
|
|
if len(rules) == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no valid rules", "errors": allErrors})
|
|
return
|
|
}
|
|
|
|
if mode == "append" {
|
|
start, _ := s.app.Store.NextManualRulePriority(ctx)
|
|
ruleio.AssignPriorities(rules, start)
|
|
if err := s.app.Store.AppendManualRules(ctx, rules); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
} else {
|
|
ruleio.AssignPriorities(rules, 100)
|
|
if err := s.app.Store.ReplaceManualRules(ctx, rules); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
}
|
|
|
|
_ = s.app.Store.AddAuditLog(ctx, "import", "rules", mode+": "+strconv.Itoa(len(rules)))
|
|
_ = s.app.ReloadEngine(ctx)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"ok": true,
|
|
"imported": len(rules),
|
|
"skipped": len(allErrors),
|
|
"errors": allErrors,
|
|
"mode": mode,
|
|
})
|
|
}
|
|
|
|
func parseImportText(text string) ([]ruleio.ExportRule, []string) {
|
|
text = strings.TrimSpace(text)
|
|
if text == "" {
|
|
return nil, nil
|
|
}
|
|
if strings.HasPrefix(text, "{") || strings.HasPrefix(text, "[") {
|
|
rules, err := ruleio.ParseJSON([]byte(text))
|
|
if err == nil && len(rules) > 0 {
|
|
return rules, nil
|
|
}
|
|
}
|
|
return ruleio.ParseText(text)
|
|
}
|