Files
2026-08-12 18:59:29 +08:00

256 lines
6.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"net/http"
"github.com/go-orz/orz"
"github.com/labstack/echo/v5"
"github.com/pika-monitor/pika/internal/service"
"go.uber.org/zap"
)
type ApiKeyHandler struct {
logger *zap.Logger
apiKeyService *service.ApiKeyService
}
func NewApiKeyHandler(logger *zap.Logger, apiKeyService *service.ApiKeyService) *ApiKeyHandler {
return &ApiKeyHandler{
logger: logger,
apiKeyService: apiKeyService,
}
}
// GenerateApiKeyRequest 生成API密钥请求
type GenerateApiKeyRequest struct {
Name string `json:"name" validate:"required"`
Type string `json:"type"`
}
// UpdateApiKeyNameRequest 更新API密钥名称请求
type UpdateApiKeyNameRequest struct {
Name string `json:"name" validate:"required"`
}
// 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")
builder := orz.NewPageBuilder(r.apiKeyService.ApiKeyRepo).
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密钥(仅限通信密钥 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
}
if err := c.Validate(&req); err != nil {
return err
}
// 从上下文获取用户ID
userID := c.Get("userID").(string)
// 固定类型为 agent(通信密钥)
apiKeyType := "agent"
ctx := c.Request().Context()
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
}
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()
apiKey, err := r.apiKeyService.GetApiKey(ctx, id)
if err != nil {
r.logger.Error("failed to get api key", zap.Error(err))
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
if err := c.Bind(&req); err != nil {
return err
}
if err := c.Validate(&req); err != nil {
return err
}
ctx := c.Request().Context()
if err := r.apiKeyService.UpdateApiKeyName(ctx, id, req.Name); err != nil {
r.logger.Error("failed to update api key name", zap.Error(err))
return err
}
return orz.Ok(c, orz.Map{})
}
// 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()
if err := r.apiKeyService.DeleteApiKey(ctx, id); err != nil {
r.logger.Error("failed to delete api key", zap.Error(err))
return err
}
return orz.Ok(c, orz.Map{})
}
// 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()
if err := r.apiKeyService.EnableApiKey(ctx, id); err != nil {
r.logger.Error("failed to enable api key", zap.Error(err))
return err
}
return orz.Ok(c, orz.Map{})
}
// 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()
if err := r.apiKeyService.DisableApiKey(ctx, id); err != nil {
r.logger.Error("failed to disable api key", zap.Error(err))
return err
}
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:]
}