feat: 支持 API Key 类型与管理接口认证

This commit is contained in:
yumusb committed 2026-04-24 18:45:01 +08:00
1 parent e952a5a67f
commit 52a30aae74
7 files changed
+233 -11

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@ func (h *AgentHandler) DownloadAgent(c echo.Context) error {
// 校验 API Key
apiKey := c.QueryParam("key")
if _, err := h.apiKeyService.ValidateApiKey(c.Request().Context(), apiKey); err != nil {
if _, err := h.apiKeyService.ValidateApiKey(c.Request().Context(), apiKey, "agent"); err != nil {
// API Key 校验失败,尝试 IP 白名单兜底(兼容旧版 Agent 自动更新)
clientIP := c.RealIP()
isOnline, checkErr := h.agentService.IsAgentByIP(c.Request().Context(), clientIP)
+111 -4
View File
@@ -1,6 +1,8 @@
package handler
import (
"net/http"
"github.com/dushixiang/pika/internal/service"
"github.com/go-orz/orz"
"github.com/labstack/echo/v4"
@@ -22,6 +24,7 @@ func NewApiKeyHandler(logger *zap.Logger, apiKeyService *service.ApiKeyService)
// GenerateApiKeyRequest 生成API密钥请求
type GenerateApiKeyRequest struct {
Name string `json:"name" validate:"required"`
Type string `json:"type"`
}
// UpdateApiKeyNameRequest 更新API密钥名称请求
@@ -32,6 +35,7 @@ type UpdateApiKeyNameRequest struct {
// Paging API密钥分页查询
func (r ApiKeyHandler) Paging(c echo.Context) error {
name := c.QueryParam("name")
apiKeyType := c.QueryParam("type")
pr := orz.GetPageRequest(c, "created_at", "name")
@@ -39,18 +43,31 @@ func (r ApiKeyHandler) Paging(c echo.Context) error {
PageRequest(pr).
Contains("name", name)
if apiKeyType != "" {
builder = builder.Equal("type", apiKeyType)
}
ctx := c.Request().Context()
page, err := builder.Execute(ctx)
if err != nil {
return err
}
// 返回完整密钥,由前端控制显示/隐藏
// 遮蔽密钥值,完整密钥仅在创建时返回一次
for i := range page.Items {
page.Items[i].Key = maskApiKey(page.Items[i].Key)
}
return orz.Ok(c, page)
}
// Create 生成API密钥
// Create 生成API密钥(仅限通信密钥 type=agent)
func (r ApiKeyHandler) Create(c echo.Context) error {
// API Key 不能创建其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能创建其他 API Key")
}
var req GenerateApiKeyRequest
if err := c.Bind(&req); err != nil {
return err
@@ -62,8 +79,11 @@ func (r ApiKeyHandler) Create(c echo.Context) error {
// 从上下文获取用户ID
userID := c.Get("userID").(string)
// 固定类型为 agent(通信密钥)
apiKeyType := "agent"
ctx := c.Request().Context()
apiKey, err := r.apiKeyService.GenerateApiKey(ctx, req.Name, userID)
apiKey, err := r.apiKeyService.GenerateApiKey(ctx, req.Name, userID, apiKeyType)
if err != nil {
r.logger.Error("failed to generate api key", zap.Error(err))
return err
@@ -72,8 +92,40 @@ func (r ApiKeyHandler) Create(c echo.Context) error {
return orz.Ok(c, apiKey)
}
// CreateAdmin 生成管理 API Key(type=admin,完整 key 仅创建时返回一次)
func (r ApiKeyHandler) CreateAdmin(c echo.Context) error {
// API Key 不能创建其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能创建其他 API Key")
}
var req GenerateApiKeyRequest
if err := c.Bind(&req); err != nil {
return err
}
if err := c.Validate(&req); err != nil {
return err
}
userID := c.Get("userID").(string)
ctx := c.Request().Context()
apiKey, err := r.apiKeyService.GenerateApiKey(ctx, req.Name, userID, "admin")
if err != nil {
r.logger.Error("failed to generate admin api key", zap.Error(err))
return err
}
return orz.Ok(c, apiKey)
}
// Get 获取API密钥详情
func (r ApiKeyHandler) Get(c echo.Context) error {
// API Key 不能查看其他 API Key 的完整值
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能查看其他 API Key")
}
id := c.Param("id")
ctx := c.Request().Context()
@@ -83,12 +135,44 @@ func (r ApiKeyHandler) Get(c echo.Context) error {
return err
}
// 返回完整密钥,由前端控制显示/隐藏
// 遮蔽密钥值
apiKey.Key = maskApiKey(apiKey.Key)
return orz.Ok(c, apiKey)
}
// GetRaw 获取API密钥完整值(仅在生成安装命令等场景使用)
func (r ApiKeyHandler) GetRaw(c echo.Context) error {
// 仅 JWT 认证可用
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能查看其他 API Key")
}
id := c.Param("id")
ctx := c.Request().Context()
apiKey, err := r.apiKeyService.GetApiKey(ctx, id)
if err != nil {
r.logger.Error("failed to get api key", zap.Error(err))
return err
}
// admin 类型密钥永远不可查看
if apiKey.Type == "admin" {
return echo.NewHTTPError(http.StatusForbidden, "管理 API Key 不允许查看完整值")
}
return orz.Ok(c, orz.Map{
"key": apiKey.Key,
})
}
// Update 更新API密钥名称
func (r ApiKeyHandler) Update(c echo.Context) error {
// API Key 不能修改其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能修改其他 API Key")
}
id := c.Param("id")
var req UpdateApiKeyNameRequest
@@ -110,6 +194,11 @@ func (r ApiKeyHandler) Update(c echo.Context) error {
// Delete 删除API密钥
func (r ApiKeyHandler) Delete(c echo.Context) error {
// API Key 不能删除其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能删除其他 API Key")
}
id := c.Param("id")
ctx := c.Request().Context()
@@ -123,6 +212,11 @@ func (r ApiKeyHandler) Delete(c echo.Context) error {
// Enable 启用API密钥
func (r ApiKeyHandler) Enable(c echo.Context) error {
// API Key 不能启用/禁用其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能启用/禁用其他 API Key")
}
id := c.Param("id")
ctx := c.Request().Context()
@@ -136,6 +230,11 @@ func (r ApiKeyHandler) Enable(c echo.Context) error {
// Disable 禁用API密钥
func (r ApiKeyHandler) Disable(c echo.Context) error {
// API Key 不能启用/禁用其他 API Key
if c.Get("authType") == "api_key" {
return echo.NewHTTPError(http.StatusForbidden, "API Key 不能启用/禁用其他 API Key")
}
id := c.Param("id")
ctx := c.Request().Context()
@@ -146,3 +245,11 @@ func (r ApiKeyHandler) Disable(c echo.Context) error {
return orz.Ok(c, orz.Map{})
}
// maskApiKey 遮蔽API密钥,只显示前4位和后4位
func maskApiKey(key string) string {
if len(key) <= 12 {
return "****"
}
return key[:4] + "..." + key[len(key)-4:]
}