diff --git a/cmd/client/main.go b/cmd/client/main.go index f0c4c32..b0f51de 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -3,8 +3,6 @@ package main import ( "flag" "log" - "os" - "path/filepath" "github.com/gotunnel/internal/client/tunnel" "github.com/gotunnel/pkg/crypto" @@ -17,27 +15,19 @@ func main() { token := flag.String("t", "", "auth token") id := flag.String("id", "", "client id (optional, auto-assigned if empty)") noTLS := flag.Bool("no-tls", false, "disable TLS") - skipVerify := flag.Bool("skip-verify", false, "skip TLS certificate verification (insecure)") flag.Parse() if *server == "" || *token == "" { - log.Fatal("Usage: client -s -t [-id ] [-no-tls] [-skip-verify]") + log.Fatal("Usage: client -s -t [-id ] [-no-tls]") } client := tunnel.NewClient(*server, *token, *id) - // TLS 默认启用,使用 TOFU 验证 + // TLS 默认启用,默认跳过证书验证(类似 frp) if !*noTLS { client.TLSEnabled = true - // 获取数据目录 - home, _ := os.UserHomeDir() - dataDir := filepath.Join(home, ".gotunnel") - client.TLSConfig = crypto.ClientTLSConfigWithTOFU(*server, dataDir, *skipVerify) - if *skipVerify { - log.Printf("[Client] TLS enabled (certificate verification DISABLED - insecure)") - } else { - log.Printf("[Client] TLS enabled with TOFU certificate verification") - } + client.TLSConfig = crypto.ClientTLSConfig() + log.Printf("[Client] TLS enabled") } // 初始化插件系统 diff --git a/internal/server/router/api.go b/internal/server/router/api.go index eaf065f..07995f0 100644 --- a/internal/server/router/api.go +++ b/internal/server/router/api.go @@ -826,11 +826,24 @@ func (h *APIHandler) getClientPluginConfig(rw http.ResponseWriter, clientID, plu return } - // 获取插件配置模式 + // 尝试从内置插件获取配置模式 schema, err := h.server.GetPluginConfigSchema(pluginName) if err != nil { - http.Error(rw, err.Error(), http.StatusNotFound) - return + // 如果内置插件中找不到,尝试从 JS 插件获取 + jsPlugin, jsErr := h.jsPluginStore.GetJSPlugin(pluginName) + if jsErr != nil { + // 两者都找不到,返回空 schema(允许配置但没有预定义的 schema) + schema = []ConfigField{} + } else { + // 使用 JS 插件的 config 作为动态 schema + for key := range jsPlugin.Config { + schema = append(schema, ConfigField{ + Key: key, + Label: key, + Type: "string", + }) + } + } } // 查找客户端的插件配置 diff --git a/web/src/views/ClientView.vue b/web/src/views/ClientView.vue index 48c7b84..20f2cc3 100644 --- a/web/src/views/ClientView.vue +++ b/web/src/views/ClientView.vue @@ -3,20 +3,20 @@ import { ref, onMounted } from 'vue' import { useRoute, useRouter } from 'vue-router' import { NCard, NButton, NSpace, NTag, NTable, NEmpty, - NFormItem, NInput, NInputNumber, NSelect, NModal, NCheckbox, NSwitch, + NFormItem, NInput, NInputNumber, NSelect, NModal, NSwitch, NIcon, useMessage, useDialog, NSpin } from 'naive-ui' import { ArrowBackOutline, CreateOutline, TrashOutline, PushOutline, PowerOutline, AddOutline, SaveOutline, CloseOutline, - DownloadOutline, SettingsOutline, StorefrontOutline, RefreshOutline, StopOutline + SettingsOutline, StorefrontOutline, RefreshOutline, StopOutline } from '@vicons/ionicons5' import { getClient, updateClient, deleteClient, pushConfigToClient, disconnectClient, restartClient, - getPlugins, installPluginsToClient, getClientPluginConfig, updateClientPluginConfig, + getClientPluginConfig, updateClientPluginConfig, getStorePlugins, installStorePlugin, getRuleSchemas, restartClientPlugin, stopClientPlugin } from '../api' -import type { ProxyRule, PluginInfo, ClientPlugin, ConfigField, StorePluginInfo, RuleSchemasMap } from '../types' +import type { ProxyRule, ClientPlugin, ConfigField, StorePluginInfo, RuleSchemasMap } from '../types' const route = useRoute() const router = useRouter() @@ -74,11 +74,6 @@ const getExtraFields = (type: string): ConfigField[] => { return schema?.extra_fields || [] } -// 插件安装相关 -const showInstallModal = ref(false) -const availablePlugins = ref([]) -const selectedPlugins = ref([]) - // 插件配置相关 const showConfigModal = ref(false) const configPluginName = ref('') @@ -93,37 +88,6 @@ const storeLoading = ref(false) const selectedStorePlugin = ref(null) const storeInstalling = ref(false) -const loadPlugins = async () => { - try { - const { data } = await getPlugins() - availablePlugins.value = (data || []).filter(p => p.enabled) - - // 更新类型选项:内置类型 + proxy 类型插件 - const proxyPlugins = availablePlugins.value - .filter(p => p.type === 'proxy') - .map(p => ({ label: `${p.name.toUpperCase()} (插件)`, value: p.name })) - typeOptions.value = [...builtinTypes, ...proxyPlugins] - - // 合并插件的 RuleSchema 到 pluginRuleSchemas - for (const p of availablePlugins.value) { - if (p.rule_schema) { - pluginRuleSchemas.value[p.name] = p.rule_schema - } - } - } catch (e) { - console.error('Failed to load plugins', e) - } -} - -const openInstallModal = async () => { - await loadPlugins() - // 过滤掉已安装的插件 - const installedNames = clientPlugins.value.map(p => p.name) - availablePlugins.value = availablePlugins.value.filter(p => !installedNames.includes(p.name)) - selectedPlugins.value = [] - showInstallModal.value = true -} - // 商店插件相关函数 const openStoreModal = async () => { showStoreModal.value = true @@ -160,11 +124,6 @@ const handleInstallStorePlugin = async (plugin: StorePluginInfo) => { } } -const getTypeLabel = (type: string) => { - const labels: Record = { proxy: '协议', app: '应用', service: '服务', tool: '工具' } - return labels[type] || type -} - const loadClient = async () => { try { const { data } = await getClient(clientId) @@ -182,7 +141,6 @@ const loadClient = async () => { onMounted(() => { loadRuleSchemas() // 加载内置协议配置模式 loadClient() - loadPlugins() }) // 打开重命名弹窗 @@ -339,21 +297,6 @@ const handleStopPlugin = async (plugin: ClientPlugin) => { } } -const installPlugins = async () => { - if (selectedPlugins.value.length === 0) { - message.warning('请选择要安装的插件') - return - } - try { - await installPluginsToClient(clientId, selectedPlugins.value) - message.success(`已推送 ${selectedPlugins.value.length} 个插件到客户端`) - showInstallModal.value = false - await loadClient() // 刷新客户端数据 - } catch (e: any) { - message.error(e.response?.data || '安装失败') - } -} - const toggleClientPlugin = async (plugin: ClientPlugin) => { const newEnabled = !plugin.enabled const updatedPlugins = clientPlugins.value.map(p => @@ -441,10 +384,6 @@ const savePluginConfig = async () => { 推送配置 - - - 安装插件 - 从商店安装 @@ -644,39 +583,6 @@ const savePluginConfig = async () => { - - - - - - - - - {{ plugin.name }} - {{ getTypeLabel(plugin.type) }} - - {{ plugin.description }} - - - - - - - -