1
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 28s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 46s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m26s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 44s
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 1m43s
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) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 44s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m25s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m1s
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 28s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 46s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m26s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 44s
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 1m43s
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) Successful in 1m3s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 44s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m25s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m1s
This commit is contained in:
@@ -7,6 +7,7 @@ type ClientPlugin struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
Running bool `json:"running"` // 运行状态
|
||||||
Config map[string]string `json:"config,omitempty"` // 插件配置
|
Config map[string]string `json:"config,omitempty"` // 插件配置
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,14 +299,14 @@ func (h *ClientHandler) InstallPlugins(c *gin.Context) {
|
|||||||
|
|
||||||
// PluginAction 客户端插件操作
|
// PluginAction 客户端插件操作
|
||||||
// @Summary 插件操作
|
// @Summary 插件操作
|
||||||
// @Description 对客户端插件执行操作(stop/restart/config/delete)
|
// @Description 对客户端插件执行操作(start/stop/restart/config/delete)
|
||||||
// @Tags 客户端
|
// @Tags 客户端
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param id path string true "客户端ID"
|
// @Param id path string true "客户端ID"
|
||||||
// @Param pluginName path string true "插件名称"
|
// @Param pluginName path string true "插件名称"
|
||||||
// @Param action path string true "操作类型" Enums(stop, restart, config, delete)
|
// @Param action path string true "操作类型" Enums(start, stop, restart, config, delete)
|
||||||
// @Param request body dto.ClientPluginActionRequest false "操作参数"
|
// @Param request body dto.ClientPluginActionRequest false "操作参数"
|
||||||
// @Success 200 {object} Response
|
// @Success 200 {object} Response
|
||||||
// @Failure 400 {object} Response
|
// @Failure 400 {object} Response
|
||||||
@@ -325,6 +325,8 @@ func (h *ClientHandler) PluginAction(c *gin.Context) {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch action {
|
switch action {
|
||||||
|
case "start":
|
||||||
|
err = h.app.GetServer().StartClientPlugin(clientID, pluginName, req.RuleName)
|
||||||
case "stop":
|
case "stop":
|
||||||
err = h.app.GetServer().StopClientPlugin(clientID, pluginName, req.RuleName)
|
err = h.app.GetServer().StopClientPlugin(clientID, pluginName, req.RuleName)
|
||||||
case "restart":
|
case "restart":
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type ServerInterface interface {
|
|||||||
SyncPluginConfigToClient(clientID string, pluginName string, config map[string]string) error
|
SyncPluginConfigToClient(clientID string, pluginName string, config map[string]string) error
|
||||||
InstallJSPluginToClient(clientID string, req JSPluginInstallRequest) error
|
InstallJSPluginToClient(clientID string, req JSPluginInstallRequest) error
|
||||||
RestartClient(clientID string) error
|
RestartClient(clientID string) error
|
||||||
|
StartClientPlugin(clientID, pluginName, ruleName string) error
|
||||||
StopClientPlugin(clientID, pluginName, ruleName string) error
|
StopClientPlugin(clientID, pluginName, ruleName string) error
|
||||||
RestartClientPlugin(clientID, pluginName, ruleName string) error
|
RestartClientPlugin(clientID, pluginName, ruleName string) error
|
||||||
UpdateClientPluginConfig(clientID, pluginName, ruleName string, config map[string]string, restart bool) error
|
UpdateClientPluginConfig(clientID, pluginName, ruleName string, config map[string]string, restart bool) error
|
||||||
|
|||||||
@@ -1231,6 +1231,20 @@ func (s *Server) RestartClient(clientID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartClientPlugin 启动客户端插件
|
||||||
|
func (s *Server) StartClientPlugin(clientID, pluginName, ruleName string) error {
|
||||||
|
s.mu.RLock()
|
||||||
|
_, ok := s.clients[clientID]
|
||||||
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("client %s not found or not online", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新发送安装请求来启动插件
|
||||||
|
return s.reinstallJSPlugin(clientID, pluginName, ruleName)
|
||||||
|
}
|
||||||
|
|
||||||
// StopClientPlugin 停止客户端插件
|
// StopClientPlugin 停止客户端插件
|
||||||
func (s *Server) StopClientPlugin(clientID, pluginName, ruleName string) error {
|
func (s *Server) StopClientPlugin(clientID, pluginName, ruleName string) error {
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ export const installPluginsToClient = (id: string, plugins: string[]) =>
|
|||||||
export const getRuleSchemas = () => get<RuleSchemasMap>('/rule-schemas')
|
export const getRuleSchemas = () => get<RuleSchemasMap>('/rule-schemas')
|
||||||
|
|
||||||
// 客户端插件控制
|
// 客户端插件控制
|
||||||
|
export const startClientPlugin = (clientId: string, pluginName: string, ruleName: string) =>
|
||||||
|
post(`/client/${clientId}/plugin/${pluginName}/start`, { rule_name: ruleName })
|
||||||
export const stopClientPlugin = (clientId: string, pluginName: string, ruleName: string) =>
|
export const stopClientPlugin = (clientId: string, pluginName: string, ruleName: string) =>
|
||||||
post(`/client/${clientId}/plugin/${pluginName}/stop`, { rule_name: ruleName })
|
post(`/client/${clientId}/plugin/${pluginName}/stop`, { rule_name: ruleName })
|
||||||
export const restartClientPlugin = (clientId: string, pluginName: string, ruleName: string) =>
|
export const restartClientPlugin = (clientId: string, pluginName: string, ruleName: string) =>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface ClientPlugin {
|
|||||||
name: string
|
name: string
|
||||||
version: string
|
version: string
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
running: boolean
|
||||||
config?: Record<string, string>
|
config?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
import {
|
import {
|
||||||
ArrowBackOutline, CreateOutline, TrashOutline,
|
ArrowBackOutline, CreateOutline, TrashOutline,
|
||||||
PushOutline, PowerOutline, AddOutline, SaveOutline, CloseOutline,
|
PushOutline, PowerOutline, AddOutline, SaveOutline, CloseOutline,
|
||||||
SettingsOutline, StorefrontOutline, RefreshOutline, StopOutline
|
SettingsOutline, StorefrontOutline, RefreshOutline, StopOutline, PlayOutline
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
import {
|
import {
|
||||||
getClient, updateClient, deleteClient, pushConfigToClient, disconnectClient, restartClient,
|
getClient, updateClient, deleteClient, pushConfigToClient, disconnectClient, restartClient,
|
||||||
getClientPluginConfig, updateClientPluginConfig,
|
getClientPluginConfig, updateClientPluginConfig,
|
||||||
getStorePlugins, installStorePlugin, getRuleSchemas, restartClientPlugin, stopClientPlugin, deleteClientPlugin
|
getStorePlugins, installStorePlugin, getRuleSchemas, startClientPlugin, restartClientPlugin, stopClientPlugin, deleteClientPlugin
|
||||||
} from '../api'
|
} from '../api'
|
||||||
import type { ProxyRule, ClientPlugin, ConfigField, StorePluginInfo, RuleSchemasMap } from '../types'
|
import type { ProxyRule, ClientPlugin, ConfigField, StorePluginInfo, RuleSchemasMap } from '../types'
|
||||||
|
|
||||||
@@ -272,28 +272,43 @@ const handleRestartClient = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动客户端插件
|
||||||
|
const handleStartPlugin = async (plugin: ClientPlugin) => {
|
||||||
|
const rule = rules.value.find(r => r.type === plugin.name)
|
||||||
|
const ruleName = rule?.name || plugin.name
|
||||||
|
try {
|
||||||
|
await startClientPlugin(clientId, plugin.name, ruleName)
|
||||||
|
message.success(`已启动 ${plugin.name}`)
|
||||||
|
plugin.running = true
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e.message || '启动失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 重启客户端插件
|
// 重启客户端插件
|
||||||
const handleRestartPlugin = async (plugin: ClientPlugin) => {
|
const handleRestartPlugin = async (plugin: ClientPlugin) => {
|
||||||
// 找到使用此插件的规则
|
// 找到使用此插件的规则
|
||||||
const rule = rules.value.find(r => r.type === plugin.name)
|
const rule = rules.value.find(r => r.type === plugin.name)
|
||||||
const ruleName = rule?.name || ''
|
const ruleName = rule?.name || plugin.name
|
||||||
try {
|
try {
|
||||||
await restartClientPlugin(clientId, plugin.name, ruleName)
|
await restartClientPlugin(clientId, plugin.name, ruleName)
|
||||||
message.success(`已重启 ${plugin.name}`)
|
message.success(`已重启 ${plugin.name}`)
|
||||||
|
plugin.running = true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.response?.data || '重启失败')
|
message.error(e.message || '重启失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止客户端插件
|
// 停止客户端插件
|
||||||
const handleStopPlugin = async (plugin: ClientPlugin) => {
|
const handleStopPlugin = async (plugin: ClientPlugin) => {
|
||||||
const rule = rules.value.find(r => r.type === plugin.name)
|
const rule = rules.value.find(r => r.type === plugin.name)
|
||||||
const ruleName = rule?.name || ''
|
const ruleName = rule?.name || plugin.name
|
||||||
try {
|
try {
|
||||||
await stopClientPlugin(clientId, plugin.name, ruleName)
|
await stopClientPlugin(clientId, plugin.name, ruleName)
|
||||||
message.success(`已停止 ${plugin.name}`)
|
message.success(`已停止 ${plugin.name}`)
|
||||||
|
plugin.running = false
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.response?.data || '停止失败')
|
message.error(e.message || '停止失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,6 +586,7 @@ const handleDeletePlugin = (plugin: ClientPlugin) => {
|
|||||||
<th>名称</th>
|
<th>名称</th>
|
||||||
<th>版本</th>
|
<th>版本</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
|
<th>启用</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -578,6 +594,10 @@ const handleDeletePlugin = (plugin: ClientPlugin) => {
|
|||||||
<tr v-for="plugin in clientPlugins" :key="plugin.name">
|
<tr v-for="plugin in clientPlugins" :key="plugin.name">
|
||||||
<td>{{ plugin.name }}</td>
|
<td>{{ plugin.name }}</td>
|
||||||
<td>v{{ plugin.version }}</td>
|
<td>v{{ plugin.version }}</td>
|
||||||
|
<td>
|
||||||
|
<n-tag v-if="plugin.running" type="success" size="small">运行中</n-tag>
|
||||||
|
<n-tag v-else type="default" size="small">已停止</n-tag>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<n-switch :value="plugin.enabled" @update:value="toggleClientPlugin(plugin)" />
|
<n-switch :value="plugin.enabled" @update:value="toggleClientPlugin(plugin)" />
|
||||||
</td>
|
</td>
|
||||||
@@ -587,11 +607,15 @@ const handleDeletePlugin = (plugin: ClientPlugin) => {
|
|||||||
<template #icon><n-icon><SettingsOutline /></n-icon></template>
|
<template #icon><n-icon><SettingsOutline /></n-icon></template>
|
||||||
配置
|
配置
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button v-if="online && plugin.enabled" size="small" quaternary type="info" @click="handleRestartPlugin(plugin)">
|
<n-button v-if="online && plugin.enabled && plugin.running" size="small" quaternary type="info" @click="handleRestartPlugin(plugin)">
|
||||||
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
||||||
重启
|
重启
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button v-if="online && plugin.enabled" size="small" quaternary type="warning" @click="handleStopPlugin(plugin)">
|
<n-button v-if="online && plugin.enabled && !plugin.running" size="small" quaternary type="success" @click="handleStartPlugin(plugin)">
|
||||||
|
<template #icon><n-icon><PlayOutline /></n-icon></template>
|
||||||
|
启动
|
||||||
|
</n-button>
|
||||||
|
<n-button v-if="online && plugin.enabled && plugin.running" size="small" quaternary type="warning" @click="handleStopPlugin(plugin)">
|
||||||
<template #icon><n-icon><StopOutline /></n-icon></template>
|
<template #icon><n-icon><StopOutline /></n-icon></template>
|
||||||
停止
|
停止
|
||||||
</n-button>
|
</n-button>
|
||||||
|
|||||||
Reference in New Issue
Block a user