feat: add authentication options for plugins including AuthEnabled, AuthUsername, and AuthPassword
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 3m27s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 5m51s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 9m56s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 7m58s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 6m5s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 3m45s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 9m10s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 7m44s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 4m49s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 10m19s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 7m35s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 10m2s
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 3m27s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 5m51s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 9m56s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 7m58s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 6m5s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 3m45s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 9m10s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 7m44s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 4m49s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 10m19s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 7m35s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 10m2s
This commit is contained in:
@@ -15,14 +15,17 @@ type ConfigField struct {
|
|||||||
|
|
||||||
// ClientPlugin 客户端已安装的插件
|
// ClientPlugin 客户端已安装的插件
|
||||||
type ClientPlugin struct {
|
type ClientPlugin struct {
|
||||||
ID string `json:"id"` // 插件实例唯一 ID
|
ID string `json:"id"` // 插件实例唯一 ID
|
||||||
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"` // 运行状态
|
Running bool `json:"running"` // 运行状态
|
||||||
Config map[string]string `json:"config,omitempty"` // 插件配置
|
Config map[string]string `json:"config,omitempty"` // 插件配置
|
||||||
RemotePort int `json:"remote_port,omitempty"`// 远程监听端口
|
RemotePort int `json:"remote_port,omitempty"` // 远程监听端口
|
||||||
ConfigSchema []ConfigField `json:"config_schema,omitempty"` // 配置模式
|
ConfigSchema []ConfigField `json:"config_schema,omitempty"` // 配置模式
|
||||||
|
AuthEnabled bool `json:"auth_enabled,omitempty"` // 是否启用认证
|
||||||
|
AuthUsername string `json:"auth_username,omitempty"` // 认证用户名
|
||||||
|
AuthPassword string `json:"auth_password,omitempty"` // 认证密码
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client 客户端数据
|
// Client 客户端数据
|
||||||
|
|||||||
@@ -179,6 +179,9 @@ func (h *StoreHandler) Install(c *gin.Context) {
|
|||||||
if p.Name == req.PluginName {
|
if p.Name == req.PluginName {
|
||||||
dbClient.Plugins[i].Enabled = true
|
dbClient.Plugins[i].Enabled = true
|
||||||
dbClient.Plugins[i].RemotePort = req.RemotePort
|
dbClient.Plugins[i].RemotePort = req.RemotePort
|
||||||
|
dbClient.Plugins[i].AuthEnabled = req.AuthEnabled
|
||||||
|
dbClient.Plugins[i].AuthUsername = req.AuthUsername
|
||||||
|
dbClient.Plugins[i].AuthPassword = req.AuthPassword
|
||||||
// 确保有 ID
|
// 确保有 ID
|
||||||
if dbClient.Plugins[i].ID == "" {
|
if dbClient.Plugins[i].ID == "" {
|
||||||
dbClient.Plugins[i].ID = pluginID
|
dbClient.Plugins[i].ID = pluginID
|
||||||
@@ -212,6 +215,9 @@ func (h *StoreHandler) Install(c *gin.Context) {
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
RemotePort: req.RemotePort,
|
RemotePort: req.RemotePort,
|
||||||
ConfigSchema: configSchema,
|
ConfigSchema: configSchema,
|
||||||
|
AuthEnabled: req.AuthEnabled,
|
||||||
|
AuthUsername: req.AuthUsername,
|
||||||
|
AuthPassword: req.AuthPassword,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1319,10 +1319,13 @@ func (s *Server) pushClientInstalledPlugins(cs *ClientSession, alreadyPushed map
|
|||||||
} else if cp.RemotePort > 0 {
|
} else if cp.RemotePort > 0 {
|
||||||
// 安装成功后启动服务端监听器
|
// 安装成功后启动服务端监听器
|
||||||
pluginRule := protocol.ProxyRule{
|
pluginRule := protocol.ProxyRule{
|
||||||
Name: cp.Name,
|
Name: cp.Name,
|
||||||
Type: cp.Name,
|
Type: cp.Name,
|
||||||
RemotePort: cp.RemotePort,
|
RemotePort: cp.RemotePort,
|
||||||
Enabled: boolPtr(true),
|
Enabled: boolPtr(true),
|
||||||
|
AuthEnabled: cp.AuthEnabled,
|
||||||
|
AuthUsername: cp.AuthUsername,
|
||||||
|
AuthPassword: cp.AuthPassword,
|
||||||
}
|
}
|
||||||
s.startClientPluginListener(cs, pluginRule)
|
s.startClientPluginListener(cs, pluginRule)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,9 +174,12 @@ func (p *JSPlugin) Start() (string, error) {
|
|||||||
func (p *JSPlugin) HandleConn(conn net.Conn) error {
|
func (p *JSPlugin) HandleConn(conn net.Conn) error {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
// goja Runtime 不是线程安全的,需要加锁
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
// 创建连接包装器
|
// 创建连接包装器
|
||||||
jsConn := newJSConn(conn)
|
jsConn := newJSConn(conn)
|
||||||
p.vm.Set("conn", jsConn)
|
|
||||||
|
|
||||||
fn, ok := goja.AssertFunction(p.vm.Get("handleConn"))
|
fn, ok := goja.AssertFunction(p.vm.Get("handleConn"))
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -49,8 +49,30 @@ export const disablePlugin = (name: string) => post(`/plugin/${name}/disable`)
|
|||||||
|
|
||||||
// 扩展商店
|
// 扩展商店
|
||||||
export const getStorePlugins = () => get<{ plugins: StorePluginInfo[] }>('/store/plugins')
|
export const getStorePlugins = () => get<{ plugins: StorePluginInfo[] }>('/store/plugins')
|
||||||
export const installStorePlugin = (pluginName: string, downloadUrl: string, signatureUrl: string, clientId: string, remotePort?: number, version?: string, configSchema?: ConfigField[]) =>
|
export const installStorePlugin = (
|
||||||
post('/store/install', { plugin_name: pluginName, version: version || '', download_url: downloadUrl, signature_url: signatureUrl, client_id: clientId, remote_port: remotePort || 0, config_schema: configSchema || [] })
|
pluginName: string,
|
||||||
|
downloadUrl: string,
|
||||||
|
signatureUrl: string,
|
||||||
|
clientId: string,
|
||||||
|
remotePort?: number,
|
||||||
|
version?: string,
|
||||||
|
configSchema?: ConfigField[],
|
||||||
|
authEnabled?: boolean,
|
||||||
|
authUsername?: string,
|
||||||
|
authPassword?: string
|
||||||
|
) =>
|
||||||
|
post('/store/install', {
|
||||||
|
plugin_name: pluginName,
|
||||||
|
version: version || '',
|
||||||
|
download_url: downloadUrl,
|
||||||
|
signature_url: signatureUrl,
|
||||||
|
client_id: clientId,
|
||||||
|
remote_port: remotePort || 0,
|
||||||
|
config_schema: configSchema || [],
|
||||||
|
auth_enabled: authEnabled || false,
|
||||||
|
auth_username: authUsername || '',
|
||||||
|
auth_password: authPassword || ''
|
||||||
|
})
|
||||||
|
|
||||||
// 客户端插件配置
|
// 客户端插件配置
|
||||||
export const getClientPluginConfig = (clientId: string, pluginName: string) =>
|
export const getClientPluginConfig = (clientId: string, pluginName: string) =>
|
||||||
|
|||||||
@@ -88,6 +88,14 @@ const storePlugins = ref<StorePluginInfo[]>([])
|
|||||||
const storeLoading = ref(false)
|
const storeLoading = ref(false)
|
||||||
const storeInstalling = ref<string | null>(null) // 正在安装的插件名称
|
const storeInstalling = ref<string | null>(null) // 正在安装的插件名称
|
||||||
|
|
||||||
|
// 安装配置模态框
|
||||||
|
const showInstallConfigModal = ref(false)
|
||||||
|
const installPlugin = ref<StorePluginInfo | null>(null)
|
||||||
|
const installRemotePort = ref<number | null>(8080)
|
||||||
|
const installAuthEnabled = ref(false)
|
||||||
|
const installAuthUsername = ref('')
|
||||||
|
const installAuthPassword = ref('')
|
||||||
|
|
||||||
// 日志查看相关
|
// 日志查看相关
|
||||||
const showLogViewer = ref(false)
|
const showLogViewer = ref(false)
|
||||||
|
|
||||||
@@ -111,11 +119,34 @@ const handleInstallStorePlugin = async (plugin: StorePluginInfo) => {
|
|||||||
message.error('该插件没有下载地址')
|
message.error('该插件没有下载地址')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 打开配置模态框
|
||||||
|
installPlugin.value = plugin
|
||||||
|
installRemotePort.value = 8080
|
||||||
|
installAuthEnabled.value = false
|
||||||
|
installAuthUsername.value = ''
|
||||||
|
installAuthPassword.value = ''
|
||||||
|
showInstallConfigModal.value = true
|
||||||
|
}
|
||||||
|
|
||||||
storeInstalling.value = plugin.name
|
const confirmInstallPlugin = async () => {
|
||||||
|
if (!installPlugin.value) return
|
||||||
|
|
||||||
|
storeInstalling.value = installPlugin.value.name
|
||||||
try {
|
try {
|
||||||
await installStorePlugin(plugin.name, plugin.download_url, plugin.signature_url || '', clientId, 8080, plugin.version, plugin.config_schema)
|
await installStorePlugin(
|
||||||
message.success(`已安装 ${plugin.name},可在配置中修改端口和其他设置`)
|
installPlugin.value.name,
|
||||||
|
installPlugin.value.download_url || '',
|
||||||
|
installPlugin.value.signature_url || '',
|
||||||
|
clientId,
|
||||||
|
installRemotePort.value || 8080,
|
||||||
|
installPlugin.value.version,
|
||||||
|
installPlugin.value.config_schema,
|
||||||
|
installAuthEnabled.value,
|
||||||
|
installAuthUsername.value,
|
||||||
|
installAuthPassword.value
|
||||||
|
)
|
||||||
|
message.success(`已安装 ${installPlugin.value.name}`)
|
||||||
|
showInstallConfigModal.value = false
|
||||||
showStoreModal.value = false
|
showStoreModal.value = false
|
||||||
await loadClient()
|
await loadClient()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -748,6 +779,44 @@ const handleDeletePlugin = (plugin: ClientPlugin) => {
|
|||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
|
<!-- 安装配置模态框 -->
|
||||||
|
<n-modal v-model:show="showInstallConfigModal" preset="card" title="安装配置" style="width: 450px;">
|
||||||
|
<n-space vertical :size="16">
|
||||||
|
<div v-if="installPlugin">
|
||||||
|
<p style="margin: 0 0 8px 0;"><strong>插件:</strong> {{ installPlugin.name }}</p>
|
||||||
|
<p style="margin: 0; color: #666;">{{ installPlugin.description }}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #666; font-size: 13px;">远程端口:</p>
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="installRemotePort"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
placeholder="输入端口号"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<n-space align="center" :size="8">
|
||||||
|
<n-switch v-model:value="installAuthEnabled" />
|
||||||
|
<span style="color: #666;">启用 HTTP Basic Auth</span>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
<template v-if="installAuthEnabled">
|
||||||
|
<n-input v-model:value="installAuthUsername" placeholder="用户名" />
|
||||||
|
<n-input v-model:value="installAuthPassword" type="password" placeholder="密码" show-password-on="click" />
|
||||||
|
</template>
|
||||||
|
</n-space>
|
||||||
|
<template #footer>
|
||||||
|
<n-space justify="end">
|
||||||
|
<n-button @click="showInstallConfigModal = false">取消</n-button>
|
||||||
|
<n-button type="primary" :loading="!!storeInstalling" @click="confirmInstallPlugin">
|
||||||
|
安装
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
|
||||||
<!-- 日志查看模态框 -->
|
<!-- 日志查看模态框 -->
|
||||||
<n-modal v-model:show="showLogViewer" preset="card" style="width: 900px; max-width: 95vw;">
|
<n-modal v-model:show="showLogViewer" preset="card" style="width: 900px; max-width: 95vw;">
|
||||||
<LogViewer :client-id="clientId" :visible="showLogViewer" @close="showLogViewer = false" />
|
<LogViewer :client-id="clientId" :visible="showLogViewer" @close="showLogViewer = false" />
|
||||||
|
|||||||
@@ -231,10 +231,18 @@ const showInstallModal = ref(false)
|
|||||||
const selectedStorePlugin = ref<StorePluginInfo | null>(null)
|
const selectedStorePlugin = ref<StorePluginInfo | null>(null)
|
||||||
const selectedClientId = ref('')
|
const selectedClientId = ref('')
|
||||||
const installing = ref(false)
|
const installing = ref(false)
|
||||||
|
const installRemotePort = ref<number | null>(8080)
|
||||||
|
const installAuthEnabled = ref(false)
|
||||||
|
const installAuthUsername = ref('')
|
||||||
|
const installAuthPassword = ref('')
|
||||||
|
|
||||||
const openInstallModal = (plugin: StorePluginInfo) => {
|
const openInstallModal = (plugin: StorePluginInfo) => {
|
||||||
selectedStorePlugin.value = plugin
|
selectedStorePlugin.value = plugin
|
||||||
selectedClientId.value = ''
|
selectedClientId.value = ''
|
||||||
|
installRemotePort.value = 8080
|
||||||
|
installAuthEnabled.value = false
|
||||||
|
installAuthUsername.value = ''
|
||||||
|
installAuthPassword.value = ''
|
||||||
showInstallModal.value = true
|
showInstallModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,11 +266,14 @@ const handleInstallStorePlugin = async () => {
|
|||||||
selectedStorePlugin.value.download_url,
|
selectedStorePlugin.value.download_url,
|
||||||
selectedStorePlugin.value.signature_url,
|
selectedStorePlugin.value.signature_url,
|
||||||
selectedClientId.value,
|
selectedClientId.value,
|
||||||
8080, // 默认端口,可在配置中修改
|
installRemotePort.value || 8080,
|
||||||
selectedStorePlugin.value.version,
|
selectedStorePlugin.value.version,
|
||||||
selectedStorePlugin.value.config_schema
|
selectedStorePlugin.value.config_schema,
|
||||||
|
installAuthEnabled.value,
|
||||||
|
installAuthUsername.value,
|
||||||
|
installAuthPassword.value
|
||||||
)
|
)
|
||||||
message.success(`已安装 ${selectedStorePlugin.value.name},可在客户端配置中修改端口和其他设置`)
|
message.success(`已安装 ${selectedStorePlugin.value.name}`)
|
||||||
showInstallModal.value = false
|
showInstallModal.value = false
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e.response?.data || '安装失败')
|
message.error(e.response?.data || '安装失败')
|
||||||
@@ -453,7 +464,7 @@ onMounted(() => {
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- 安装商店插件模态框 -->
|
<!-- 安装商店插件模态框 -->
|
||||||
<n-modal v-model:show="showInstallModal" preset="card" title="安装插件" style="width: 400px;">
|
<n-modal v-model:show="showInstallModal" preset="card" title="安装插件" style="width: 450px;">
|
||||||
<n-space vertical :size="16">
|
<n-space vertical :size="16">
|
||||||
<div v-if="selectedStorePlugin">
|
<div v-if="selectedStorePlugin">
|
||||||
<p style="margin: 0 0 8px 0;"><strong>插件:</strong> {{ selectedStorePlugin.name }}</p>
|
<p style="margin: 0 0 8px 0;"><strong>插件:</strong> {{ selectedStorePlugin.name }}</p>
|
||||||
@@ -464,7 +475,26 @@ onMounted(() => {
|
|||||||
placeholder="选择要安装到的客户端"
|
placeholder="选择要安装到的客户端"
|
||||||
:options="onlineClients.map(c => ({ label: c.nickname || c.id, value: c.id }))"
|
:options="onlineClients.map(c => ({ label: c.nickname || c.id, value: c.id }))"
|
||||||
/>
|
/>
|
||||||
<p style="margin: 0; color: #999; font-size: 12px;">安装后可在客户端详情页配置端口和其他设置</p>
|
<div>
|
||||||
|
<p style="margin: 0 0 8px 0; color: #666; font-size: 13px;">远程端口:</p>
|
||||||
|
<n-input-number
|
||||||
|
v-model:value="installRemotePort"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
placeholder="输入端口号"
|
||||||
|
style="width: 100%;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<n-space align="center" :size="8">
|
||||||
|
<n-switch v-model:value="installAuthEnabled" />
|
||||||
|
<span style="color: #666;">启用 HTTP Basic Auth</span>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
<template v-if="installAuthEnabled">
|
||||||
|
<n-input v-model:value="installAuthUsername" placeholder="用户名" />
|
||||||
|
<n-input v-model:value="installAuthPassword" type="password" placeholder="密码" show-password-on="click" />
|
||||||
|
</template>
|
||||||
</n-space>
|
</n-space>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-space justify="end">
|
<n-space justify="end">
|
||||||
|
|||||||
Reference in New Issue
Block a user