260 lines
7.5 KiB
Go
260 lines
7.5 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"html/template"
|
||
"log"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"github.com/darkit/goproxy/pkg/rewriter"
|
||
)
|
||
|
||
// 全局重写器实例
|
||
var rw *rewriter.Rewriter
|
||
|
||
// 规则配置文件路径
|
||
var rulesFile = "rules.json"
|
||
|
||
// AdminHandler Web管理页面处理器
|
||
func AdminHandler(w http.ResponseWriter, r *http.Request) {
|
||
// 定义模板
|
||
tmpl := `
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>URL重写规则管理</title>
|
||
<style>
|
||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||
h1 { color: #333; }
|
||
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
||
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
|
||
th { background-color: #f2f2f2; }
|
||
.enabled { color: green; }
|
||
.disabled { color: red; }
|
||
.form-group { margin-bottom: 15px; }
|
||
label { display: block; margin-bottom: 5px; }
|
||
input[type="text"], textarea { width: 100%; padding: 8px; box-sizing: border-box; }
|
||
input[type="checkbox"] { margin-right: 5px; }
|
||
button { padding: 10px 15px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
|
||
button:hover { background-color: #45a049; }
|
||
.action-btn { padding: 5px 10px; margin-right: 5px; }
|
||
.delete-btn { background-color: #f44336; }
|
||
.edit-btn { background-color: #2196F3; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>URL重写规则管理</h1>
|
||
|
||
<h2>当前规则</h2>
|
||
<table>
|
||
<tr>
|
||
<th>索引</th>
|
||
<th>匹配模式</th>
|
||
<th>替换模式</th>
|
||
<th>类型</th>
|
||
<th>描述</th>
|
||
<th>状态</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
{{range $i, $rule := .Rules}}
|
||
<tr>
|
||
<td>{{$i}}</td>
|
||
<td>{{$rule.Pattern}}</td>
|
||
<td>{{$rule.Replacement}}</td>
|
||
<td>{{if $rule.UseRegex}}正则表达式{{else}}前缀匹配{{end}}</td>
|
||
<td>{{$rule.Description}}</td>
|
||
<td class="{{if $rule.Enabled}}enabled{{else}}disabled{{end}}">
|
||
{{if $rule.Enabled}}启用{{else}}禁用{{end}}
|
||
</td>
|
||
<td>
|
||
{{if $rule.Enabled}}
|
||
<a href="/disable/{{$i}}"><button class="action-btn">禁用</button></a>
|
||
{{else}}
|
||
<a href="/enable/{{$i}}"><button class="action-btn">启用</button></a>
|
||
{{end}}
|
||
<a href="/remove/{{$i}}"><button class="action-btn delete-btn">删除</button></a>
|
||
</td>
|
||
</tr>
|
||
{{end}}
|
||
</table>
|
||
|
||
<h2>添加新规则</h2>
|
||
<form action="/add" method="post">
|
||
<div class="form-group">
|
||
<label for="pattern">匹配模式:</label>
|
||
<input type="text" id="pattern" name="pattern" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="replacement">替换模式:</label>
|
||
<input type="text" id="replacement" name="replacement" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="regex">
|
||
<input type="checkbox" id="regex" name="regex">
|
||
使用正则表达式
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="description">描述:</label>
|
||
<textarea id="description" name="description" rows="3"></textarea>
|
||
</div>
|
||
<button type="submit">添加规则</button>
|
||
</form>
|
||
|
||
<h2>保存配置</h2>
|
||
<form action="/save" method="post">
|
||
<button type="submit">保存规则配置</button>
|
||
</form>
|
||
</body>
|
||
</html>
|
||
`
|
||
|
||
// 解析模板
|
||
t, err := template.New("admin").Parse(tmpl)
|
||
if err != nil {
|
||
http.Error(w, fmt.Sprintf("模板解析错误: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 渲染模板
|
||
data := struct {
|
||
Rules []*rewriter.RewriteRule
|
||
}{
|
||
Rules: rw.GetRules(),
|
||
}
|
||
|
||
if err := t.Execute(w, data); err != nil {
|
||
http.Error(w, fmt.Sprintf("模板渲染错误: %v", err), http.StatusInternalServerError)
|
||
}
|
||
}
|
||
|
||
// AddRuleHandler 添加规则处理器
|
||
func AddRuleHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
return
|
||
}
|
||
|
||
r.ParseForm()
|
||
|
||
pattern := r.Form.Get("pattern")
|
||
replacement := r.Form.Get("replacement")
|
||
useRegex := r.Form.Get("regex") == "on"
|
||
description := r.Form.Get("description")
|
||
|
||
if pattern == "" || replacement == "" {
|
||
http.Error(w, "匹配模式和替换模式不能为空", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
if err := rw.AddRuleWithDescription(pattern, replacement, useRegex, description); err != nil {
|
||
http.Error(w, fmt.Sprintf("添加规则失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
}
|
||
|
||
// EnableRuleHandler 启用规则处理器
|
||
func EnableRuleHandler(w http.ResponseWriter, r *http.Request) {
|
||
// 解析URL中的规则索引
|
||
indexStr := r.URL.Path[len("/enable/"):]
|
||
index, err := strconv.Atoi(indexStr)
|
||
if err != nil {
|
||
http.Error(w, "无效的规则索引", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
if err := rw.EnableRule(index); err != nil {
|
||
http.Error(w, fmt.Sprintf("启用规则失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
}
|
||
|
||
// DisableRuleHandler 禁用规则处理器
|
||
func DisableRuleHandler(w http.ResponseWriter, r *http.Request) {
|
||
// 解析URL中的规则索引
|
||
indexStr := r.URL.Path[len("/disable/"):]
|
||
index, err := strconv.Atoi(indexStr)
|
||
if err != nil {
|
||
http.Error(w, "无效的规则索引", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
if err := rw.DisableRule(index); err != nil {
|
||
http.Error(w, fmt.Sprintf("禁用规则失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
}
|
||
|
||
// RemoveRuleHandler 删除规则处理器
|
||
func RemoveRuleHandler(w http.ResponseWriter, r *http.Request) {
|
||
// 解析URL中的规则索引
|
||
indexStr := r.URL.Path[len("/remove/"):]
|
||
index, err := strconv.Atoi(indexStr)
|
||
if err != nil {
|
||
http.Error(w, "无效的规则索引", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
if err := rw.RemoveRule(index); err != nil {
|
||
http.Error(w, fmt.Sprintf("删除规则失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
}
|
||
|
||
// SaveRulesHandler 保存规则处理器
|
||
func SaveRulesHandler(w http.ResponseWriter, r *http.Request) {
|
||
if r.Method != http.MethodPost {
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
return
|
||
}
|
||
|
||
if err := rw.SaveRulesToFile(rulesFile); err != nil {
|
||
http.Error(w, fmt.Sprintf("保存规则失败: %v", err), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||
}
|
||
|
||
// APIHandler API处理器,返回JSON格式的规则列表
|
||
func APIHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
rules := rw.GetRules()
|
||
json.NewEncoder(w).Encode(rules)
|
||
}
|
||
|
||
func main() {
|
||
// 创建重写器
|
||
rw = rewriter.NewRewriter()
|
||
|
||
// 尝试从文件加载规则
|
||
if err := rw.LoadRulesFromFile(rulesFile); err != nil {
|
||
log.Printf("加载规则失败: %v, 将使用空规则集", err)
|
||
}
|
||
|
||
// 注册处理器
|
||
http.HandleFunc("/", AdminHandler)
|
||
http.HandleFunc("/add", AddRuleHandler)
|
||
http.HandleFunc("/enable/", EnableRuleHandler)
|
||
http.HandleFunc("/disable/", DisableRuleHandler)
|
||
http.HandleFunc("/remove/", RemoveRuleHandler)
|
||
http.HandleFunc("/save", SaveRulesHandler)
|
||
http.HandleFunc("/api/rules", APIHandler)
|
||
|
||
// 启动服务器
|
||
log.Println("管理界面启动在 :8080...")
|
||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||
}
|