fix(tunnels): 恢复 SSH config 别名选择 (#3540)
This commit is contained in:
4 files changed
+123
-19
No files matched your search
@@ -20,6 +20,7 @@ import type { NacosAdminConfig, NacosAuthConfig } from "@/types/nacos";
|
||||
import { CONNECTION_ATTEMPT_CANCELLED_MESSAGE, useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useTunnelProfileStore } from "@/stores/tunnelProfileStore";
|
||||
import { detachTunnelProfileLayer, tunnelProfileReferenceLayer, tunnelProfileSummary } from "@/lib/connection/tunnelProfiles";
|
||||
import { applySshConfigHostAliasPrefill as prefillSshConfigHostAlias } from "@/lib/connection/sshConfigHosts";
|
||||
import { REDIS_SCAN_PAGE_SIZE_DEFAULT, REDIS_SCAN_PAGE_SIZE_MIN, REDIS_SCAN_PAGE_SIZE_MAX, REDIS_SCAN_PAGE_SIZE_OPTIONS } from "@/lib/redis/redisKeyPattern";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
@@ -3480,24 +3481,8 @@ watch([() => editingId.value, () => open.value], () => {
|
||||
|
||||
const sshConfigHostAliases = computed(() => sshConfigHosts.value.map((entry) => entry.alias));
|
||||
|
||||
/**
|
||||
* Prefills user/port/key_path from a matching ~/.ssh/config alias, without
|
||||
* overwriting values the user already changed away from the form defaults.
|
||||
* This is a UX preview only — the authoritative resolution happens in the
|
||||
* Rust backend at connect time (see resolve_ssh_tunnel_config), so imported
|
||||
* configs that never touched this UI still resolve correctly.
|
||||
*/
|
||||
function applySshConfigHostAliasPrefill(target: SshTunnelConfig) {
|
||||
const entry = sshConfigHosts.value.find((candidate) => candidate.alias === target.host);
|
||||
if (!entry) return;
|
||||
if (target.user === DEFAULT_SSH_USER && entry.user) target.user = entry.user;
|
||||
if (target.port === 22 && entry.port) target.port = entry.port;
|
||||
if (!target.key_path && entry.identity_file) {
|
||||
target.key_path = entry.identity_file;
|
||||
if ((!target.auth_method || target.auth_method === "password") && !target.password?.trim()) {
|
||||
target.auth_method = "key";
|
||||
}
|
||||
}
|
||||
prefillSshConfigHostAlias(target, sshConfigHosts.value);
|
||||
}
|
||||
|
||||
async function browseSshKeyPath(target?: SshTunnelConfig | null) {
|
||||
|
||||
@@ -10,7 +10,9 @@ import { Loader2, Plus, Trash2 } from "@lucide/vue";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { useTunnelProfileStore } from "@/stores/tunnelProfileStore";
|
||||
import { createTunnelProfile, createTunnelProfileTestGuard, tunnelProfileSummary, type TunnelProfileType } from "@/lib/connection/tunnelProfiles";
|
||||
import type { TunnelProfile } from "@/types/database";
|
||||
import { applySshConfigHostAliasPrefill as prefillSshConfigHostAlias } from "@/lib/connection/sshConfigHosts";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import type { SshConfigHostEntry, TunnelProfile } from "@/types/database";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -24,6 +26,7 @@ const hasInitializedDraft = ref(false);
|
||||
const isTesting = ref(false);
|
||||
const testResult = ref<{ ok: boolean; message: string } | null>(null);
|
||||
const testGuard = createTunnelProfileTestGuard();
|
||||
const sshConfigHosts = ref<SshConfigHostEntry[]>([]);
|
||||
|
||||
function cloneProfiles(profiles: TunnelProfile[]): TunnelProfile[] {
|
||||
return JSON.parse(JSON.stringify(profiles)) as TunnelProfile[];
|
||||
@@ -56,6 +59,23 @@ const selected = computed(() => draft.value.find((profile) => profile.id === sel
|
||||
const selectedSsh = computed(() => (selected.value?.type === "ssh" ? selected.value : null));
|
||||
const selectedProxy = computed(() => (selected.value?.type === "proxy" ? selected.value : null));
|
||||
const selectedHttp = computed(() => (selected.value?.type === "http_tunnel" ? selected.value : null));
|
||||
const sshConfigHostAliases = computed(() => sshConfigHosts.value.map((entry) => entry.alias));
|
||||
|
||||
async function loadSshConfigHosts() {
|
||||
try {
|
||||
sshConfigHosts.value = await api.listSshConfigHosts();
|
||||
} catch {
|
||||
sshConfigHosts.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelectedSshHost(value: string | number) {
|
||||
if (!selectedSsh.value) return;
|
||||
selectedSsh.value.host = String(value);
|
||||
prefillSshConfigHostAlias(selectedSsh.value, sshConfigHosts.value);
|
||||
}
|
||||
|
||||
void loadSshConfigHosts();
|
||||
|
||||
function invalidateProfileTest() {
|
||||
testGuard.invalidate();
|
||||
@@ -190,7 +210,10 @@ async function testSelected() {
|
||||
<template v-if="selectedSsh">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-xs">{{ t("connection.sshHost") }}</Label>
|
||||
<Input v-model="selectedSsh.host" class="col-span-2" :placeholder="t('connection.sshHostPlaceholder')" />
|
||||
<Input :model-value="selectedSsh.host" class="col-span-2" list="tunnel-profile-ssh-config-host-aliases" :placeholder="t('connection.sshHostPlaceholder')" @update:model-value="updateSelectedSshHost" />
|
||||
<datalist id="tunnel-profile-ssh-config-host-aliases">
|
||||
<option v-for="alias in sshConfigHostAliases" :key="alias" :value="alias" />
|
||||
</datalist>
|
||||
<Input v-model.number="selectedSsh.port" type="number" min="1" max="65535" class="col-span-1" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
|
||||
Reference in new issue
Block a user