From ae6cb9d422daa9bb8ea2dcdd423557ba7484c8a1 Mon Sep 17 00:00:00 2001 From: Flik Date: Sat, 3 Jan 2026 19:29:23 +0800 Subject: [PATCH] 1 --- .gitea/workflows/Create Release.yml | 20 +++- .gitignore | 5 +- internal/server/router/dto/plugin.go | 7 ++ internal/server/router/handler/js_plugin.go | 14 ++- internal/server/router/handler/store.go | 1 + web/components.d.ts | 1 + web/src/api/index.ts | 8 +- web/src/views/PluginsView.vue | 100 +++++++++++++++++--- 8 files changed, 133 insertions(+), 23 deletions(-) diff --git a/.gitea/workflows/Create Release.yml b/.gitea/workflows/Create Release.yml index 61dc40f..a6fc6c9 100644 --- a/.gitea/workflows/Create Release.yml +++ b/.gitea/workflows/Create Release.yml @@ -56,7 +56,25 @@ jobs: with: go-version: '1.24' cache: true - + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: web/package-lock.json + + - name: Build frontend + run: | + cd web + npm ci + npm run build + mkdir -p ../internal/server/app/dist + cp -r dist/* ../internal/server/app/dist/ + cd .. + echo "Frontend build completed" + ls -la internal/server/app/dist/ + - name: Build all platforms run: | mkdir -p dist diff --git a/.gitignore b/.gitignore index 2ca0907..3cbd208 100644 --- a/.gitignore +++ b/.gitignore @@ -26,10 +26,11 @@ Thumbs.db # 前端 node_modules web/node_modules/ -# 构建产物 (源码在 web/dist,嵌入用的在 pkg/webserver/dist) +# 构建产物 (源码在 web/dist,嵌入用的在 internal/server/app/dist) web/dist/ pkg/webserver/dist/ -**/dist/** +internal/server/app/dist/* +!internal/server/app/dist/.gitkeep build/** # 日志 diff --git a/internal/server/router/dto/plugin.go b/internal/server/router/dto/plugin.go index 9864415..43e3886 100644 --- a/internal/server/router/dto/plugin.go +++ b/internal/server/router/dto/plugin.go @@ -102,4 +102,11 @@ type StoreInstallRequest struct { DownloadURL string `json:"download_url" binding:"required,url"` SignatureURL string `json:"signature_url" binding:"required,url"` ClientID string `json:"client_id" binding:"required"` + RemotePort int `json:"remote_port"` +} + +// JSPluginPushRequest 推送 JS 插件到客户端请求 +// @Description 推送 JS 插件到指定客户端 +type JSPluginPushRequest struct { + RemotePort int `json:"remote_port"` } diff --git a/internal/server/router/handler/js_plugin.go b/internal/server/router/handler/js_plugin.go index 59ed43d..810d2f6 100644 --- a/internal/server/router/handler/js_plugin.go +++ b/internal/server/router/handler/js_plugin.go @@ -158,10 +158,12 @@ func (h *JSPluginHandler) Delete(c *gin.Context) { // @Summary 推送插件到客户端 // @Description 将 JS 插件推送到指定客户端 // @Tags JS插件 +// @Accept json // @Produce json // @Security Bearer // @Param name path string true "插件名称" // @Param clientID path string true "客户端ID" +// @Param request body dto.JSPluginPushRequest false "推送配置" // @Success 200 {object} Response // @Failure 400 {object} Response // @Failure 404 {object} Response @@ -170,6 +172,10 @@ func (h *JSPluginHandler) PushToClient(c *gin.Context) { pluginName := c.Param("name") clientID := c.Param("clientID") + // 解析请求体(可选) + var pushReq dto.JSPluginPushRequest + c.ShouldBindJSON(&pushReq) // 忽略错误,允许空请求体 + // 检查客户端是否在线 online, _, _ := h.app.GetServer().GetClientStatus(clientID) if !online { @@ -195,6 +201,7 @@ func (h *JSPluginHandler) PushToClient(c *gin.Context) { Source: plugin.Source, Signature: plugin.Signature, RuleName: plugin.Name, + RemotePort: pushReq.RemotePort, Config: plugin.Config, AutoStart: plugin.AutoStart, } @@ -205,8 +212,9 @@ func (h *JSPluginHandler) PushToClient(c *gin.Context) { } Success(c, gin.H{ - "status": "ok", - "plugin": pluginName, - "client": clientID, + "status": "ok", + "plugin": pluginName, + "client": clientID, + "remote_port": pushReq.RemotePort, }) } diff --git a/internal/server/router/handler/store.go b/internal/server/router/handler/store.go index 9697b16..2c996cd 100644 --- a/internal/server/router/handler/store.go +++ b/internal/server/router/handler/store.go @@ -131,6 +131,7 @@ func (h *StoreHandler) Install(c *gin.Context) { Source: string(source), Signature: string(signature), RuleName: req.PluginName, + RemotePort: req.RemotePort, AutoStart: true, } diff --git a/web/components.d.ts b/web/components.d.ts index 142cf0c..3a12423 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -12,6 +12,7 @@ export {} declare module 'vue' { export interface GlobalComponents { HelloWorld: typeof import('./src/components/HelloWorld.vue')['default'] + LogViewer: typeof import('./src/components/LogViewer.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/web/src/api/index.ts b/web/src/api/index.ts index 71d29b8..5f6299c 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -49,8 +49,8 @@ export const disablePlugin = (name: string) => post(`/plugin/${name}/disable`) // 扩展商店 export const getStorePlugins = () => get<{ plugins: StorePluginInfo[] }>('/store/plugins') -export const installStorePlugin = (pluginName: string, downloadUrl: string, signatureUrl: string, clientId: string) => - post('/store/install', { plugin_name: pluginName, download_url: downloadUrl, signature_url: signatureUrl, client_id: clientId }) +export const installStorePlugin = (pluginName: string, downloadUrl: string, signatureUrl: string, clientId: string, remotePort?: number) => + post('/store/install', { plugin_name: pluginName, download_url: downloadUrl, signature_url: signatureUrl, client_id: clientId, remote_port: remotePort || 0 }) // 客户端插件配置 export const getClientPluginConfig = (clientId: string, pluginName: string) => @@ -64,8 +64,8 @@ export const createJSPlugin = (plugin: JSPlugin) => post('/js-plugins', plugin) export const getJSPlugin = (name: string) => get(`/js-plugin/${name}`) export const updateJSPlugin = (name: string, plugin: JSPlugin) => put(`/js-plugin/${name}`, plugin) export const deleteJSPlugin = (name: string) => del(`/js-plugin/${name}`) -export const pushJSPluginToClient = (pluginName: string, clientId: string) => - post(`/js-plugin/${pluginName}/push/${clientId}`) +export const pushJSPluginToClient = (pluginName: string, clientId: string, remotePort?: number) => + post(`/js-plugin/${pluginName}/push/${clientId}`, { remote_port: remotePort || 0 }) export const updateJSPluginConfig = (name: string, config: Record) => put(`/js-plugin/${name}/config`, { config }) export const setJSPluginEnabled = (name: string, enabled: boolean) => diff --git a/web/src/views/PluginsView.vue b/web/src/views/PluginsView.vue index afc21ea..e20f7e5 100644 --- a/web/src/views/PluginsView.vue +++ b/web/src/views/PluginsView.vue @@ -4,7 +4,7 @@ import { useRouter } from 'vue-router' import { NCard, NButton, NSpace, NTag, NStatistic, NGrid, NGi, NEmpty, NSpin, NIcon, NSwitch, NTabs, NTabPane, useMessage, - NSelect, NModal, NInput + NSelect, NModal, NInput, NInputNumber } from 'naive-ui' import { ArrowBackOutline, ExtensionPuzzleOutline, StorefrontOutline, CodeSlashOutline, SettingsOutline } from '@vicons/ionicons5' import { @@ -131,12 +131,34 @@ const loadClients = async () => { } } -const handlePushJSPlugin = async (pluginName: string, clientId: string) => { +// JS 插件推送相关 +const showPushModal = ref(false) +const selectedJSPlugin = ref(null) +const pushClientId = ref('') +const pushRemotePort = ref(8080) +const pushing = ref(false) + +const openPushModal = (plugin: JSPlugin) => { + selectedJSPlugin.value = plugin + pushClientId.value = '' + pushRemotePort.value = 8080 + showPushModal.value = true +} + +const handlePushJSPlugin = async () => { + if (!selectedJSPlugin.value || !pushClientId.value) { + message.warning('请选择要推送到的客户端') + return + } + pushing.value = true try { - await pushJSPluginToClient(pluginName, clientId) - message.success(`已推送 ${pluginName} 到 ${clientId}`) - } catch (e) { - message.error('推送失败') + await pushJSPluginToClient(selectedJSPlugin.value.name, pushClientId.value, pushRemotePort.value || 0) + message.success(`已推送 ${selectedJSPlugin.value.name} 到 ${pushClientId.value},监听端口: ${pushRemotePort.value || '未指定'}`) + showPushModal.value = false + } catch (e: any) { + message.error(e.response?.data || '推送失败') + } finally { + pushing.value = false } } @@ -208,11 +230,13 @@ const toggleJSPlugin = async (plugin: JSPlugin) => { const showInstallModal = ref(false) const selectedStorePlugin = ref(null) const selectedClientId = ref('') +const storePluginRemotePort = ref(8080) const installing = ref(false) const openInstallModal = (plugin: StorePluginInfo) => { selectedStorePlugin.value = plugin selectedClientId.value = '' + storePluginRemotePort.value = 8080 showInstallModal.value = true } @@ -235,7 +259,8 @@ const handleInstallStorePlugin = async () => { selectedStorePlugin.value.name, selectedStorePlugin.value.download_url, selectedStorePlugin.value.signature_url, - selectedClientId.value + selectedClientId.value, + storePluginRemotePort.value || 0 ) message.success(`已安装 ${selectedStorePlugin.value.name} 到客户端`) showInstallModal.value = false @@ -404,14 +429,14 @@ onMounted(() => { 配置 - + type="primary" + @click="openPushModal(plugin)" + > + 推送到客户端 + @@ -439,6 +464,16 @@ onMounted(() => { placeholder="选择要安装到的客户端" :options="onlineClients.map(c => ({ label: c.nickname || c.id, value: c.id }))" /> +
+

远程端口(服务端监听端口):

+ +
+ + + + +
+

插件: {{ selectedJSPlugin.name }}

+

{{ selectedJSPlugin.description || '无描述' }}

+
+ +
+

远程端口(服务端监听端口):

+ +

用户可以通过 服务端IP:端口 访问此插件提供的服务

+
+
+ +