c26b06f1cc
Go 控制面(SQLite + REST API)嵌入 Mihomo 数据面,Vue 3 多语言 Web 管理台。 含订阅/节点/规则/出站/监控/日志、OpenAPI、API Key、Gitea Actions CI 与开源文档; CI 使用 ubuntu-latest runner,Go/Docker 步骤走国内镜像与 shell,避免 GitHub 下载超时。 Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/prism/proxy/internal/auth"
|
|
)
|
|
|
|
type createAPIKeyReq struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func (s *Server) listAPIKeys(c *gin.Context) {
|
|
items, err := s.app.Store.ListAPIKeys(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"items": items})
|
|
}
|
|
|
|
func (s *Server) createAPIKey(c *gin.Context) {
|
|
var req createAPIKeyReq
|
|
_ = c.ShouldBindJSON(&req)
|
|
name := strings.TrimSpace(req.Name)
|
|
if name == "" {
|
|
name = "default"
|
|
}
|
|
|
|
raw, err := auth.GenerateAPIKey()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
item, err := s.app.Store.CreateAPIKey(
|
|
c.Request.Context(),
|
|
name,
|
|
auth.APIKeyDisplayPrefix(raw),
|
|
auth.HashAPIKey(raw),
|
|
)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
_ = s.app.Store.AddAuditLog(c.Request.Context(), "create", "api_key", name)
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"id": item.ID,
|
|
"name": item.Name,
|
|
"key": raw,
|
|
"key_prefix": item.KeyPrefix,
|
|
"created_at": item.CreatedAt,
|
|
})
|
|
}
|
|
|
|
func (s *Server) deleteAPIKey(c *gin.Context) {
|
|
id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err := s.app.Store.DeleteAPIKey(c.Request.Context(), id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
_ = s.app.Store.AddAuditLog(c.Request.Context(), "delete", "api_key", strconv.FormatInt(id, 10))
|
|
c.JSON(http.StatusOK, gin.H{"ok": true})
|
|
}
|
|
|
|
func (s *Server) authorizeRequest(c *gin.Context, settings map[string]string) bool {
|
|
if settings["auth_enabled"] != "true" {
|
|
return true
|
|
}
|
|
|
|
ctx := c.Request.Context()
|
|
if key := strings.TrimSpace(c.GetHeader("X-API-Key")); key != "" {
|
|
if ok, _ := s.app.Store.VerifyAPIKey(ctx, auth.HashAPIKey(key)); ok {
|
|
return true
|
|
}
|
|
}
|
|
|
|
header := c.GetHeader("Authorization")
|
|
if !strings.HasPrefix(header, "Bearer ") {
|
|
return false
|
|
}
|
|
token := strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
|
|
if token == "" {
|
|
return false
|
|
}
|
|
if auth.IsAPIKeyFormat(token) {
|
|
ok, _ := s.app.Store.VerifyAPIKey(ctx, auth.HashAPIKey(token))
|
|
return ok
|
|
}
|
|
return verifyToken(settings["admin_password"], token)
|
|
}
|