updete
Some checks failed
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m4s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 50s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Has been cancelled
Some checks failed
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m4s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 50s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Has been cancelled
This commit is contained in:
169
internal/server/router/handler/store.go
Normal file
169
internal/server/router/handler/store.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gotunnel/internal/server/db"
|
||||
// removed router import
|
||||
"github.com/gotunnel/internal/server/router/dto"
|
||||
)
|
||||
|
||||
// StoreHandler 插件商店处理器
|
||||
type StoreHandler struct {
|
||||
app AppInterface
|
||||
}
|
||||
|
||||
// NewStoreHandler 创建插件商店处理器
|
||||
func NewStoreHandler(app AppInterface) *StoreHandler {
|
||||
return &StoreHandler{app: app}
|
||||
}
|
||||
|
||||
// ListPlugins 获取商店插件列表
|
||||
// @Summary 获取商店插件
|
||||
// @Description 从远程插件商店获取可用插件列表
|
||||
// @Tags 插件商店
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Success 200 {object} Response{data=object{plugins=[]dto.StorePluginInfo}}
|
||||
// @Failure 502 {object} Response
|
||||
// @Router /api/store/plugins [get]
|
||||
func (h *StoreHandler) ListPlugins(c *gin.Context) {
|
||||
cfg := h.app.GetConfig()
|
||||
storeURL := cfg.PluginStore.GetPluginStoreURL()
|
||||
|
||||
// 从远程 URL 获取插件列表
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := client.Get(storeURL)
|
||||
if err != nil {
|
||||
BadGateway(c, "Failed to fetch store: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
BadGateway(c, "Store returned error")
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
InternalError(c, "Failed to read response")
|
||||
return
|
||||
}
|
||||
|
||||
// 直接返回原始 JSON(已经是数组格式)
|
||||
c.Header("Content-Type", "application/json")
|
||||
c.Writer.Write([]byte(`{"code":0,"data":{"plugins":`))
|
||||
c.Writer.Write(body)
|
||||
c.Writer.Write([]byte(`}}`))
|
||||
}
|
||||
|
||||
// Install 从商店安装插件到客户端
|
||||
// @Summary 安装商店插件
|
||||
// @Description 从插件商店下载并安装插件到指定客户端
|
||||
// @Tags 插件商店
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param request body dto.StoreInstallRequest true "安装请求"
|
||||
// @Success 200 {object} Response
|
||||
// @Failure 400 {object} Response
|
||||
// @Failure 502 {object} Response
|
||||
// @Router /api/store/install [post]
|
||||
func (h *StoreHandler) Install(c *gin.Context) {
|
||||
var req dto.StoreInstallRequest
|
||||
if !BindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查客户端是否在线
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(req.ClientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 下载插件
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Get(req.DownloadURL)
|
||||
if err != nil {
|
||||
BadGateway(c, "Failed to download plugin: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
BadGateway(c, "Plugin download failed with status: "+resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
source, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
InternalError(c, "Failed to read plugin: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 下载签名文件
|
||||
sigResp, err := client.Get(req.SignatureURL)
|
||||
if err != nil {
|
||||
BadGateway(c, "Failed to download signature: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer sigResp.Body.Close()
|
||||
|
||||
if sigResp.StatusCode != http.StatusOK {
|
||||
BadGateway(c, "Signature download failed with status: "+sigResp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
signature, err := io.ReadAll(sigResp.Body)
|
||||
if err != nil {
|
||||
InternalError(c, "Failed to read signature: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 安装到客户端
|
||||
installReq := JSPluginInstallRequest{
|
||||
PluginName: req.PluginName,
|
||||
Source: string(source),
|
||||
Signature: string(signature),
|
||||
RuleName: req.PluginName,
|
||||
AutoStart: true,
|
||||
}
|
||||
|
||||
if err := h.app.GetServer().InstallJSPluginToClient(req.ClientID, installReq); err != nil {
|
||||
InternalError(c, "Failed to install plugin: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 将插件信息保存到数据库
|
||||
dbClient, err := h.app.GetClientStore().GetClient(req.ClientID)
|
||||
if err == nil {
|
||||
// 检查插件是否已存在
|
||||
exists := false
|
||||
for i, p := range dbClient.Plugins {
|
||||
if p.Name == req.PluginName {
|
||||
dbClient.Plugins[i].Enabled = true
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
dbClient.Plugins = append(dbClient.Plugins, db.ClientPlugin{
|
||||
Name: req.PluginName,
|
||||
Version: "1.0.0",
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
h.app.GetClientStore().UpdateClient(dbClient)
|
||||
}
|
||||
|
||||
Success(c, gin.H{
|
||||
"status": "ok",
|
||||
"plugin": req.PluginName,
|
||||
"client": req.ClientID,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user