diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 59c8281d..b6651f4b 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -58,6 +58,7 @@ "DirectoryService": "Directory Service", "Resource": "Resource", "Database": "Database", + "Web": "Web", "OfflinePlayer": "Offline Player", "Favorite": "Favorite" }, @@ -94,6 +95,7 @@ "ModifyConnectionInfo": "Connect", "Description": "Next connections will use these settings by default, right-click to edit", "OptionalProtocol": "Protocol", + "ConnectMethod": "Connect Method", "OptionalAccount": "Account" }, "Layout": { diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json index 21cb2a08..c15c300e 100644 --- a/i18n/locales/zh.json +++ b/i18n/locales/zh.json @@ -56,6 +56,7 @@ "DirectoryService": "目录服务", "Database": "数据库", "Device": "设备", + "Web": "Web", "Other": "其他", "Favorite": "收藏", "OfflinePlayer": "离线播放器", @@ -94,6 +95,7 @@ "ModifyConnectionInfo": "连接", "Description": "保存后,下次连接将默认使用这些设置,右击重新设置", "OptionalProtocol": "协议", + "ConnectMethod": "连接方法", "OptionalAccount": "账号" }, "SettingModal": { diff --git a/public/icons/browser.png b/public/icons/browser.png new file mode 100644 index 00000000..98fcf3ec Binary files /dev/null and b/public/icons/browser.png differ diff --git a/src-tauri/src/commands/get_connect_methods.rs b/src-tauri/src/commands/get_connect_methods.rs new file mode 100644 index 00000000..237f4607 --- /dev/null +++ b/src-tauri/src/commands/get_connect_methods.rs @@ -0,0 +1,38 @@ +use crate::commands::auth_login::ensure_fresh_token; +use crate::service::connect_methods::ConnectMethodsService; +use log::{error, info}; +use serde_json::json; +use tauri::{AppHandle, Emitter}; + +#[tauri::command] +pub async fn get_connect_methods(app: AppHandle, site: String, bearer_token: String) { + let bearer = match ensure_fresh_token(&app, &site, Some(&bearer_token)).await { + Ok(b) => b, + Err(e) => { + let _ = app.emit( + "get-connect-methods-failure", + json!({ "status": 401, "error": e.to_string() }), + ); + return; + } + }; + let connect_methods_service = ConnectMethodsService::new(site, bearer); + let connect_methods_data = connect_methods_service.get_connect_methods().await; + + if !connect_methods_data.success { + error!("获取 ConnectMethods 数据失败"); + + let _ = app.emit( + "get-connect-methods-failure", + json!({ "status": connect_methods_data.status }), + ); + return; + } + + info!("获取 ConnectMethods 数据成功"); + + let _ = app.emit( + "get-connect-methods-success", + json!({ "status": connect_methods_data.status, "data": connect_methods_data.data }), + ); +} \ No newline at end of file diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 17d18f77..bb7c126d 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -2,9 +2,11 @@ pub mod auth_login; pub mod get_asset_detail; pub mod get_assets; pub mod get_config; +pub mod get_connect_methods; pub mod get_setting; pub mod get_token; pub mod get_version_message; +pub mod http_callback; pub mod list_system_fonts; pub mod logout; pub mod pull_up; @@ -14,4 +16,3 @@ pub mod set_favorite; pub mod unfavorite; pub mod update_config; pub mod window_controls; -pub mod http_callback; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e49fdb36..5987d0d0 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,6 +11,7 @@ use crate::commands::auth_login::{auth_cancel, auth_login, handle_auth_callback, use crate::commands::get_asset_detail::get_asset_detail; use crate::commands::get_assets::get_assets; use crate::commands::get_config::get_config; +use crate::commands::get_connect_methods::get_connect_methods; use crate::commands::get_setting::get_setting; use crate::commands::get_token::get_connect_token; use crate::commands::get_version_message::get_version_message; @@ -195,6 +196,7 @@ pub fn run() { toggle_maximize_window, update_config_selection, init_http_callback_server, + get_connect_methods, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/service/asset.rs b/src-tauri/src/service/asset.rs index 1b8bed3a..ecc17d4f 100644 --- a/src-tauri/src/service/asset.rs +++ b/src-tauri/src/service/asset.rs @@ -13,6 +13,7 @@ pub enum Category { WindowsAd, Database, Device, + Web, } #[derive(Serialize, Deserialize, Default, Debug, Clone)] @@ -43,7 +44,9 @@ impl AssetQuery { pub fn new(asset_type: Category, org: String) -> Self { let (r#type, category) = match asset_type { Category::Database | Category::Device => (None, Some(asset_type)), - Category::Linux | Category::Windows | Category::WindowsAd => (Some(asset_type), None), + Category::Linux | Category::Windows | Category::WindowsAd | Category::Web => { + (Some(asset_type), None) + } }; Self { @@ -115,6 +118,7 @@ impl AssetService { Category::WindowsAd => (Some(Category::WindowsAd), None), Category::Database => (None, Some(Category::Database)), Category::Device => (None, Some(Category::Device)), + Category::Web => (None, Some(Category::Web)), } }; diff --git a/src-tauri/src/service/connect_methods.rs b/src-tauri/src/service/connect_methods.rs new file mode 100644 index 00000000..cd2b9dbe --- /dev/null +++ b/src-tauri/src/service/connect_methods.rs @@ -0,0 +1,20 @@ +use crate::commands::requests::{get_with_response, ApiResponse}; + +pub struct ConnectMethodsService { + origin: String, + bearer_token: String, +} + +impl ConnectMethodsService { + pub fn new(origin: String, bearer_token: String) -> Self { + Self { + origin, + bearer_token, + } + } + + pub async fn get_connect_methods(&self) -> ApiResponse { + let url = format!("{}/api/v1/terminal/components/connect-methods/", self.origin); + get_with_response(&url, &self.bearer_token).await + } +} \ No newline at end of file diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 61d23ac5..dfc002bf 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -9,3 +9,4 @@ pub(crate) mod token_oauth; pub(crate) mod user; pub(crate) mod http_callback; pub(crate) mod version; +pub(crate) mod connect_methods; diff --git a/ui/components/BasePage/basePage.vue b/ui/components/BasePage/basePage.vue index 5cd3375b..9f2a728d 100644 --- a/ui/components/BasePage/basePage.vue +++ b/ui/components/BasePage/basePage.vue @@ -163,6 +163,7 @@ const handleConnectAsset = async (asset: AssetItem) => { manualPassword: saved!.manualPassword || "", dynamicPassword: saved!.dynamicPassword || "", rememberSecret: !!saved!.rememberSecret, + connectMethod: saved!.connectMethod || "", availableProtocols: saved!.availableProtocols || [] }); } diff --git a/ui/components/Card/AssetIcon/assetIcon.vue b/ui/components/Card/AssetIcon/assetIcon.vue index c52d8d1d..acd2526d 100644 --- a/ui/components/Card/AssetIcon/assetIcon.vue +++ b/ui/components/Card/AssetIcon/assetIcon.vue @@ -25,7 +25,8 @@ const imageProps = computed(() => { mongodb: "/icons/mongodb.png", dameng: "/icons/dameng.png", clickhouse: "/icons/clickhouse.png", - windows_ad: "/icons/windows.png" + windows_ad: "/icons/windows.png", + website: "/icons/browser.png" }; const src = iconMap[props.type] || ""; diff --git a/ui/components/ConnectionEditor/connectionEditor.vue b/ui/components/ConnectionEditor/connectionEditor.vue index 175e44e2..4f607237 100644 --- a/ui/components/ConnectionEditor/connectionEditor.vue +++ b/ui/components/ConnectionEditor/connectionEditor.vue @@ -14,6 +14,7 @@ const draftManualUsername = ref(""); const draftManualPassword = ref(""); const draftDynamicPassword = ref(""); const draftRememberSecret = ref(false); +const draftConnectMethod = ref(""); let pendingResolve: ((info: any) => void) | null = null; let pendingReject: ((reason?: any) => void) | null = null; @@ -63,6 +64,7 @@ const initDraft = (asset: AssetItem) => { draftManualPassword.value = saved?.manualPassword || ""; draftDynamicPassword.value = saved?.dynamicPassword || ""; draftRememberSecret.value = saved?.rememberSecret || false; + draftConnectMethod.value = saved?.connectMethod || ""; }; /** @@ -109,6 +111,7 @@ const buildConnectionInfo = () => { manualPassword: draftManualPassword.value || "", dynamicPassword: draftDynamicPassword.value || "", rememberSecret: !!draftRememberSecret.value, + connectMethod: draftConnectMethod.value || "", availableProtocols: normalizeProtocols() }; }; @@ -208,6 +211,7 @@ defineExpose({ open: openModal, close }); v-model:manual-password="draftManualPassword" v-model:dynamic-password="draftDynamicPassword" v-model:remember-secret="draftRememberSecret" + v-model:connect-method="draftConnectMethod" :accounts="currentAsset.permedAccounts || []" :protocols="currentAsset.permedProtocols || []" /> diff --git a/ui/components/EditForm/editForm.vue b/ui/components/EditForm/editForm.vue index 23e61d56..9c619dfb 100644 --- a/ui/components/EditForm/editForm.vue +++ b/ui/components/EditForm/editForm.vue @@ -1,6 +1,7 @@ + + diff --git a/ui/store/modules/userInfo.ts b/ui/store/modules/userInfo.ts index e04ef2a5..6116d841 100644 --- a/ui/store/modules/userInfo.ts +++ b/ui/store/modules/userInfo.ts @@ -1,4 +1,5 @@ import type { ConnectionInfo, PermOrgItem, RdpGraphics, UserData } from "~/types/index"; +import { useConnectMethods } from "~/composables/useConnectMethods"; export type SiteUserData = UserData & { language?: string @@ -62,6 +63,16 @@ export const useUserInfoStore = defineStore( // 初始化当前站点连接信息映射以及 RDP 客户端选项 currentConnectionInfoMap.value = next.connectionInfoMap || {}; currentRdpClientOption.value = next.rdpClientOption || {}; + + // 登录后获取连接方法 + const { fetchConnectMethods } = useConnectMethods(); + nextTick(async () => { + try { + await fetchConnectMethods(); + } catch (error) { + console.debug("Failed to fetch connect methods on login:", error); + } + }); }; /** diff --git a/ui/types/index.ts b/ui/types/index.ts index 03fd990f..9df791e9 100644 --- a/ui/types/index.ts +++ b/ui/types/index.ts @@ -8,7 +8,7 @@ export type LangType = "zh" | "en"; export type LanguagePreference = LangType | "system"; export type CharsetType = "default" | "utf8" | "gbk" | "gb2312" | "ios-8859-1"; export type ResolutionType = "auto" | "1024x768" | "1366x768" | "1600x900" | "1920x1080"; -export type AssetPageType = "linux" | "windows" | "windows_ad" | "database" | "device" | "favorite"; +export type AssetPageType = "linux" | "windows" | "windows_ad" | "database" | "device" | "web" | "favorite"; export interface ActionItem { key: string @@ -224,6 +224,16 @@ export interface ConnectionInfo { rememberSecret?: boolean dynamicPassword?: string availableProtocols?: string[] + connectMethod?: string +} + +export interface ConnectMethod { + name: string + display_name: string + protocols: string[] + type: string + is_default: boolean + is_internal: boolean } export interface RdpGraphics {