From 7975a077ea2e600831d2605f8ad46e377de2710d Mon Sep 17 00:00:00 2001 From: Tran Trung Kien Date: Sat, 22 Nov 2025 01:03:05 +0700 Subject: [PATCH 01/24] fix select edit host but not update view (#438) --- src/ui/desktop/apps/host-manager/HostManager.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ui/desktop/apps/host-manager/HostManager.tsx b/src/ui/desktop/apps/host-manager/HostManager.tsx index 284ad37f..dc37723b 100644 --- a/src/ui/desktop/apps/host-manager/HostManager.tsx +++ b/src/ui/desktop/apps/host-manager/HostManager.tsx @@ -42,6 +42,15 @@ export function HostManager({ } }, [initialTab]); + // Update editingHost when hostConfig changes + useEffect(() => { + if (hostConfig) { + setEditingHost(hostConfig); + setActiveTab("add_host"); + lastProcessedHostIdRef.current = hostConfig.id; + } + }, [hostConfig?.id]); + const handleEditHost = (host: SSHHost) => { setEditingHost(host); setActiveTab("add_host"); From 757d0c246dcb387b213c27dbd09dcc15117efcc6 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sat, 22 Nov 2025 20:32:07 -0600 Subject: [PATCH 02/24] fix: Checksum issue with chocolatey --- .github/workflows/electron.yml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index bd1a5b4c..b694378e 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -509,7 +509,6 @@ jobs: submit-to-chocolatey: runs-on: windows-latest if: github.event.inputs.artifact_destination == 'submit' - needs: [build-windows] permissions: contents: read @@ -525,20 +524,25 @@ jobs: $VERSION = (Get-Content package.json | ConvertFrom-Json).version echo "version=$VERSION" >> $env:GITHUB_OUTPUT - - name: Download Windows x64 MSI artifact - uses: actions/download-artifact@v4 - with: - name: termix_windows_x64_msi - path: artifact - - - name: Get MSI file info + - name: Download and prepare MSI info from public release id: msi-info run: | $VERSION = "${{ steps.package-version.outputs.version }}" - $MSI_FILE = Get-ChildItem -Path artifact -Filter "*.msi" | Select-Object -First 1 - $MSI_NAME = $MSI_FILE.Name - $CHECKSUM = (Get-FileHash -Path $MSI_FILE.FullName -Algorithm SHA256).Hash + $MSI_NAME = "termix_windows_x64_msi.msi" + $DOWNLOAD_URL = "https://github.com/Termix-SSH/Termix/releases/download/release-$($VERSION)-tag/$($MSI_NAME)" + Write-Host "Downloading from $DOWNLOAD_URL" + New-Item -ItemType Directory -Force -Path "release_asset" + $DOWNLOAD_PATH = "release_asset\$MSI_NAME" + + try { + Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $DOWNLOAD_PATH -UseBasicParsing + } catch { + Write-Error "Failed to download MSI from $DOWNLOAD_URL. Please ensure the release and asset exist." + exit 1 + } + + $CHECKSUM = (Get-FileHash -Path $DOWNLOAD_PATH -Algorithm SHA256).Hash echo "msi_name=$MSI_NAME" >> $env:GITHUB_OUTPUT echo "checksum=$CHECKSUM" >> $env:GITHUB_OUTPUT From b57cc52c94a57ade020feab5823f89875587fecb Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 00:48:47 -0600 Subject: [PATCH 03/24] fix: Remove homebrew old stuff --- .github/workflows/electron.yml | 270 ++++++++++++++++++++------------- homebrew/termix.rb | 24 --- 2 files changed, 161 insertions(+), 133 deletions(-) delete mode 100644 homebrew/termix.rb diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index b694378e..027685c9 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -27,7 +27,7 @@ on: jobs: build-windows: runs-on: windows-latest - if: github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'windows' || github.event.inputs.build_type == '' + if: (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'windows' || github.event.inputs.build_type == '') && github.event.inputs.artifact_destination != 'submit' permissions: contents: write @@ -72,10 +72,6 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm run build && npx electron-builder --win --x64 --ia32 - - name: List release files - run: | - dir release - - name: Upload Windows x64 NSIS Installer uses: actions/upload-artifact@v4 if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && github.event.inputs.artifact_destination != 'none' @@ -136,7 +132,7 @@ jobs: build-linux: runs-on: blacksmith-4vcpu-ubuntu-2404 - if: github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'linux' || github.event.inputs.build_type == '' + if: (github.event.inputs.build_type == 'all' || github.event.inputs.build_type == 'linux' || github.event.inputs.build_type == '') && github.event.inputs.artifact_destination != 'submit' permissions: contents: write @@ -199,17 +195,6 @@ jobs: cd .. - - name: List release files - run: | - ls -la release/ - - - name: Debug electron-builder output - if: always() - run: | - if [ -f "release/builder-debug.yml" ]; then - cat release/builder-debug.yml - fi - - name: Upload Linux x64 AppImage uses: actions/upload-artifact@v4 if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none' @@ -425,11 +410,6 @@ jobs: export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}" npx electron-builder --mac dmg --universal --x64 --arm64 --publish never - - name: List release directory - if: steps.check_certs.outputs.has_certs == 'true' - run: | - ls -R release/ || echo "Release directory not found" - - name: Upload macOS MAS PKG if: steps.check_certs.outputs.has_certs == 'true' && hashFiles('release/termix_macos_universal_mas.pkg') != '' && (github.event.inputs.artifact_destination == 'file' || github.event.inputs.artifact_destination == 'release' || github.event.inputs.artifact_destination == 'submit') uses: actions/upload-artifact@v4 @@ -463,42 +443,6 @@ jobs: path: release/termix_macos_arm64_dmg.dmg retention-days: 30 - - name: Check for App Store Connect API credentials - if: steps.check_certs.outputs.has_certs == 'true' - id: check_asc_creds - run: | - if [ -n "${{ secrets.APPLE_KEY_ID }}" ] && [ -n "${{ secrets.APPLE_ISSUER_ID }}" ] && [ -n "${{ secrets.APPLE_KEY_CONTENT }}" ]; then - echo "has_credentials=true" >> $GITHUB_OUTPUT - fi - - - name: Setup Ruby for Fastlane - if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit' - uses: ruby/setup-ruby@v1 - with: - ruby-version: "3.2" - bundler-cache: false - - - name: Install Fastlane - if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit' - run: | - gem install fastlane -N - - - name: Deploy to App Store Connect (TestFlight) - if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit' - run: | - PKG_FILE=$(find release -name "*.pkg" -type f | head -n 1) - if [ -z "$PKG_FILE" ]; then - exit 1 - fi - - mkdir -p ~/private_keys - echo "${{ secrets.APPLE_KEY_CONTENT }}" | base64 --decode > ~/private_keys/AuthKey_${{ secrets.APPLE_KEY_ID }}.p8 - - xcrun altool --upload-app -f "$PKG_FILE" \ - --type macos \ - --apiKey "${{ secrets.APPLE_KEY_ID }}" \ - --apiIssuer "${{ secrets.APPLE_ISSUER_ID }}" - continue-on-error: true - name: Clean up keychains if: always() @@ -614,7 +558,7 @@ jobs: submit-to-flatpak: runs-on: ubuntu-latest if: github.event.inputs.artifact_destination == 'submit' - needs: [build-linux] + needs: [] permissions: contents: read @@ -632,31 +576,28 @@ jobs: echo "version=$VERSION" >> $GITHUB_OUTPUT echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT - - name: Download Linux x64 AppImage artifact - uses: actions/download-artifact@v4 - with: - name: termix_linux_x64_appimage - path: artifact-x64 - - - name: Download Linux arm64 AppImage artifact - uses: actions/download-artifact@v4 - with: - name: termix_linux_arm64_appimage - path: artifact-arm64 - - - name: Get AppImage file info + - name: Download and prepare AppImage info from public release id: appimage-info run: | VERSION="${{ steps.package-version.outputs.version }}" - - APPIMAGE_X64_FILE=$(find artifact-x64 -name "*.AppImage" -type f | head -n 1) - APPIMAGE_X64_NAME=$(basename "$APPIMAGE_X64_FILE") - CHECKSUM_X64=$(sha256sum "$APPIMAGE_X64_FILE" | awk '{print $1}') - - APPIMAGE_ARM64_FILE=$(find artifact-arm64 -name "*.AppImage" -type f | head -n 1) - APPIMAGE_ARM64_NAME=$(basename "$APPIMAGE_ARM64_FILE") - CHECKSUM_ARM64=$(sha256sum "$APPIMAGE_ARM64_FILE" | awk '{print $1}') - + mkdir -p release_assets + + APPIMAGE_X64_NAME="termix_linux_x64_appimage.AppImage" + URL_X64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_X64_NAME" + PATH_X64="release_assets/$APPIMAGE_X64_NAME" + echo "Downloading x64 AppImage from $URL_X64" + curl -L -o "$PATH_X64" "$URL_X64" + chmod +x "$PATH_X64" + CHECKSUM_X64=$(sha256sum "$PATH_X64" | awk '{print $1}') + + APPIMAGE_ARM64_NAME="termix_linux_arm64_appimage.AppImage" + URL_ARM64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_ARM64_NAME" + PATH_ARM64="release_assets/$APPIMAGE_ARM64_NAME" + echo "Downloading arm64 AppImage from $URL_ARM64" + curl -L -o "$PATH_ARM64" "$URL_ARM64" + chmod +x "$PATH_ARM64" + CHECKSUM_ARM64=$(sha256sum "$PATH_ARM64" | awk '{print $1}') + echo "appimage_x64_name=$APPIMAGE_X64_NAME" >> $GITHUB_OUTPUT echo "checksum_x64=$CHECKSUM_X64" >> $GITHUB_OUTPUT echo "appimage_arm64_name=$APPIMAGE_ARM64_NAME" >> $GITHUB_OUTPUT @@ -694,10 +635,6 @@ jobs: sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" flatpak-submission/com.karmaa.termix.metainfo.xml sed -i "s/DATE_PLACEHOLDER/$RELEASE_DATE/g" flatpak-submission/com.karmaa.termix.metainfo.xml - - name: List submission files - run: | - ls -la flatpak-submission/ - - name: Upload Flatpak submission as artifact uses: actions/upload-artifact@v4 with: @@ -708,7 +645,7 @@ jobs: submit-to-homebrew: runs-on: macos-latest if: github.event.inputs.artifact_destination == 'submit' - needs: [build-macos] + needs: [] permissions: contents: read @@ -724,20 +661,20 @@ jobs: VERSION=$(node -p "require('./package.json').version") echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Download macOS Universal DMG artifact - uses: actions/download-artifact@v4 - with: - name: termix_macos_universal_dmg - path: artifact - - - name: Get DMG file info + - name: Download and prepare DMG info from public release id: dmg-info run: | VERSION="${{ steps.package-version.outputs.version }}" - DMG_FILE=$(find artifact -name "*.dmg" -type f | head -n 1) - DMG_NAME=$(basename "$DMG_FILE") - CHECKSUM=$(shasum -a 256 "$DMG_FILE" | awk '{print $1}') - + DMG_NAME="termix_macos_universal_dmg.dmg" + URL="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$DMG_NAME" + + mkdir -p release_asset + PATH="release_asset/$DMG_NAME" + echo "Downloading DMG from $URL" + curl -L -o "$PATH" "$URL" + + CHECKSUM=$(shasum -a 256 "$PATH" | awk '{print $1}') + echo "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT @@ -756,16 +693,8 @@ jobs: - name: Verify Cask syntax run: | - if ! command -v brew &> /dev/null; then - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - fi - ruby -c homebrew-submission/Casks/t/termix.rb - - name: List submission files - run: | - find homebrew-submission -type f - - name: Upload Homebrew submission as artifact uses: actions/upload-artifact@v4 with: @@ -793,10 +722,6 @@ jobs: env: GH_TOKEN: ${{ github.token }} - - name: Display artifact structure - run: | - ls -R artifacts/ - - name: Upload artifacts to latest release run: | cd artifacts @@ -812,3 +737,130 @@ jobs: done env: GH_TOKEN: ${{ github.token }} + + submit-to-testflight: + runs-on: macos-latest + if: github.event.inputs.artifact_destination == 'submit' + needs: [] + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 1 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install dependencies + run: | + for i in 1 2 3; + do + if npm ci; then + break + else + if [ $i -eq 3 ]; then + exit 1 + fi + sleep 10 + fi + done + npm install --force @rollup/rollup-darwin-arm64 + npm install dmg-license + + - name: Check for Code Signing Certificates + id: check_certs + run: | + if [ -n "${{ secrets.MAC_BUILD_CERTIFICATE_BASE64 }}" ] && [ -n "${{ secrets.MAC_P12_PASSWORD }}" ]; then + echo "has_certs=true" >> $GITHUB_OUTPUT + fi + + - name: Import Code Signing Certificates + if: steps.check_certs.outputs.has_certs == 'true' + env: + MAC_BUILD_CERTIFICATE_BASE64: ${{ secrets.MAC_BUILD_CERTIFICATE_BASE64 }} + MAC_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.MAC_INSTALLER_CERTIFICATE_BASE64 }} + MAC_P12_PASSWORD: ${{ secrets.MAC_P12_PASSWORD }} + MAC_KEYCHAIN_PASSWORD: ${{ secrets.MAC_KEYCHAIN_PASSWORD }} + run: | + APP_CERT_PATH=$RUNNER_TEMP/app_certificate.p12 + INSTALLER_CERT_PATH=$RUNNER_TEMP/installer_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + echo -n "$MAC_BUILD_CERTIFICATE_BASE64" | base64 --decode -o $APP_CERT_PATH + + if [ -n "$MAC_INSTALLER_CERTIFICATE_BASE64" ]; then + echo -n "$MAC_INSTALLER_CERTIFICATE_BASE64" | base64 --decode -o $INSTALLER_CERT_PATH + fi + + security create-keychain -p "$MAC_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$MAC_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + security import $APP_CERT_PATH -P "$MAC_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + + if [ -f "$INSTALLER_CERT_PATH" ]; then + security import $INSTALLER_CERT_PATH -P "$MAC_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + fi + + security list-keychain -d user -s $KEYCHAIN_PATH + + security find-identity -v -p codesigning $KEYCHAIN_PATH + + - name: Build macOS App Store Package + if: steps.check_certs.outputs.has_certs == 'true' + env: + ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES: true + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + CURRENT_VERSION=$(node -p "require('./package.json').version") + BUILD_VERSION="${{ github.run_number }}" + + npm run build && npx electron-builder --mac mas --universal --config.buildVersion="$BUILD_VERSION" + + - name: Check for App Store Connect API credentials + id: check_asc_creds + run: | + if [ -n "${{ secrets.APPLE_KEY_ID }}" ] && [ -n "${{ secrets.APPLE_ISSUER_ID }}" ] && [ -n "${{ secrets.APPLE_KEY_CONTENT }}" ]; then + echo "has_credentials=true" >> $GITHUB_OUTPUT + fi + + - name: Setup Ruby for Fastlane + if: steps.check_asc_creds.outputs.has_credentials == 'true' + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.2" + bundler-cache: false + + - name: Install Fastlane + if: steps.check_asc_creds.outputs.has_credentials == 'true' + run: | + gem install fastlane -N + + - name: Deploy to App Store Connect (TestFlight) + if: steps.check_asc_creds.outputs.has_credentials == 'true' + run: | + PKG_FILE=$(find artifact-mas -name "*.pkg" -type f | head -n 1) + if [ -z "$PKG_FILE" ]; then + echo "PKG file not found, exiting." + exit 1 + fi + + mkdir -p ~/private_keys + echo "${{ secrets.APPLE_KEY_CONTENT }}" | base64 --decode > ~/private_keys/AuthKey_${{ secrets.APPLE_KEY_ID }}.p8 + + xcrun altool --upload-app -f "$PKG_FILE" \ + --type macos \ + --apiKey "${{ secrets.APPLE_KEY_ID }}" \ + --apiIssuer "${{ secrets.APPLE_ISSUER_ID }}" + continue-on-error: true + + - name: Clean up keychains + if: always() + run: | + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true \ No newline at end of file diff --git a/homebrew/termix.rb b/homebrew/termix.rb deleted file mode 100644 index 9522fa73..00000000 --- a/homebrew/termix.rb +++ /dev/null @@ -1,24 +0,0 @@ -cask "termix" do - version "VERSION_PLACEHOLDER" - sha256 "CHECKSUM_PLACEHOLDER" - - url "https://github.com/Termix-SSH/Termix/releases/download/release-#{version}-tag/termix_macos_universal_#{version}_dmg.dmg" - name "Termix" - desc "Web-based server management platform with SSH terminal, tunneling, and file editing" - homepage "https://github.com/Termix-SSH/Termix" - - livecheck do - url :url - strategy :github_latest - end - - app "Termix.app" - - zap trash: [ - "~/Library/Application Support/termix", - "~/Library/Caches/com.karmaa.termix", - "~/Library/Caches/com.karmaa.termix.ShipIt", - "~/Library/Preferences/com.karmaa.termix.plist", - "~/Library/Saved Application State/com.karmaa.termix.savedState", - ] -end From 4da2b985ad2558eafbedbfe6e18ecab1440e84e2 Mon Sep 17 00:00:00 2001 From: junu Date: Wed, 26 Nov 2025 05:11:46 +0900 Subject: [PATCH 04/24] Add Korean translation (#439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 송준우 <2484@coreit.co.kr> --- src/i18n/i18n.ts | 6 +- src/locales/ko/translation.json | 1815 ++++++++++++++++++++++ src/ui/desktop/user/LanguageSwitcher.tsx | 1 + 3 files changed, 1821 insertions(+), 1 deletion(-) create mode 100644 src/locales/ko/translation.json diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 4e440866..0a723003 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -8,12 +8,13 @@ import deTranslation from "../locales/de/translation.json"; import ptbrTranslation from "../locales/pt-BR/translation.json"; import ruTranslation from "../locales/ru/translation.json"; import frTranslation from "../locales/fr/translation.json"; +import koTranslation from "../locales/ko/translation.json"; i18n .use(LanguageDetector) .use(initReactI18next) .init({ - supportedLngs: ["en", "zh", "de", "ptbr", "ru", "fr"], + supportedLngs: ["en", "zh", "de", "ptbr", "ru", "fr", "ko"], fallbackLng: "en", debug: false, @@ -44,6 +45,9 @@ i18n fr: { translation: frTranslation, }, + ko: { + translation: koTranslation, + }, }, interpolation: { diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json new file mode 100644 index 00000000..ff1dea4c --- /dev/null +++ b/src/locales/ko/translation.json @@ -0,0 +1,1815 @@ +{ + "credentials": { + "credentialsViewer": "자격 증명 뷰어", + "manageYourSSHCredentials": "SSH 자격 증명을 안전하게 관리하세요", + "addCredential": "자격 증명 추가", + "createCredential": "자격 증명 생성", + "editCredential": "자격 증명 편집", + "viewCredential": "자격 증명 보기", + "duplicateCredential": "자격 증명 복제", + "deleteCredential": "자격 증명 삭제", + "updateCredential": "자격 증명 업데이트", + "credentialName": "자격 증명 이름", + "credentialDescription": "설명", + "username": "사용자 이름", + "searchCredentials": "자격 증명 검색...", + "selectFolder": "폴더 선택", + "selectAuthType": "인증 유형 선택", + "allFolders": "모든 폴더", + "allAuthTypes": "모든 인증 유형", + "uncategorized": "미분류", + "totalCredentials": "전체", + "keyBased": "키 기반", + "passwordBased": "비밀번호 기반", + "folders": "폴더", + "noCredentialsMatchFilters": "필터와 일치하는 자격 증명이 없습니다", + "noCredentialsYet": "생성된 자격 증명이 없습니다", + "createFirstCredential": "첫 번째 자격 증명을 생성하세요", + "failedToFetchCredentials": "자격 증명을 가져오는 데 실패했습니다", + "credentialDeletedSuccessfully": "자격 증명이 성공적으로 삭제되었습니다", + "failedToDeleteCredential": "자격 증명 삭제에 실패했습니다", + "confirmDeleteCredential": "자격 증명 \"{{name}}\"을(를) 삭제하시겠습니까?", + "credentialCreatedSuccessfully": "자격 증명이 성공적으로 생성되었습니다", + "credentialUpdatedSuccessfully": "자격 증명이 성공적으로 업데이트되었습니다", + "failedToSaveCredential": "자격 증명 저장에 실패했습니다", + "failedToFetchCredentialDetails": "자격 증명 세부 정보를 가져오는 데 실패했습니다", + "failedToFetchHostsUsing": "이 자격 증명을 사용하는 호스트를 가져오는 데 실패했습니다", + "loadingCredentials": "자격 증명 로드 중...", + "retry": "재시도", + "noCredentials": "자격 증명 없음", + "noCredentialsMessage": "아직 자격 증명을 추가하지 않았습니다. \"자격 증명 추가\"를 클릭하여 시작하세요.", + "sshCredentials": "SSH 자격 증명", + "credentialsCount": "{{count}}개 자격 증명", + "refresh": "새로 고침", + "passwordRequired": "비밀번호가 필요합니다", + "sshKeyRequired": "SSH 키가 필요합니다", + "credentialAddedSuccessfully": "자격 증명 \"{{name}}\"이(가) 성공적으로 추가되었습니다", + "general": "일반", + "description": "설명", + "folder": "폴더", + "tags": "태그", + "addTagsSpaceToAdd": "태그 추가 (스페이스바로 추가)", + "password": "비밀번호", + "key": "키", + "sshPrivateKey": "SSH 개인 키", + "upload": "업로드", + "updateKey": "키 업데이트", + "keyPassword": "키 비밀번호", + "keyType": "키 유형", + "keyTypeRSA": "RSA", + "keyTypeECDSA": "ECDSA", + "keyTypeEd25519": "Ed25519", + "updateCredential": "자격 증명 업데이트", + "basicInfo": "기본 정보", + "authentication": "인증", + "organization": "구성", + "basicInformation": "기본 정보", + "basicInformationDescription": "이 자격 증명의 기본 정보를 입력하세요", + "authenticationMethod": "인증 방법", + "authenticationMethodDescription": "SSH 서버 인증 방법을 선택하세요", + "organizationDescription": "폴더와 태그로 자격 증명을 정리하세요", + "enterCredentialName": "자격 증명 이름 입력", + "enterCredentialDescription": "설명 입력 (선택사항)", + "enterUsername": "사용자 이름 입력", + "nameIsRequired": "자격 증명 이름이 필요합니다", + "usernameIsRequired": "사용자 이름이 필요합니다", + "authenticationType": "인증 유형", + "passwordAuthDescription": "비밀번호 인증 사용", + "sshKeyAuthDescription": "SSH 키 인증 사용", + "passwordIsRequired": "비밀번호가 필요합니다", + "sshKeyIsRequired": "SSH 키가 필요합니다", + "sshKeyType": "SSH 키 유형", + "privateKey": "개인 키", + "enterPassword": "비밀번호 입력", + "enterPrivateKey": "개인 키 입력", + "keyPassphrase": "키 암호구문", + "enterKeyPassphrase": "키 암호구문 입력 (선택사항)", + "keyPassphraseOptional": "선택사항: 키에 암호구문이 없으면 비워두세요", + "leaveEmptyToKeepCurrent": "현재 값을 유지하려면 비워두세요", + "uploadKeyFile": "키 파일 업로드", + "generateKeyPairButton": "키 쌍 생성", + "generateKeyPair": "키 쌍 생성", + "generateKeyPairDescription": "새 SSH 키 쌍을 생성합니다. 암호구문으로 키를 보호하려면 먼저 아래 키 비밀번호 필드에 입력하세요.", + "deploySSHKey": "SSH 키 배포", + "deploySSHKeyDescription": "대상 서버에 공개 키 배포", + "sourceCredential": "소스 자격 증명", + "targetHost": "대상 호스트", + "deploymentProcess": "배포 프로세스", + "deploymentProcessDescription": "대상 호스트의 ~/.ssh/authorized_keys 파일에 기존 키를 덮어쓰지 않고 공개 키를 안전하게 추가합니다. 이 작업은 되돌릴 수 있습니다.", + "chooseHostToDeploy": "배포할 호스트 선택...", + "deploying": "배포 중...", + "name": "이름", + "noHostsAvailable": "사용 가능한 호스트 없음", + "noHostsMatchSearch": "검색과 일치하는 호스트 없음", + "sshKeyGenerationNotImplemented": "SSH 키 생성 기능이 곧 제공됩니다", + "connectionTestingNotImplemented": "연결 테스트 기능이 곧 제공됩니다", + "testConnection": "연결 테스트", + "selectOrCreateFolder": "폴더 선택 또는 생성", + "noFolder": "폴더 없음", + "orCreateNewFolder": "또는 새 폴더 생성", + "addTag": "태그 추가", + "saving": "저장 중...", + "credentialId": "자격 증명 ID", + "overview": "개요", + "security": "보안", + "usage": "사용량", + "securityDetails": "보안 세부 정보", + "securityDetailsDescription": "암호화된 자격 증명 정보 보기", + "credentialSecured": "자격 증명 보안 처리됨", + "credentialSecuredDescription": "모든 민감한 데이터는 AES-256으로 암호화됩니다", + "passwordAuthentication": "비밀번호 인증", + "keyAuthentication": "키 인증", + "keyType": "키 유형", + "securityReminder": "보안 알림", + "securityReminderText": "자격 증명을 절대 공유하지 마세요. 모든 데이터는 저장 시 암호화됩니다.", + "hostsUsingCredential": "이 자격 증명을 사용하는 호스트", + "noHostsUsingCredential": "현재 이 자격 증명을 사용하는 호스트가 없습니다", + "timesUsed": "사용 횟수", + "lastUsed": "마지막 사용", + "connectedHosts": "연결된 호스트", + "created": "생성됨", + "lastModified": "마지막 수정", + "usageStatistics": "사용 통계", + "copiedToClipboard": "{{field}}이(가) 클립보드에 복사되었습니다", + "failedToCopy": "클립보드에 복사 실패", + "sshKey": "SSH 키", + "createCredentialDescription": "보안 액세스를 위한 새 SSH 자격 증명 생성", + "editCredentialDescription": "자격 증명 정보 업데이트", + "listView": "목록", + "folderView": "폴더", + "unknownCredential": "알 수 없음", + "confirmRemoveFromFolder": "폴더 \"{{folder}}\"에서 \"{{name}}\"을(를) 제거하시겠습니까? 자격 증명은 \"미분류\"로 이동됩니다.", + "removedFromFolder": "자격 증명 \"{{name}}\"이(가) 폴더에서 성공적으로 제거되었습니다", + "failedToRemoveFromFolder": "폴더에서 자격 증명 제거에 실패했습니다", + "folderRenamed": "폴더 \"{{oldName}}\"이(가) \"{{newName}}\"(으)로 성공적으로 이름이 변경되었습니다", + "failedToRenameFolder": "폴더 이름 변경에 실패했습니다", + "movedToFolder": "자격 증명 \"{{name}}\"이(가) \"{{folder}}\"(으)로 성공적으로 이동되었습니다", + "failedToMoveToFolder": "자격 증명 이동에 실패했습니다", + "sshPublicKey": "SSH 공개 키", + "publicKeyNote": "공개 키는 선택사항이지만 키 유효성 검사를 위해 권장됩니다", + "publicKeyUploaded": "공개 키 업로드됨", + "uploadPublicKey": "공개 키 업로드", + "uploadPrivateKeyFile": "개인 키 파일 업로드", + "uploadPublicKeyFile": "공개 키 파일 업로드", + "privateKeyRequiredForGeneration": "공개 키 생성을 위해서는 개인 키가 필요합니다", + "failedToGeneratePublicKey": "공개 키 생성에 실패했습니다", + "generatePublicKey": "개인 키에서 생성", + "publicKeyGeneratedSuccessfully": "공개 키가 성공적으로 생성되었습니다", + "detectedKeyType": "감지된 키 유형", + "detectingKeyType": "감지 중...", + "optional": "선택사항", + "generateKeyPairNew": "새 키 쌍 생성", + "generateEd25519": "Ed25519 생성", + "generateECDSA": "ECDSA 생성", + "generateRSA": "RSA 생성", + "keyPairGeneratedSuccessfully": "{{keyType}} 키 쌍이 성공적으로 생성되었습니다", + "failedToGenerateKeyPair": "키 쌍 생성에 실패했습니다", + "generateKeyPairNote": "새 SSH 키 쌍을 직접 생성합니다. 이는 양식의 기존 키를 대체합니다.", + "invalidKey": "잘못된 키", + "detectionError": "감지 오류", + "unknown": "알 수 없음" + }, + "dragIndicator": { + "error": "오류: {{error}}", + "dragging": "{{fileName}} 드래그 중", + "preparing": "{{fileName}} 준비 중", + "readySingle": "{{fileName}} 다운로드 준비 완료", + "readyMultiple": "{{count}}개 파일 다운로드 준비 완료", + "batchDrag": "{{count}}개 파일을 데스크톱으로 드래그", + "dragToDesktop": "데스크톱으로 드래그", + "canDragAnywhere": "데스크톱 어디든 파일을 드래그할 수 있습니다" + }, + "sshTools": { + "title": "SSH 도구", + "closeTools": "SSH 도구 닫기", + "keyRecording": "키 녹화", + "startKeyRecording": "키 녹화 시작", + "stopKeyRecording": "키 녹화 중지", + "selectTerminals": "터미널 선택:", + "typeCommands": "명령어 입력 (모든 키 지원):", + "commandsWillBeSent": "명령어가 {{count}}개의 선택된 터미널로 전송됩니다.", + "settings": "설정", + "enableRightClickCopyPaste": "우클릭 복사/붙여넣기 활성화", + "shareIdeas": "SSH 도구의 다음 기능에 대한 아이디어가 있으신가요? 공유해주세요" + }, + "snippets": { + "title": "스니펫", + "new": "새 스니펫", + "create": "스니펫 생성", + "edit": "스니펫 편집", + "run": "실행", + "empty": "스니펫이 없습니다", + "emptyHint": "자주 사용하는 명령어를 저장하기 위해 스니펫을 생성하세요", + "name": "이름", + "description": "설명", + "content": "명령어", + "namePlaceholder": "예: Nginx 재시작", + "descriptionPlaceholder": "선택적 설명", + "contentPlaceholder": "예: sudo systemctl restart nginx", + "nameRequired": "이름이 필요합니다", + "contentRequired": "명령어가 필요합니다", + "createDescription": "빠른 실행을 위한 새 명령어 스니펫 생성", + "editDescription": "이 명령어 스니펫 편집", + "deleteConfirmTitle": "스니펫 삭제", + "deleteConfirmDescription": "\"{{name}}\"을(를) 삭제하시겠습니까?", + "createSuccess": "스니펫이 성공적으로 생성되었습니다", + "updateSuccess": "스니펫이 성공적으로 업데이트되었습니다", + "deleteSuccess": "스니펫이 성공적으로 삭제되었습니다", + "createFailed": "스니펫 생성에 실패했습니다", + "updateFailed": "스니펫 업데이트에 실패했습니다", + "deleteFailed": "스니펫 삭제에 실패했습니다", + "failedToFetch": "스니펫을 가져오는 데 실패했습니다", + "executeSuccess": "실행 중: {{name}}", + "copySuccess": "\"{{name}}\"이(가) 클립보드에 복사되었습니다", + "runTooltip": "터미널에서 이 스니펫 실행", + "copyTooltip": "스니펫을 클립보드에 복사", + "editTooltip": "이 스니펫 편집", + "deleteTooltip": "이 스니펫 삭제" + }, + "commandHistory": { + "title": "기록", + "searchPlaceholder": "명령어 검색...", + "noTerminal": "활성 터미널 없음", + "noTerminalHint": "터미널을 열어 명령어 기록을 확인하세요.", + "empty": "명령어 기록이 없습니다", + "emptyHint": "활성 터미널에서 명령어를 실행하여 기록을 만드세요.", + "noResults": "명령어를 찾을 수 없습니다", + "noResultsHint": "\"{{query}}\"와(과) 일치하는 명령어 없음", + "deleteSuccess": "기록에서 명령어가 삭제되었습니다", + "deleteFailed": "명령어 삭제에 실패했습니다.", + "deleteTooltip": "명령어 삭제", + "tabHint": "터미널에서 Tab 키를 사용하여 명령어 기록에서 자동 완성" + }, + "homepage": { + "loggedInTitle": "로그인되었습니다!", + "loggedInMessage": "로그인되었습니다! 사이드바를 사용하여 사용 가능한 모든 도구에 액세스하세요. 시작하려면 SSH 관리자 탭에서 SSH 호스트를 생성하세요. 생성 후 사이드바의 다른 앱을 사용하여 해당 호스트에 연결할 수 있습니다.", + "failedToLoadAlerts": "알림을 로드하는 데 실패했습니다", + "failedToDismissAlert": "알림 해제에 실패했습니다" + }, + "serverConfig": { + "title": "서버 구성", + "description": "백엔드 서비스에 연결하기 위한 Termix 서버 URL 구성", + "serverUrl": "서버 URL", + "enterServerUrl": "서버 URL을 입력하세요", + "testConnectionFirst": "먼저 연결을 테스트하세요", + "connectionSuccess": "연결 성공!", + "connectionFailed": "연결 실패", + "connectionError": "연결 오류 발생", + "connected": "연결됨", + "disconnected": "연결 끊김", + "configSaved": "구성이 성공적으로 저장되었습니다", + "saveFailed": "구성 저장에 실패했습니다", + "saveError": "구성 저장 오류", + "saving": "저장 중...", + "saveConfig": "구성 저장", + "helpText": "Termix 서버가 실행 중인 URL을 입력하세요 (예: http://localhost:30001 또는 https://your-server.com)", + "warning": "경고", + "notValidatedWarning": "URL이 검증되지 않았습니다 - 올바른지 확인하세요", + "changeServer": "서버 변경", + "mustIncludeProtocol": "서버 URL은 http:// 또는 https://로 시작해야 합니다" + }, + "versionCheck": { + "error": "버전 확인 오류", + "checkFailed": "업데이트 확인에 실패했습니다", + "upToDate": "앱이 최신 상태입니다", + "currentVersion": "현재 버전 {{version}}을(를) 실행 중입니다", + "updateAvailable": "업데이트 사용 가능", + "newVersionAvailable": "새 버전이 사용 가능합니다! 현재 {{current}}을(를) 실행 중이지만 {{latest}}을(를) 사용할 수 있습니다.", + "releasedOn": "{{date}}에 릴리스됨", + "downloadUpdate": "업데이트 다운로드", + "dismiss": "무시", + "checking": "업데이트 확인 중...", + "checkUpdates": "업데이트 확인", + "checkingUpdates": "업데이트 확인 중...", + "refresh": "새로 고침", + "updateRequired": "업데이트 필요", + "updateDismissed": "업데이트 알림이 무시되었습니다", + "noUpdatesFound": "업데이트를 찾을 수 없습니다" + }, + "common": { + "close": "닫기", + "minimize": "최소화", + "online": "온라인", + "offline": "오프라인", + "continue": "계속", + "maintenance": "유지 관리", + "degraded": "성능 저하", + "discord": "Discord", + "error": "오류", + "warning": "경고", + "info": "정보", + "success": "성공", + "loading": "로드 중", + "required": "필수", + "optional": "선택사항", + "connect": "연결", + "connecting": "연결 중...", + "clear": "지우기", + "toggleSidebar": "사이드바 토글", + "sidebar": "사이드바", + "home": "홈", + "expired": "만료됨", + "expiresToday": "오늘 만료", + "expiresTomorrow": "내일 만료", + "expiresInDays": "{{days}}일 후 만료", + "updateAvailable": "업데이트 사용 가능", + "sshPath": "SSH 경로", + "localPath": "로컬 경로", + "noAuthCredentials": "이 SSH 호스트에 사용 가능한 인증 자격 증명이 없습니다", + "noReleases": "릴리스 없음", + "updatesAndReleases": "업데이트 및 릴리스", + "newVersionAvailable": "새 버전 ({{version}})을(를) 사용할 수 있습니다.", + "failedToFetchUpdateInfo": "업데이트 정보를 가져오는 데 실패했습니다", + "preRelease": "사전 릴리스", + "loginFailed": "로그인 실패", + "noReleasesFound": "릴리스를 찾을 수 없습니다.", + "yourBackupCodes": "백업 코드", + "sendResetCode": "재설정 코드 전송", + "verifyCode": "코드 확인", + "resetPassword": "비밀번호 재설정", + "resetCode": "재설정 코드", + "newPassword": "새 비밀번호", + "folder": "폴더", + "file": "파일", + "renamedSuccessfully": "이름이 성공적으로 변경되었습니다", + "deletedSuccessfully": "성공적으로 삭제되었습니다", + "noTunnelConnections": "구성된 터널 연결이 없습니다", + "sshTools": "SSH 도구", + "english": "English", + "chinese": "Chinese", + "german": "German", + "cancel": "취소", + "username": "사용자 이름", + "name": "이름", + "login": "로그인", + "logout": "로그아웃", + "register": "등록", + "password": "비밀번호", + "version": "버전", + "confirmPassword": "비밀번호 확인", + "back": "뒤로", + "email": "이메일", + "submit": "제출", + "change": "변경", + "save": "저장", + "saving": "저장 중...", + "delete": "삭제", + "edit": "편집", + "add": "추가", + "search": "검색", + "confirm": "확인", + "yes": "예", + "no": "아니오", + "ok": "확인", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "important": "중요", + "notEnabled": "활성화되지 않음", + "settingUp": "설정 중...", + "next": "다음", + "previous": "이전", + "refresh": "새로 고침", + "settings": "설정", + "profile": "프로필", + "help": "도움말", + "about": "정보", + "language": "언어", + "autoDetect": "자동 감지", + "changeAccountPassword": "계정 비밀번호 변경", + "passwordResetTitle": "비밀번호 재설정", + "passwordResetDescription": "비밀번호를 재설정하려고 합니다. 모든 활성 세션에서 로그아웃됩니다.", + "enterSixDigitCode": "사용자의 Docker 컨테이너 로그에서 6자리 코드를 입력하세요:", + "enterNewPassword": "사용자의 새 비밀번호를 입력하세요:", + "passwordsDoNotMatch": "비밀번호가 일치하지 않습니다", + "passwordMinLength": "비밀번호는 최소 6자 이상이어야 합니다", + "passwordResetSuccess": "비밀번호가 성공적으로 재설정되었습니다! 이제 새 비밀번호로 로그인할 수 있습니다.", + "failedToInitiatePasswordReset": "비밀번호 재설정 시작에 실패했습니다", + "failedToVerifyResetCode": "재설정 코드 확인에 실패했습니다", + "failedToCompletePasswordReset": "비밀번호 재설정 완료에 실패했습니다", + "documentation": "문서", + "retry": "재시도", + "checking": "확인 중...", + "checkingDatabase": "데이터베이스 연결 확인 중..." + }, + "nav": { + "home": "홈", + "hosts": "호스트", + "credentials": "자격 증명", + "terminal": "터미널", + "tunnels": "터널", + "fileManager": "파일 관리자", + "serverStats": "서버 통계", + "admin": "관리자", + "userProfile": "사용자 프로필", + "tools": "도구", + "snippets": "스니펫", + "newTab": "새 탭", + "splitScreen": "화면 분할", + "closeTab": "탭 닫기", + "sshManager": "SSH 관리자", + "hostManager": "호스트 관리자", + "cannotSplitTab": "이 탭을 분할할 수 없습니다", + "tabNavigation": "탭 탐색" + }, + "admin": { + "title": "관리자 설정", + "oidc": "OIDC", + "users": "사용자", + "userManagement": "사용자 관리", + "makeAdmin": "관리자로 지정", + "removeAdmin": "관리자 제거", + "deleteUser": "사용자 삭제", + "allowRegistration": "등록 허용", + "oidcSettings": "OIDC 설정", + "clientId": "클라이언트 ID", + "clientSecret": "클라이언트 시크릿", + "issuerUrl": "발급자 URL", + "authorizationUrl": "인증 URL", + "tokenUrl": "토큰 URL", + "updateSettings": "설정 업데이트", + "confirmDelete": "이 사용자를 삭제하시겠습니까?", + "confirmMakeAdmin": "이 사용자를 관리자로 지정하시겠습니까?", + "confirmRemoveAdmin": "이 사용자의 관리자 권한을 제거하시겠습니까?", + "externalAuthentication": "외부 인증 (OIDC)", + "configureExternalProvider": "OIDC/OAuth2 인증을 위한 외부 ID 공급자를 구성합니다.", + "userIdentifierPath": "사용자 식별자 경로", + "displayNamePath": "표시 이름 경로", + "scopes": "범위", + "saving": "저장 중...", + "saveConfiguration": "구성 저장", + "reset": "재설정", + "success": "성공", + "loading": "로드 중...", + "refresh": "새로 고침", + "loadingUsers": "사용자 로드 중...", + "username": "사용자 이름", + "type": "유형", + "actions": "작업", + "external": "외부", + "local": "로컬", + "adminManagement": "관리자 관리", + "makeUserAdmin": "사용자를 관리자로 지정", + "adding": "추가 중...", + "currentAdmins": "현재 관리자", + "adminBadge": "관리자", + "removeAdminButton": "관리자 제거", + "general": "일반", + "userRegistration": "사용자 등록", + "allowNewAccountRegistration": "새 계정 등록 허용", + "allowPasswordLogin": "사용자 이름/비밀번호 로그인 허용", + "missingRequiredFields": "필수 필드 누락: {{fields}}", + "oidcConfigurationUpdated": "OIDC 구성이 성공적으로 업데이트되었습니다!", + "failedToFetchOidcConfig": "OIDC 구성을 가져오는 데 실패했습니다", + "failedToFetchRegistrationStatus": "등록 상태를 가져오는 데 실패했습니다", + "failedToFetchPasswordLoginStatus": "비밀번호 로그인 상태를 가져오는 데 실패했습니다", + "failedToFetchUsers": "사용자를 가져오는 데 실패했습니다", + "oidcConfigurationDisabled": "OIDC 구성이 성공적으로 비활성화되었습니다!", + "failedToUpdateOidcConfig": "OIDC 구성 업데이트에 실패했습니다", + "failedToDisableOidcConfig": "OIDC 구성 비활성화에 실패했습니다", + "enterUsernameToMakeAdmin": "관리자로 지정할 사용자 이름 입력", + "userIsNowAdmin": "사용자 {{username}}이(가) 이제 관리자입니다", + "failedToMakeUserAdmin": "사용자를 관리자로 지정하는 데 실패했습니다", + "removeAdminStatus": "{{username}}의 관리자 상태를 제거하시겠습니까?", + "adminStatusRemoved": "{{username}}의 관리자 상태가 제거되었습니다", + "failedToRemoveAdminStatus": "관리자 상태 제거에 실패했습니다", + "deleteUser": "사용자 {{username}}을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", + "userDeletedSuccessfully": "사용자 {{username}}이(가) 성공적으로 삭제되었습니다", + "failedToDeleteUser": "사용자 삭제에 실패했습니다", + "overrideUserInfoUrl": "사용자 정보 URL 재정의 (필수 아님)", + "failedToFetchSessions": "세션을 가져오는 데 실패했습니다", + "sessionRevokedSuccessfully": "세션이 성공적으로 취소되었습니다", + "failedToRevokeSession": "세션 취소에 실패했습니다", + "confirmRevokeSession": "이 세션을 취소하시겠습니까?", + "confirmRevokeAllSessions": "이 사용자의 모든 세션을 취소하시겠습니까?", + "failedToRevokeSessions": "세션 취소에 실패했습니다", + "sessionsRevokedSuccessfully": "세션이 성공적으로 취소되었습니다", + "linkToPasswordAccount": "비밀번호 계정에 연결", + "linkOIDCDialogTitle": "OIDC 계정을 비밀번호 계정에 연결", + "linkOIDCDialogDescription": "{{username}} (OIDC 사용자)를 기존 비밀번호 계정에 연결합니다. 이렇게 하면 비밀번호 계정에 대한 이중 인증이 활성화됩니다.", + "linkOIDCWarningTitle": "경고: OIDC 사용자 데이터가 삭제됩니다", + "linkOIDCActionDeleteUser": "OIDC 사용자 계정 및 모든 데이터 삭제", + "linkOIDCActionAddCapability": "대상 비밀번호 계정에 OIDC 로그인 기능 추가", + "linkOIDCActionDualAuth": "비밀번호 계정이 비밀번호와 OIDC 모두로 로그인할 수 있도록 허용", + "linkTargetUsernameLabel": "대상 비밀번호 계정 사용자 이름", + "linkTargetUsernamePlaceholder": "비밀번호 계정의 사용자 이름 입력", + "linkAccountsButton": "계정 연결", + "linkingAccounts": "연결 중...", + "accountsLinkedSuccessfully": "OIDC 사용자 {{oidcUsername}}이(가) {{targetUsername}}에 연결되었습니다", + "failedToLinkAccounts": "계정 연결에 실패했습니다", + "linkTargetUsernameRequired": "대상 사용자 이름이 필요합니다", + "unlinkOIDCTitle": "OIDC 인증 연결 해제", + "unlinkOIDCDescription": "{{username}}에서 OIDC 인증을 제거하시겠습니까? 사용자는 이후 사용자 이름/비밀번호로만 로그인할 수 있습니다.", + "unlinkOIDCSuccess": "{{username}}에서 OIDC가 연결 해제되었습니다", + "failedToUnlinkOIDC": "OIDC 연결 해제에 실패했습니다", + "databaseSecurity": "데이터베이스 보안", + "encryptionStatus": "암호화 상태", + "encryptionEnabled": "암호화 활성화됨", + "enabled": "활성화됨", + "disabled": "비활성화됨", + "keyId": "키 ID", + "created": "생성됨", + "migrationStatus": "마이그레이션 상태", + "migrationCompleted": "마이그레이션 완료", + "migrationRequired": "마이그레이션 필요", + "deviceProtectedMasterKey": "환경 보호 마스터 키", + "legacyKeyStorage": "레거시 키 저장소", + "masterKeyEncryptedWithDeviceFingerprint": "환경 지문으로 암호화된 마스터 키 (KEK 보호 활성)", + "keyNotProtectedByDeviceBinding": "환경 바인딩으로 보호되지 않는 키 (업그레이드 권장)", + "valid": "유효", + "initializeDatabaseEncryption": "데이터베이스 암호화 초기화", + "enableAes256EncryptionWithDeviceBinding": "환경 바인딩 마스터 키 보호를 사용하여 AES-256 암호화를 활성화합니다. SSH 키, 비밀번호 및 인증 토큰에 대한 엔터프라이즈급 보안을 생성합니다.", + "featuresEnabled": "활성화된 기능:", + "aes256GcmAuthenticatedEncryption": "AES-256-GCM 인증 암호화", + "deviceFingerprintMasterKeyProtection": "환경 지문 마스터 키 보호 (KEK)", + "pbkdf2KeyDerivation": "100K 반복을 사용한 PBKDF2 키 파생", + "automaticKeyManagement": "자동 키 관리 및 순환", + "initializing": "초기화 중...", + "initializeEnterpriseEncryption": "엔터프라이즈 암호화 초기화", + "migrateExistingData": "기존 데이터 마이그레이션", + "encryptExistingUnprotectedData": "데이터베이스의 기존 보호되지 않은 데이터를 암호화합니다. 이 프로세스는 안전하며 자동 백업을 생성합니다.", + "testMigrationDryRun": "암호화 호환성 확인", + "migrating": "마이그레이션 중...", + "migrateData": "데이터 마이그레이션", + "securityInformation": "보안 정보", + "sshPrivateKeysEncryptedWithAes256": "SSH 개인 키 및 비밀번호는 AES-256-GCM으로 암호화됩니다", + "userAuthTokensProtected": "사용자 인증 토큰 및 2FA 비밀이 보호됩니다", + "masterKeysProtectedByDeviceFingerprint": "마스터 암호화 키는 장치 지문(KEK)으로 보호됩니다", + "keysBoundToServerInstance": "키는 현재 서버 환경에 바인딩됩니다 (환경 변수를 통해 마이그레이션 가능)", + "pbkdf2HkdfKeyDerivation": "100K 반복을 사용한 PBKDF2 + HKDF 키 파생", + "backwardCompatibleMigration": "마이그레이션 중에도 모든 데이터는 이전 버전과 호환됩니다", + "enterpriseGradeSecurityActive": "엔터프라이즈급 보안 활성", + "masterKeysProtectedByDeviceBinding": "마스터 암호화 키는 환경 지문으로 보호됩니다. 서버 호스트 이름, 경로 및 기타 환경 정보를 사용하여 보호 키를 생성합니다. 서버를 마이그레이션하려면 새 서버에서 DB_ENCRYPTION_KEY 환경 변수를 설정하세요.", + "important": "중요", + "keepEncryptionKeysSecure": "데이터 보안 보장: 데이터베이스 파일 및 서버 구성을 정기적으로 백업하세요. 새 서버로 마이그레이션하려면 새 환경에서 DB_ENCRYPTION_KEY 환경 변수를 설정하거나 동일한 호스트 이름 및 디렉터리 구조를 유지하세요.", + "loadingEncryptionStatus": "암호화 상태 로드 중...", + "testMigrationDescription": "실제로 데이터를 수정하지 않고 기존 데이터를 암호화된 형식으로 안전하게 마이그레이션할 수 있는지 확인합니다", + "serverMigrationGuide": "서버 마이그레이션 가이드", + "migrationInstructions": "암호화된 데이터를 새 서버로 마이그레이션하려면: 1) 데이터베이스 파일 백업, 2) 새 서버에서 환경 변수 DB_ENCRYPTION_KEY=\"your-key\" 설정, 3) 데이터베이스 파일 복원", + "environmentProtection": "환경 보호", + "environmentProtectionDesc": "서버 환경 정보(호스트 이름, 경로 등)를 기반으로 암호화 키를 보호하며, 환경 변수를 통해 마이그레이션 가능", + "verificationCompleted": "호환성 확인 완료 - 데이터가 변경되지 않았습니다", + "verificationInProgress": "확인 완료", + "dataMigrationCompleted": "데이터 마이그레이션이 성공적으로 완료되었습니다!", + "migrationCompleted": "마이그레이션 완료", + "verificationFailed": "호환성 확인 실패", + "migrationFailed": "마이그레이션 실패", + "runningVerification": "호환성 확인 실행 중...", + "startingMigration": "마이그레이션 시작 중...", + "hardwareFingerprintSecurity": "하드웨어 지문 보안", + "hardwareBoundEncryption": "하드웨어 바인딩 암호화 활성", + "masterKeysNowProtectedByHardwareFingerprint": "마스터 키는 이제 환경 변수 대신 실제 하드웨어 지문으로 보호됩니다", + "cpuSerialNumberDetection": "CPU 일련 번호 감지", + "motherboardUuidIdentification": "마더보드 UUID 식별", + "diskSerialNumberVerification": "디스크 일련 번호 확인", + "biosSerialNumberCheck": "BIOS 일련 번호 확인", + "stableMacAddressFiltering": "안정적인 MAC 주소 필터링", + "databaseFileEncryption": "데이터베이스 파일 암호화", + "dualLayerProtection": "이중 계층 보호 활성", + "bothFieldAndFileEncryptionActive": "최대 보안을 위해 필드 수준 및 파일 수준 암호화가 모두 활성화되었습니다", + "fieldLevelAes256Encryption": "민감한 데이터에 대한 필드 수준 AES-256 암호화", + "fileLevelDatabaseEncryption": "하드웨어 바인딩을 사용한 파일 수준 데이터베이스 암호화", + "hardwareBoundFileKeys": "하드웨어 바인딩 파일 암호화 키", + "automaticEncryptedBackups": "자동 암호화 백업 생성", + "createEncryptedBackup": "암호화된 백업 생성", + "creatingBackup": "백업 생성 중...", + "backupCreated": "백업 생성됨", + "encryptedBackupCreatedSuccessfully": "암호화된 백업이 성공적으로 생성되었습니다", + "backupCreationFailed": "백업 생성 실패", + "databaseMigration": "데이터베이스 마이그레이션", + "exportForMigration": "마이그레이션을 위한 내보내기", + "exportDatabaseForHardwareMigration": "새 하드웨어로 마이그레이션하기 위해 복호화된 데이터가 포함된 SQLite 파일로 데이터베이스 내보내기", + "exportDatabase": "SQLite 데이터베이스 내보내기", + "exporting": "내보내기 중...", + "exportCreated": "SQLite 내보내기 생성됨", + "exportContainsDecryptedData": "SQLite 내보내기에는 복호화된 데이터가 포함되어 있습니다 - 안전하게 보관하세요!", + "databaseExportedSuccessfully": "SQLite 데이터베이스가 성공적으로 내보내졌습니다", + "databaseExportFailed": "SQLite 데이터베이스 내보내기 실패", + "importFromMigration": "마이그레이션에서 가져오기", + "importDatabaseFromAnotherSystem": "다른 시스템 또는 하드웨어에서 SQLite 데이터베이스 가져오기", + "importDatabase": "SQLite 데이터베이스 가져오기", + "importing": "가져오기 중...", + "selectedFile": "선택된 SQLite 파일", + "importWillReplaceExistingData": "SQLite 가져오기는 기존 데이터를 대체합니다 - 백업 권장!", + "pleaseSelectImportFile": "SQLite 가져오기 파일을 선택하세요", + "databaseImportedSuccessfully": "SQLite 데이터베이스가 성공적으로 가져와졌습니다", + "databaseImportFailed": "SQLite 데이터베이스 가져오기 실패", + "manageEncryptionAndBackups": "암호화 키, 데이터베이스 보안 및 백업 작업 관리", + "activeSecurityFeatures": "현재 활성화된 보안 조치 및 보호", + "deviceBindingTechnology": "고급 하드웨어 기반 키 보호 기술", + "backupAndRecovery": "안전한 백업 생성 및 데이터베이스 복구 옵션", + "crossSystemDataTransfer": "다른 시스템 간 데이터베이스 내보내기 및 가져오기", + "noMigrationNeeded": "마이그레이션 불필요", + "encryptionKey": "암호화 키", + "keyProtection": "키 보호", + "active": "활성", + "legacy": "레거시", + "dataStatus": "데이터 상태", + "encrypted": "암호화됨", + "needsMigration": "마이그레이션 필요", + "ready": "준비됨", + "initializeEncryption": "암호화 초기화", + "initialize": "초기화", + "test": "테스트", + "migrate": "마이그레이션", + "backup": "백업", + "createBackup": "백업 생성", + "exportImport": "내보내기/가져오기", + "export": "내보내기", + "import": "가져오기", + "passwordRequired": "비밀번호 필요", + "confirmExport": "내보내기 확인", + "exportDescription": "SSH 호스트 및 자격 증명을 SQLite 파일로 내보내기", + "importDescription": "증분 병합으로 SQLite 파일 가져오기 (중복 건너뛰기)", + "criticalWarning": "중요 경고", + "cannotDisablePasswordLoginWithoutOIDC": "OIDC가 구성되지 않은 상태에서는 비밀번호 로그인을 비활성화할 수 없습니다! 비밀번호 로그인을 비활성화하기 전에 OIDC 인증을 구성해야 합니다. 그렇지 않으면 Termix에 액세스할 수 없게 됩니다.", + "confirmDisablePasswordLogin": "비밀번호 로그인을 비활성화하시겠습니까? 계속하기 전에 OIDC가 올바르게 구성되고 작동하는지 확인하세요. 그렇지 않으면 Termix 인스턴스에 액세스할 수 없게 됩니다.", + "passwordLoginDisabled": "비밀번호 로그인이 성공적으로 비활성화되었습니다", + "passwordLoginAndRegistrationDisabled": "비밀번호 로그인 및 새 계정 등록이 성공적으로 비활성화되었습니다", + "requiresPasswordLogin": "비밀번호 로그인 활성화 필요", + "passwordLoginDisabledWarning": "비밀번호 로그인이 비활성화되었습니다. OIDC가 올바르게 구성되어 있는지 확인하세요. 그렇지 않으면 Termix에 로그인할 수 없습니다.", + "oidcRequiredWarning": "중요: 비밀번호 로그인이 비활성화되었습니다. OIDC를 재설정하거나 잘못 구성하면 Termix에 대한 모든 액세스 권한을 잃고 인스턴스가 작동하지 않게 됩니다. 절대적으로 확신하는 경우에만 진행하세요.", + "confirmDisableOIDCWarning": "경고: 비밀번호 로그인도 비활성화된 상태에서 OIDC를 비활성화하려고 합니다. 이렇게 하면 Termix 인스턴스가 작동하지 않게 되고 모든 액세스 권한을 잃게 됩니다. 정말로 진행하시겠습니까?" + }, + "hosts": { + "title": "호스트 관리자", + "sshHosts": "SSH 호스트", + "noHosts": "SSH 호스트 없음", + "noHostsMessage": "아직 SSH 호스트를 추가하지 않았습니다. \"호스트 추가\"를 클릭하여 시작하세요.", + "loadingHosts": "호스트 로드 중...", + "failedToLoadHosts": "호스트 로드 실패", + "retry": "재시도", + "refresh": "새로 고침", + "hostsCount": "{{count}}개 호스트", + "importJson": "JSON 가져오기", + "importing": "가져오기 중...", + "importJsonTitle": "JSON에서 SSH 호스트 가져오기", + "importJsonDesc": "JSON 파일을 업로드하여 여러 SSH 호스트를 일괄 가져오기 (최대 100개).", + "downloadSample": "샘플 다운로드", + "formatGuide": "형식 가이드", + "exportCredentialWarning": "경고: 호스트 \"{{name}}\"은(는) 자격 증명 인증을 사용합니다. 내보낸 파일에는 자격 증명 데이터가 포함되지 않으며 가져오기 후 수동으로 다시 구성해야 합니다. 계속하시겠습니까?", + "exportSensitiveDataWarning": "경고: 호스트 \"{{name}}\"에는 민감한 인증 데이터(비밀번호/SSH 키)가 포함되어 있습니다. 내보낸 파일에는 이 데이터가 평문으로 포함됩니다. 파일을 안전하게 보관하고 사용 후 삭제하세요. 계속하시겠습니까?", + "uncategorized": "미분류", + "confirmDelete": "\"{{name}}\"을(를) 삭제하시겠습니까?", + "failedToDeleteHost": "호스트 삭제 실패", + "failedToExportHost": "호스트 내보내기 실패. 로그인되어 있고 호스트 데이터에 액세스할 수 있는지 확인하세요.", + "jsonMustContainHosts": "JSON에는 \"hosts\" 배열이 포함되어 있거나 호스트 배열이어야 합니다", + "noHostsInJson": "JSON 파일에서 호스트를 찾을 수 없습니다", + "maxHostsAllowed": "가져오기당 최대 100개 호스트 허용", + "importCompleted": "가져오기 완료: {{success}}개 성공, {{failed}}개 실패", + "importFailed": "가져오기 실패", + "importError": "가져오기 오류", + "failedToImportJson": "JSON 파일 가져오기 실패", + "connectionDetails": "연결 세부 정보", + "organization": "구성", + "ipAddress": "IP 주소", + "port": "포트", + "name": "이름", + "username": "사용자 이름", + "folder": "폴더", + "tags": "태그", + "pin": "고정", + "passwordRequired": "비밀번호 인증을 사용할 때 비밀번호가 필요합니다", + "sshKeyRequired": "키 인증을 사용할 때 SSH 개인 키가 필요합니다", + "keyTypeRequired": "키 인증을 사용할 때 키 유형이 필요합니다", + "mustSelectValidSshConfig": "목록에서 유효한 SSH 구성을 선택해야 합니다", + "addHost": "호스트 추가", + "editHost": "호스트 편집", + "cloneHost": "호스트 복제", + "updateHost": "호스트 업데이트", + "hostUpdatedSuccessfully": "호스트 \"{{name}}\"이(가) 성공적으로 업데이트되었습니다!", + "hostAddedSuccessfully": "호스트 \"{{name}}\"이(가) 성공적으로 추가되었습니다!", + "hostDeletedSuccessfully": "호스트 \"{{name}}\"이(가) 성공적으로 삭제되었습니다!", + "failedToSaveHost": "호스트 저장 실패. 다시 시도하세요.", + "enableTerminal": "터미널 활성화", + "enableTerminalDesc": "터미널 탭에서 호스트 표시 활성화/비활성화", + "enableTunnel": "터널 활성화", + "enableTunnelDesc": "터널 탭에서 호스트 표시 활성화/비활성화", + "enableFileManager": "파일 관리자 활성화", + "enableFileManagerDesc": "파일 관리자 탭에서 호스트 표시 활성화/비활성화", + "defaultPath": "기본 경로", + "defaultPathDesc": "이 호스트의 파일 관리자를 열 때 기본 디렉터리", + "tunnelConnections": "터널 연결", + "connection": "연결", + "remove": "제거", + "sourcePort": "소스 포트", + "sourcePortDesc": " (소스는 일반 탭의 현재 연결 세부 정보를 나타냅니다)", + "endpointPort": "엔드포인트 포트", + "endpointSshConfig": "엔드포인트 SSH 구성", + "tunnelForwardDescription": "이 터널은 소스 머신(일반 탭의 현재 연결 세부 정보)의 포트 {{sourcePort}}에서 엔드포인트 머신의 포트 {{endpointPort}}로 트래픽을 전달합니다.", + "maxRetries": "최대 재시도 횟수", + "maxRetriesDescription": "터널 연결에 대한 최대 재시도 횟수.", + "retryInterval": "재시도 간격 (초)", + "retryIntervalDescription": "재시도 시도 사이의 대기 시간.", + "autoStartContainer": "컨테이너 시작 시 자동 시작", + "autoStartDesc": "컨테이너가 시작될 때 이 터널을 자동으로 시작합니다", + "addConnection": "터널 연결 추가", + "sshpassRequired": "비밀번호 인증에 Sshpass 필요", + "sshpassRequiredDesc": "터널에서 비밀번호 인증을 사용하려면 시스템에 sshpass가 설치되어 있어야 합니다.", + "otherInstallMethods": "기타 설치 방법:", + "debianUbuntuEquivalent": "(Debian/Ubuntu) 또는 OS에 해당하는 방법.", + "or": "또는", + "centosRhelFedora": "CentOS/RHEL/Fedora", + "macos": "macOS", + "windows": "Windows", + "sshServerConfigRequired": "SSH 서버 구성 필요", + "sshServerConfigDesc": "터널 연결의 경우 SSH 서버가 포트 포워딩을 허용하도록 구성되어야 합니다:", + "gatewayPortsYes": "모든 인터페이스에 원격 포트를 바인딩하려면", + "allowTcpForwardingYes": "포트 포워딩을 활성화하려면", + "permitRootLoginYes": "터널링에 루트 사용자를 사용하는 경우", + "editSshConfig": "/etc/ssh/sshd_config를 편집하고 SSH를 재시작하세요: sudo systemctl restart sshd", + "upload": "업로드", + "authentication": "인증", + "password": "비밀번호", + "key": "키", + "credential": "자격 증명", + "none": "없음", + "selectCredential": "자격 증명 선택", + "selectCredentialPlaceholder": "자격 증명 선택...", + "credentialRequired": "자격 증명 인증을 사용할 때 자격 증명이 필요합니다", + "credentialDescription": "자격 증명을 선택하면 현재 사용자 이름을 덮어쓰고 자격 증명의 인증 세부 정보를 사용합니다.", + "sshPrivateKey": "SSH 개인 키", + "keyPassword": "키 비밀번호", + "keyType": "키 유형", + "autoDetect": "자동 감지", + "rsa": "RSA", + "ed25519": "ED25519", + "ecdsaNistP256": "ECDSA NIST P-256", + "ecdsaNistP384": "ECDSA NIST P-384", + "ecdsaNistP521": "ECDSA NIST P-521", + "dsa": "DSA", + "rsaSha2256": "RSA SHA2-256", + "rsaSha2512": "RSA SHA2-512", + "uploadFile": "파일 업로드", + "pasteKey": "키 붙여넣기", + "updateKey": "키 업데이트", + "existingKey": "기존 키 (클릭하여 변경)", + "existingCredential": "기존 자격 증명 (클릭하여 변경)", + "addTagsSpaceToAdd": "태그 추가 (스페이스바로 추가)", + "terminalBadge": "터미널", + "tunnelBadge": "터널", + "fileManagerBadge": "파일 관리자", + "general": "일반", + "terminal": "터미널", + "tunnel": "터널", + "fileManager": "파일 관리자", + "serverStats": "서버 통계", + "hostViewer": "호스트 뷰어", + "enableServerStats": "서버 통계 활성화", + "enableServerStatsDesc": "이 호스트에 대한 서버 통계 수집 활성화/비활성화", + "displayItems": "표시 항목", + "displayItemsDesc": "서버 통계 페이지에 표시할 메트릭 선택", + "enableCpu": "CPU 사용량", + "enableMemory": "메모리 사용량", + "enableDisk": "디스크 사용량", + "enableNetwork": "네트워크 통계 (곧 제공)", + "enableProcesses": "프로세스 수 (곧 제공)", + "enableUptime": "가동 시간 (곧 제공)", + "enableHostname": "호스트 이름 (곧 제공)", + "enableOs": "운영 체제 (곧 제공)", + "customCommands": "사용자 지정 명령 (곧 제공)", + "customCommandsDesc": "이 서버에 대한 사용자 지정 종료 및 재부팅 명령 정의", + "shutdownCommand": "종료 명령", + "rebootCommand": "재부팅 명령", + "confirmRemoveFromFolder": "폴더 \"{{folder}}\"에서 \"{{name}}\"을(를) 제거하시겠습니까? 호스트는 \"폴더 없음\"으로 이동됩니다.", + "removedFromFolder": "호스트 \"{{name}}\"이(가) 폴더에서 성공적으로 제거되었습니다", + "failedToRemoveFromFolder": "폴더에서 호스트 제거 실패", + "folderRenamed": "폴더 \"{{oldName}}\"이(가) \"{{newName}}\"(으)로 성공적으로 이름이 변경되었습니다", + "failedToRenameFolder": "폴더 이름 변경 실패", + "editFolderAppearance": "폴더 모양 편집", + "editFolderAppearanceDesc": "폴더의 색상 및 아이콘 사용자 지정", + "folderColor": "폴더 색상", + "folderIcon": "폴더 아이콘", + "preview": "미리보기", + "folderAppearanceUpdated": "폴더 모양이 성공적으로 업데이트되었습니다", + "failedToUpdateFolderAppearance": "폴더 모양 업데이트 실패", + "deleteAllHostsInFolder": "폴더의 모든 호스트 삭제", + "confirmDeleteAllHostsInFolder": "폴더 \"{{folder}}\"의 모든 {{count}}개 호스트를 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", + "allHostsInFolderDeleted": "폴더 \"{{folder}}\"에서 {{count}}개 호스트가 성공적으로 삭제되었습니다", + "failedToDeleteHostsInFolder": "폴더의 호스트 삭제 실패", + "movedToFolder": "호스트 \"{{name}}\"이(가) \"{{folder}}\"(으)로 성공적으로 이동되었습니다", + "failedToMoveToFolder": "호스트를 폴더로 이동하는 데 실패했습니다", + "statistics": "통계", + "enabledWidgets": "활성화된 위젯", + "enabledWidgetsDesc": "이 호스트에 대해 표시할 통계 위젯 선택", + "monitoringConfiguration": "모니터링 구성", + "monitoringConfigurationDesc": "서버 통계 및 상태를 확인하는 빈도 구성", + "statusCheckEnabled": "상태 모니터링 활성화", + "statusCheckEnabledDesc": "서버가 온라인인지 오프라인인지 확인", + "statusCheckInterval": "상태 확인 간격", + "statusCheckIntervalDesc": "호스트가 온라인인지 확인하는 빈도 (5초 - 1시간)", + "metricsEnabled": "메트릭 모니터링 활성화", + "metricsEnabledDesc": "CPU, RAM, 디스크 및 기타 시스템 통계 수집", + "metricsInterval": "메트릭 수집 간격", + "metricsIntervalDesc": "서버 통계를 수집하는 빈도 (5초 - 1시간)", + "intervalSeconds": "초", + "intervalMinutes": "분", + "intervalValidation": "모니터링 간격은 5초에서 1시간(3600초) 사이여야 합니다", + "monitoringDisabled": "이 호스트에 대한 서버 모니터링이 비활성화되었습니다", + "enableMonitoring": "호스트 관리자 → 통계 탭에서 모니터링 활성화", + "monitoringDisabledBadge": "모니터링 꺼짐", + "statusMonitoring": "상태", + "metricsMonitoring": "메트릭", + "terminalCustomizationNotice": "참고: 터미널 사용자 지정은 데스크톱(웹사이트 및 Electron 앱)에서만 작동합니다. 모바일 앱 및 모바일 웹사이트는 시스템 기본 터미널 설정을 사용합니다.", + "terminalCustomization": "터미널 사용자 지정", + "appearance": "모양", + "behavior": "동작", + "advanced": "고급", + "themePreview": "테마 미리보기", + "theme": "테마", + "selectTheme": "테마 선택", + "chooseColorTheme": "터미널의 색상 테마 선택", + "fontFamily": "글꼴", + "selectFont": "글꼴 선택", + "selectFontDesc": "터미널에서 사용할 글꼴 선택", + "fontSize": "글꼴 크기", + "fontSizeValue": "글꼴 크기: {{value}}px", + "adjustFontSize": "터미널 글꼴 크기 조정", + "letterSpacing": "자간", + "letterSpacingValue": "자간: {{value}}px", + "adjustLetterSpacing": "문자 간 간격 조정", + "lineHeight": "줄 높이", + "lineHeightValue": "줄 높이: {{value}}", + "adjustLineHeight": "줄 간 간격 조정", + "cursorStyle": "커서 스타일", + "selectCursorStyle": "커서 스타일 선택", + "cursorStyleBlock": "블록", + "cursorStyleUnderline": "밑줄", + "cursorStyleBar": "바", + "chooseCursorAppearance": "커서 모양 선택", + "cursorBlink": "커서 깜박임", + "enableCursorBlink": "커서 깜박임 애니메이션 활성화", + "scrollbackBuffer": "스크롤백 버퍼", + "scrollbackBufferValue": "스크롤백 버퍼: {{value}}줄", + "scrollbackBufferDesc": "스크롤백 기록에 유지할 줄 수", + "bellStyle": "벨 스타일", + "selectBellStyle": "벨 스타일 선택", + "bellStyleNone": "없음", + "bellStyleSound": "소리", + "bellStyleVisual": "시각적", + "bellStyleBoth": "둘 다", + "bellStyleDesc": "터미널 벨(BEL 문자, \\x07) 처리 방법. 프로그램은 작업 완료, 오류 발생 또는 알림을 위해 이를 트리거합니다. \"소리\"는 오디오 비프음을 재생하고, \"시각적\"은 화면을 잠깐 깜박이며, \"둘 다\"는 둘 다 수행하고, \"없음\"은 벨 알림을 비활성화합니다.", + "rightClickSelectsWord": "우클릭으로 단어 선택", + "rightClickSelectsWordDesc": "우클릭하면 커서 아래의 단어를 선택합니다", + "fastScrollModifier": "빠른 스크롤 수정자", + "selectModifier": "수정자 선택", + "modifierAlt": "Alt", + "modifierCtrl": "Ctrl", + "modifierShift": "Shift", + "fastScrollModifierDesc": "빠른 스크롤을 위한 수정자 키", + "fastScrollSensitivity": "빠른 스크롤 감도", + "fastScrollSensitivityValue": "빠른 스크롤 감도: {{value}}", + "fastScrollSensitivityDesc": "수정자를 누르고 있을 때 스크롤 속도 배수", + "minimumContrastRatio": "최소 대비율", + "minimumContrastRatioValue": "최소 대비율: {{value}}", + "minimumContrastRatioDesc": "가독성 향상을 위해 색상 자동 조정", + "sshAgentForwarding": "SSH 에이전트 포워딩", + "sshAgentForwardingDesc": "SSH 인증 에이전트를 원격 호스트로 전달", + "backspaceMode": "백스페이스 모드", + "selectBackspaceMode": "백스페이스 모드 선택", + "backspaceModeNormal": "일반 (DEL)", + "backspaceModeControlH": "Control-H (^H)", + "backspaceModeDesc": "호환성을 위한 백스페이스 키 동작", + "startupSnippet": "시작 스니펫", + "selectSnippet": "스니펫 선택", + "searchSnippets": "스니펫 검색...", + "snippetNone": "없음", + "noneAuthTitle": "키보드 대화형 인증", + "noneAuthDescription": "이 인증 방법은 SSH 서버에 연결할 때 키보드 대화형 인증을 사용합니다.", + "noneAuthDetails": "키보드 대화형 인증을 사용하면 서버가 연결 중에 자격 증명을 요청할 수 있습니다. 다단계 인증이 필요한 서버나 자격 증명을 로컬에 저장하지 않으려는 경우에 유용합니다.", + "forceKeyboardInteractive": "키보드 대화형 강제", + "forceKeyboardInteractiveDesc": "키보드 대화형 인증을 강제로 사용합니다. 이는 2단계 인증(TOTP/2FA)을 사용하는 서버에 종종 필요합니다.", + "overrideCredentialUsername": "자격 증명 사용자 이름 재정의", + "overrideCredentialUsernameDesc": "자격 증명에 저장된 것과 다른 사용자 이름을 사용합니다. 이를 통해 동일한 자격 증명을 다른 사용자 이름과 함께 사용할 수 있습니다.", + "jumpHosts": "점프 호스트", + "jumpHostsDescription": "점프 호스트(배스천 호스트라고도 함)를 사용하면 하나 이상의 중간 서버를 통해 대상 서버에 연결할 수 있습니다. 방화벽 뒤 또는 개인 네트워크의 서버에 액세스하는 데 유용합니다.", + "jumpHostChain": "점프 호스트 체인", + "addJumpHost": "점프 호스트 추가", + "selectServer": "서버 선택", + "searchServers": "서버 검색...", + "noServerFound": "서버를 찾을 수 없습니다", + "jumpHostsOrder": "연결은 다음 순서로 이루어집니다: 점프 호스트 1 → 점프 호스트 2 → ... → 대상 서버", + "quickActions": "빠른 작업", + "quickActionsDescription": "빠른 작업을 사용하면 이 서버에서 SSH 스니펫을 실행하는 사용자 지정 버튼을 만들 수 있습니다. 이러한 버튼은 빠른 액세스를 위해 서버 통계 페이지 상단에 나타납니다.", + "quickActionsList": "빠른 작업 목록", + "addQuickAction": "빠른 작업 추가", + "quickActionName": "작업 이름", + "noSnippetFound": "스니펫을 찾을 수 없습니다", + "quickActionsOrder": "빠른 작업 버튼은 서버 통계 페이지에 위에 나열된 순서대로 나타납니다", + "advancedAuthSettings": "고급 인증 설정" + }, + "terminal": { + "title": "터미널", + "connect": "호스트에 연결", + "disconnect": "연결 해제", + "clear": "지우기", + "copy": "복사", + "paste": "붙여넣기", + "find": "찾기", + "fullscreen": "전체 화면", + "splitHorizontal": "가로 분할", + "splitVertical": "세로 분할", + "closePanel": "패널 닫기", + "reconnect": "재연결", + "sessionEnded": "세션 종료됨", + "connectionLost": "연결 끊김", + "error": "오류: {{message}}", + "disconnected": "연결 해제됨", + "connectionClosed": "연결이 닫혔습니다", + "connectionError": "연결 오류: {{message}}", + "connected": "연결됨", + "sshConnected": "SSH 연결이 설정되었습니다", + "authError": "인증 실패: {{message}}", + "unknownError": "알 수 없는 오류가 발생했습니다", + "messageParseError": "서버 메시지 파싱 실패", + "websocketError": "WebSocket 연결 오류", + "connecting": "연결 중...", + "reconnecting": "재연결 중... ({{attempt}}/{{max}})", + "reconnected": "성공적으로 재연결되었습니다", + "maxReconnectAttemptsReached": "최대 재연결 시도 횟수에 도달했습니다", + "connectionTimeout": "연결 시간 초과", + "terminalTitle": "터미널 - {{host}}", + "terminalWithPath": "터미널 - {{host}}:{{path}}", + "runTitle": "{{command}} 실행 중 - {{host}}", + "totpRequired": "2단계 인증 필요", + "totpCodeLabel": "인증 코드", + "totpPlaceholder": "000000", + "totpVerify": "확인" + }, + "fileManager": { + "title": "파일 관리자", + "file": "파일", + "folder": "폴더", + "connectToSsh": "파일 작업을 사용하려면 SSH에 연결하세요", + "uploadFile": "파일 업로드", + "downloadFile": "다운로드", + "extractArchive": "압축 해제", + "extractingArchive": "{{name}} 압축 해제 중...", + "archiveExtractedSuccessfully": "{{name}}이(가) 성공적으로 압축 해제되었습니다", + "extractFailed": "압축 해제 실패", + "compressFile": "파일 압축", + "compressFiles": "파일 압축", + "compressFilesDesc": "{{count}}개 항목을 압축 파일로 압축", + "archiveName": "압축 파일 이름", + "enterArchiveName": "압축 파일 이름 입력...", + "compressionFormat": "압축 형식", + "selectedFiles": "선택된 파일", + "andMoreFiles": "및 {{count}}개 더...", + "compress": "압축", + "compressingFiles": "{{count}}개 항목을 {{name}}(으)로 압축 중...", + "filesCompressedSuccessfully": "{{name}}이(가) 성공적으로 생성되었습니다", + "compressFailed": "압축 실패", + "edit": "편집", + "preview": "미리보기", + "previous": "이전", + "next": "다음", + "pageXOfY": "{{current}} / {{total}} 페이지", + "zoomOut": "축소", + "zoomIn": "확대", + "newFile": "새 파일", + "newFolder": "새 폴더", + "rename": "이름 변경", + "renameItem": "항목 이름 변경", + "deleteItem": "항목 삭제", + "currentPath": "현재 경로", + "uploadFileTitle": "파일 업로드", + "maxFileSize": "최대: 1GB (JSON) / 5GB (바이너리) - 대용량 파일 지원", + "removeFile": "파일 제거", + "clickToSelectFile": "클릭하여 파일 선택", + "chooseFile": "파일 선택", + "uploading": "업로드 중...", + "downloading": "다운로드 중...", + "uploadingFile": "{{name}} 업로드 중...", + "uploadingLargeFile": "대용량 파일 {{name}} ({{size}}) 업로드 중...", + "downloadingFile": "{{name}} 다운로드 중...", + "creatingFile": "{{name}} 생성 중...", + "creatingFolder": "{{name}} 생성 중...", + "deletingItem": "{{type}} {{name}} 삭제 중...", + "renamingItem": "{{type}} {{oldName}}을(를) {{newName}}(으)로 이름 변경 중...", + "createNewFile": "새 파일 생성", + "fileName": "파일 이름", + "creating": "생성 중...", + "createFile": "파일 생성", + "createNewFolder": "새 폴더 생성", + "folderName": "폴더 이름", + "createFolder": "폴더 생성", + "warningCannotUndo": "경고: 이 작업은 취소할 수 없습니다", + "itemPath": "항목 경로", + "thisIsDirectory": "디렉터리입니다 (재귀적으로 삭제됩니다)", + "deleting": "삭제 중...", + "currentPathLabel": "현재 경로", + "newName": "새 이름", + "thisIsDirectoryRename": "디렉터리입니다", + "renaming": "이름 변경 중...", + "fileUploadedSuccessfully": "파일 \"{{name}}\"이(가) 성공적으로 업로드되었습니다", + "failedToUploadFile": "파일 업로드 실패", + "fileDownloadedSuccessfully": "파일 \"{{name}}\"이(가) 성공적으로 다운로드되었습니다", + "failedToDownloadFile": "파일 다운로드 실패", + "noFileContent": "파일 내용이 수신되지 않았습니다", + "filePath": "파일 경로", + "fileCreatedSuccessfully": "파일 \"{{name}}\"이(가) 성공적으로 생성되었습니다", + "failedToCreateFile": "파일 생성 실패", + "folderCreatedSuccessfully": "폴더 \"{{name}}\"이(가) 성공적으로 생성되었습니다", + "failedToCreateFolder": "폴더 생성 실패", + "failedToCreateItem": "항목 생성 실패", + "operationFailed": "{{name}}에 대한 {{operation}} 작업 실패: {{error}}", + "failedToResolveSymlink": "심볼릭 링크 해석 실패", + "itemDeletedSuccessfully": "{{type}}이(가) 성공적으로 삭제되었습니다", + "itemsDeletedSuccessfully": "{{count}}개 항목이 성공적으로 삭제되었습니다", + "failedToDeleteItems": "항목 삭제 실패", + "dragFilesToUpload": "업로드할 파일을 여기에 드롭하세요", + "emptyFolder": "이 폴더는 비어 있습니다", + "itemCount": "{{count}}개 항목", + "selectedCount": "{{count}}개 선택됨", + "searchFiles": "파일 검색...", + "upload": "업로드", + "selectHostToStart": "파일 관리를 시작하려면 호스트를 선택하세요", + "failedToConnect": "SSH 연결 실패", + "failedToLoadDirectory": "디렉터리 로드 실패", + "noSSHConnection": "사용 가능한 SSH 연결이 없습니다", + "enterFolderName": "폴더 이름 입력:", + "enterFileName": "파일 이름 입력:", + "copy": "복사", + "cut": "잘라내기", + "paste": "붙여넣기", + "copyPath": "경로 복사", + "copyPaths": "경로 복사", + "delete": "삭제", + "properties": "속성", + "preview": "미리보기", + "refresh": "새로 고침", + "downloadFiles": "{{count}}개 파일을 브라우저로 다운로드", + "copyFiles": "{{count}}개 항목 복사", + "cutFiles": "{{count}}개 항목 잘라내기", + "deleteFiles": "{{count}}개 항목 삭제", + "filesCopiedToClipboard": "{{count}}개 항목이 클립보드에 복사되었습니다", + "filesCutToClipboard": "{{count}}개 항목이 클립보드에 잘라내기되었습니다", + "pathCopiedToClipboard": "경로가 클립보드에 복사되었습니다", + "pathsCopiedToClipboard": "{{count}}개 경로가 클립보드에 복사되었습니다", + "failedToCopyPath": "경로를 클립보드에 복사하는 데 실패했습니다", + "movedItems": "{{count}}개 항목이 이동되었습니다", + "failedToDeleteItem": "항목 삭제 실패", + "itemRenamedSuccessfully": "{{type}}의 이름이 성공적으로 변경되었습니다", + "failedToRenameItem": "항목 이름 변경 실패", + "upload": "업로드", + "download": "다운로드", + "newFile": "새 파일", + "newFolder": "새 폴더", + "rename": "이름 변경", + "delete": "삭제", + "permissions": "권한", + "size": "크기", + "modified": "수정됨", + "path": "경로", + "fileName": "파일 이름", + "folderName": "폴더 이름", + "confirmDelete": "{{name}}을(를) 삭제하시겠습니까?", + "uploadSuccess": "파일이 성공적으로 업로드되었습니다", + "uploadFailed": "파일 업로드 실패", + "downloadSuccess": "파일이 성공적으로 다운로드되었습니다", + "downloadFailed": "파일 다운로드 실패", + "permissionDenied": "권한이 거부되었습니다", + "checkDockerLogs": "자세한 오류 정보는 Docker 로그를 확인하세요", + "internalServerError": "내부 서버 오류가 발생했습니다", + "serverError": "서버 오류", + "error": "오류", + "requestFailed": "요청 실패, 상태 코드", + "unknownFileError": "알 수 없음", + "cannotReadFile": "파일을 읽을 수 없습니다", + "noSshSessionId": "사용 가능한 SSH 세션 ID가 없습니다", + "noFilePath": "사용 가능한 파일 경로가 없습니다", + "noCurrentHost": "사용 가능한 현재 호스트가 없습니다", + "fileSavedSuccessfully": "파일이 성공적으로 저장되었습니다", + "saveTimeout": "저장 작업 시간 초과. 파일이 성공적으로 저장되었을 수 있지만 작업 완료에 시간이 너무 오래 걸렸습니다. 확인을 위해 Docker 로그를 확인하세요.", + "failedToSaveFile": "파일 저장 실패", + "folder": "폴더", + "file": "파일", + "deletedSuccessfully": "성공적으로 삭제되었습니다", + "failedToDeleteItem": "항목 삭제 실패", + "connectToServer": "서버에 연결", + "selectServerToEdit": "사이드바에서 서버를 선택하여 파일 편집 시작", + "fileOperations": "파일 작업", + "confirmDeleteMessage": "{{name}}을(를) 삭제하시겠습니까?", + "confirmDeleteSingleItem": "\"{{name}}\"을(를) 영구적으로 삭제하시겠습니까?", + "confirmDeleteMultipleItems": "{{count}}개 항목을 영구적으로 삭제하시겠습니까?", + "confirmDeleteMultipleItemsWithFolders": "{{count}}개 항목을 영구적으로 삭제하시겠습니까? 폴더와 그 내용이 포함됩니다.", + "confirmDeleteFolder": "폴더 \"{{name}}\"과(와) 모든 내용을 영구적으로 삭제하시겠습니까?", + "deleteDirectoryWarning": "폴더와 모든 내용이 삭제됩니다.", + "actionCannotBeUndone": "이 작업은 취소할 수 없습니다.", + "permanentDeleteWarning": "이 작업은 취소할 수 없습니다. 항목이 서버에서 영구적으로 삭제됩니다.", + "recent": "최근", + "pinned": "고정됨", + "folderShortcuts": "폴더 바로가기", + "noRecentFiles": "최근 파일이 없습니다.", + "noPinnedFiles": "고정된 파일이 없습니다.", + "enterFolderPath": "폴더 경로 입력", + "noShortcuts": "바로가기가 없습니다.", + "searchFilesAndFolders": "파일 및 폴더 검색...", + "noFilesOrFoldersFound": "파일 또는 폴더를 찾을 수 없습니다.", + "failedToConnectSSH": "SSH 연결 실패", + "failedToReconnectSSH": "SSH 세션 재연결 실패", + "failedToListFiles": "파일 목록 가져오기 실패", + "fetchHomeDataTimeout": "홈 데이터 가져오기 시간 초과", + "sshStatusCheckTimeout": "SSH 상태 확인 시간 초과", + "sshReconnectionTimeout": "SSH 재연결 시간 초과", + "saveOperationTimeout": "저장 작업 시간 초과", + "cannotSaveFile": "파일을 저장할 수 없습니다", + "dragSystemFilesToUpload": "시스템 파일을 여기로 드래그하여 업로드", + "dragFilesToWindowToDownload": "파일을 창 밖으로 드래그하여 다운로드", + "openTerminalHere": "여기서 터미널 열기", + "run": "실행", + "saveToSystem": "다른 이름으로 저장...", + "selectLocationToSave": "저장 위치 선택", + "openTerminalInFolder": "이 폴더에서 터미널 열기", + "openTerminalInFileLocation": "파일 위치에서 터미널 열기", + "terminalWithPath": "터미널 - {{host}}:{{path}}", + "runningFile": "실행 중 - {{file}}", + "onlyRunExecutableFiles": "실행 가능한 파일만 실행할 수 있습니다", + "noHostSelected": "선택된 호스트 없음", + "starred": "즐겨찾기", + "shortcuts": "바로가기", + "directories": "디렉터리", + "removedFromRecentFiles": "최근 파일에서 \"{{name}}\"이(가) 제거되었습니다", + "removeFailed": "제거 실패", + "unpinnedSuccessfully": "\"{{name}}\"의 고정이 성공적으로 해제되었습니다", + "unpinFailed": "고정 해제 실패", + "removedShortcut": "바로가기 \"{{name}}\"이(가) 제거되었습니다", + "removeShortcutFailed": "바로가기 제거 실패", + "clearedAllRecentFiles": "모든 최근 파일이 지워졌습니다", + "clearFailed": "지우기 실패", + "removeFromRecentFiles": "최근 파일에서 제거", + "clearAllRecentFiles": "모든 최근 파일 지우기", + "unpinFile": "파일 고정 해제", + "removeShortcut": "바로가기 제거", + "saveFilesToSystem": "{{count}}개 파일을 다른 이름으로 저장...", + "saveToSystem": "다른 이름으로 저장...", + "pinFile": "파일 고정", + "addToShortcuts": "바로가기에 추가", + "selectLocationToSave": "저장 위치 선택", + "downloadToDefaultLocation": "기본 위치로 다운로드", + "pasteFailed": "붙여넣기 실패", + "noUndoableActions": "실행 취소할 작업이 없습니다", + "undoCopySuccess": "복사 작업 실행 취소: {{count}}개 복사된 파일 삭제됨", + "undoCopyFailedDelete": "실행 취소 실패: 복사된 파일을 삭제할 수 없습니다", + "undoCopyFailedNoInfo": "실행 취소 실패: 복사된 파일 정보를 찾을 수 없습니다", + "undoMoveSuccess": "이동 작업 실행 취소: {{count}}개 파일을 원래 위치로 이동함", + "undoMoveFailedMove": "실행 취소 실패: 파일을 되돌릴 수 없습니다", + "undoMoveFailedNoInfo": "실행 취소 실패: 이동된 파일 정보를 찾을 수 없습니다", + "undoDeleteNotSupported": "삭제 작업은 실행 취소할 수 없습니다: 파일이 서버에서 영구적으로 삭제되었습니다", + "undoTypeNotSupported": "지원되지 않는 실행 취소 작업 유형", + "undoOperationFailed": "실행 취소 작업 실패", + "unknownError": "알 수 없는 오류", + "enterPath": "경로 입력...", + "editPath": "경로 편집", + "confirm": "확인", + "cancel": "취소", + "folderName": "폴더 이름", + "find": "찾기...", + "replaceWith": "바꿀 내용...", + "replace": "바꾸기", + "replaceAll": "모두 바꾸기", + "downloadInstead": "대신 다운로드", + "keyboardShortcuts": "키보드 단축키", + "searchAndReplace": "검색 및 바꾸기", + "editing": "편집", + "navigation": "탐색", + "code": "코드", + "search": "검색", + "findNext": "다음 찾기", + "findPrevious": "이전 찾기", + "save": "저장", + "selectAll": "모두 선택", + "undo": "실행 취소", + "redo": "다시 실행", + "goToLine": "줄로 이동", + "moveLineUp": "줄 위로 이동", + "moveLineDown": "줄 아래로 이동", + "toggleComment": "주석 토글", + "indent": "들여쓰기", + "outdent": "내어쓰기", + "autoComplete": "자동 완성", + "imageLoadError": "이미지 로드 실패", + "zoomIn": "확대", + "zoomOut": "축소", + "rotate": "회전", + "originalSize": "원본 크기", + "startTyping": "입력 시작...", + "unknownSize": "알 수 없는 크기", + "fileIsEmpty": "파일이 비어 있습니다", + "modified": "수정됨", + "largeFileWarning": "대용량 파일 경고", + "largeFileWarningDesc": "이 파일의 크기는 {{size}}이며, 텍스트로 열 때 성능 문제가 발생할 수 있습니다.", + "fileNotFoundAndRemoved": "파일 \"{{name}}\"을(를) 찾을 수 없어 최근/고정 파일에서 제거되었습니다", + "failedToLoadFile": "파일 로드 실패: {{error}}", + "serverErrorOccurred": "서버 오류가 발생했습니다. 나중에 다시 시도하세요.", + "fileSavedSuccessfully": "파일이 성공적으로 저장되었습니다", + "autoSaveFailed": "자동 저장 실패", + "fileAutoSaved": "파일 자동 저장됨", + "fileDownloadedSuccessfully": "파일이 성공적으로 다운로드되었습니다", + "moveFileFailed": "{{name}} 이동 실패", + "moveOperationFailed": "이동 작업 실패", + "canOnlyCompareFiles": "두 개의 파일만 비교할 수 있습니다", + "comparingFiles": "파일 비교 중: {{file1}}과(와) {{file2}}", + "dragFailed": "드래그 작업 실패", + "filePinnedSuccessfully": "파일 \"{{name}}\"이(가) 성공적으로 고정되었습니다", + "pinFileFailed": "파일 고정 실패", + "fileUnpinnedSuccessfully": "파일 \"{{name}}\"의 고정이 성공적으로 해제되었습니다", + "unpinFileFailed": "파일 고정 해제 실패", + "shortcutAddedSuccessfully": "폴더 바로가기 \"{{name}}\"이(가) 성공적으로 추가되었습니다", + "addShortcutFailed": "바로가기 추가 실패", + "operationCompletedSuccessfully": "{{count}}개 항목 {{operation}} 성공", + "operationCompleted": "{{count}}개 항목 {{operation}}", + "downloadFileSuccess": "파일 {{name}}이(가) 성공적으로 다운로드되었습니다", + "downloadFileFailed": "다운로드 실패", + "moveTo": "{{name}}(으)로 이동", + "diffCompareWith": "{{name}}과(와) 차이 비교", + "dragOutsideToDownload": "창 밖으로 드래그하여 다운로드 ({{count}}개 파일)", + "newFolderDefault": "NewFolder", + "newFileDefault": "NewFile.txt", + "successfullyMovedItems": "{{count}}개 항목을 {{target}}(으)로 성공적으로 이동했습니다", + "move": "이동", + "searchInFile": "파일에서 검색 (Ctrl+F)", + "showKeyboardShortcuts": "키보드 단축키 표시", + "startWritingMarkdown": "마크다운 콘텐츠 작성 시작...", + "loadingFileComparison": "파일 비교 로드 중...", + "reload": "다시 로드", + "compare": "비교", + "sideBySide": "나란히", + "inline": "인라인", + "fileComparison": "파일 비교: {{file1}} vs {{file2}}", + "fileTooLarge": "파일이 너무 큽니다: {{error}}", + "sshConnectionFailed": "SSH 연결 실패. {{name}} ({{ip}}:{{port}})에 대한 연결을 확인하세요", + "loadFileFailed": "파일 로드 실패: {{error}}", + "connectedSuccessfully": "성공적으로 연결되었습니다", + "totpVerificationFailed": "TOTP 확인 실패", + "changePermissions": "권한 변경", + "changePermissionsDesc": "파일 권한 수정", + "currentPermissions": "현재 권한", + "newPermissions": "새 권한", + "owner": "소유자", + "group": "그룹", + "others": "기타", + "read": "읽기", + "write": "쓰기", + "execute": "실행", + "permissionsChangedSuccessfully": "권한이 성공적으로 변경되었습니다", + "failedToChangePermissions": "권한 변경 실패" + }, + "tunnels": { + "title": "SSH 터널", + "noSshTunnels": "SSH 터널 없음", + "createFirstTunnelMessage": "아직 SSH 터널을 생성하지 않았습니다. 시작하려면 호스트 관리자에서 터널 연결을 구성하세요.", + "connected": "연결됨", + "disconnected": "연결 해제됨", + "connecting": "연결 중...", + "disconnecting": "연결 해제 중...", + "unknownTunnelStatus": "알 수 없음", + "unknown": "알 수 없음", + "error": "오류", + "failed": "실패", + "retrying": "재시도 중", + "waiting": "대기 중", + "waitingForRetry": "재시도 대기 중", + "retryingConnection": "연결 재시도 중", + "canceling": "취소 중...", + "connect": "연결", + "disconnect": "연결 해제", + "cancel": "취소", + "port": "포트", + "attempt": "시도 {{current}}/{{max}}", + "nextRetryIn": "{{seconds}}초 후 재시도", + "checkDockerLogs": "오류 원인은 Docker 로그를 확인하거나", + "noTunnelConnections": "구성된 터널 연결 없음", + "tunnelConnections": "터널 연결", + "addTunnel": "터널 추가", + "editTunnel": "터널 편집", + "deleteTunnel": "터널 삭제", + "tunnelName": "터널 이름", + "localPort": "로컬 포트", + "remoteHost": "원격 호스트", + "remotePort": "원격 포트", + "autoStart": "자동 시작", + "status": "상태", + "active": "활성", + "inactive": "비활성", + "start": "시작", + "stop": "중지", + "restart": "재시작", + "connectionType": "연결 유형", + "local": "로컬", + "remote": "원격", + "dynamic": "동적", + "noSshTunnels": "SSH 터널 없음", + "createFirstTunnelMessage": "첫 번째 SSH 터널을 생성하여 시작하세요. SSH 관리자를 사용하여 터널 연결이 있는 호스트를 추가하세요.", + "unknownConnectionStatus": "알 수 없음", + "connected": "연결됨", + "connecting": "연결 중...", + "disconnecting": "연결 해제 중...", + "disconnected": "연결 해제됨", + "portMapping": "포트 {{sourcePort}} → {{endpointHost}}:{{endpointPort}}", + "disconnect": "연결 해제", + "connect": "연결", + "canceling": "취소 중...", + "endpointHostNotFound": "엔드포인트 호스트를 찾을 수 없습니다", + "discord": "Discord", + "githubIssue": "GitHub 이슈", + "forHelp": "에서 도움을 받으세요" + }, + "serverStats": { + "title": "서버 통계", + "cpu": "CPU", + "memory": "메모리", + "disk": "디스크", + "network": "네트워크", + "uptime": "가동 시간", + "loadAverage": "평균 부하", + "processes": "프로세스", + "connections": "연결", + "usage": "사용량", + "available": "사용 가능", + "total": "전체", + "free": "여유", + "used": "사용됨", + "percentage": "백분율", + "refreshStatusAndMetrics": "상태 및 메트릭 새로 고침", + "refreshStatus": "상태 새로 고침", + "fileManagerAlreadyOpen": "이 호스트에 대한 파일 관리자가 이미 열려 있습니다", + "openFileManager": "파일 관리자 열기", + "cpuCores_one": "{{count}}개 CPU", + "cpuCores_other": "{{count}}개 CPU", + "naCpus": "N/A CPU", + "loadAverage": "평균: {{avg1}}, {{avg5}}, {{avg15}}", + "loadAverageNA": "평균: N/A", + "cpuUsage": "CPU 사용량", + "memoryUsage": "메모리 사용량", + "diskUsage": "디스크 사용량", + "rootStorageSpace": "루트 저장 공간", + "of": "/", + "feedbackMessage": "서버 관리를 위해 다음에 추가되어야 할 아이디어가 있으신가요? 공유하세요", + "failedToFetchHostConfig": "호스트 구성 가져오기 실패", + "failedToFetchStatus": "서버 상태 가져오기 실패", + "failedToFetchMetrics": "서버 메트릭 가져오기 실패", + "failedToFetchHomeData": "홈 데이터 가져오기 실패", + "loadingMetrics": "메트릭 로드 중...", + "refreshing": "새로 고침 중...", + "serverOffline": "서버 오프라인", + "cannotFetchMetrics": "오프라인 서버에서 메트릭을 가져올 수 없습니다", + "totpRequired": "TOTP 인증 필요", + "totpUnavailable": "TOTP가 활성화된 서버에서는 서버 통계를 사용할 수 없습니다", + "load": "부하", + "available": "사용 가능", + "editLayout": "레이아웃 편집", + "cancelEdit": "취소", + "addWidget": "위젯 추가", + "saveLayout": "레이아웃 저장", + "unsavedChanges": "저장되지 않은 변경사항", + "layoutSaved": "레이아웃이 성공적으로 저장되었습니다", + "failedToSaveLayout": "레이아웃 저장 실패", + "systemInfo": "시스템 정보", + "hostname": "호스트 이름", + "operatingSystem": "운영 체제", + "kernel": "커널", + "totalUptime": "총 가동 시간", + "seconds": "초", + "networkInterfaces": "네트워크 인터페이스", + "noInterfacesFound": "네트워크 인터페이스를 찾을 수 없습니다", + "totalProcesses": "총 프로세스", + "running": "실행 중", + "noProcessesFound": "프로세스를 찾을 수 없습니다", + "loginStats": "SSH 로그인 통계", + "totalLogins": "총 로그인", + "uniqueIPs": "고유 IP", + "recentSuccessfulLogins": "최근 성공한 로그인", + "recentFailedAttempts": "최근 실패한 시도", + "noRecentLoginData": "최근 로그인 데이터 없음", + "from": "출처", + "quickActions": "빠른 작업", + "executeQuickAction": "{{name}} 실행", + "executingQuickAction": "{{name}} 실행 중...", + "quickActionSuccess": "{{name}}이(가) 성공적으로 완료되었습니다", + "quickActionFailed": "{{name}} 실패", + "quickActionError": "{{name}} 실행 실패" + }, + "auth": { + "tagline": "SSH 서버 관리자", + "description": "안전하고 강력하며 직관적인 SSH 연결 관리", + "welcomeBack": "TERMIX에 다시 오신 것을 환영합니다", + "createAccount": "TERMIX 계정 만들기", + "continueExternal": "외부 공급자로 계속", + "loginTitle": "Termix 로그인", + "registerTitle": "계정 만들기", + "loginButton": "로그인", + "registerButton": "등록", + "forgotPassword": "비밀번호를 잊으셨나요?", + "rememberMe": "로그인 상태 유지", + "noAccount": "계정이 없으신가요?", + "hasAccount": "이미 계정이 있으신가요?", + "loginSuccess": "로그인 성공", + "loginFailed": "로그인 실패", + "registerSuccess": "등록 성공", + "registerFailed": "등록 실패", + "logoutSuccess": "성공적으로 로그아웃되었습니다", + "invalidCredentials": "잘못된 사용자 이름 또는 비밀번호", + "accountCreated": "계정이 성공적으로 생성되었습니다", + "passwordReset": "비밀번호 재설정 링크가 전송되었습니다", + "twoFactorAuth": "2단계 인증", + "enterCode": "인증 코드 입력", + "backupCode": "또는 백업 코드 사용", + "verifyCode": "코드 확인", + "redirectingToApp": "앱으로 리디렉션 중...", + "enableTwoFactor": "2단계 인증 활성화", + "disableTwoFactor": "2단계 인증 비활성화", + "scanQRCode": "인증 앱으로 이 QR 코드를 스캔하세요", + "backupCodes": "백업 코드", + "saveBackupCodes": "이 백업 코드를 안전한 곳에 저장하세요", + "twoFactorEnabledSuccess": "2단계 인증이 성공적으로 활성화되었습니다!", + "twoFactorDisabled": "2단계 인증이 비활성화되었습니다", + "newBackupCodesGenerated": "새 백업 코드가 생성되었습니다", + "backupCodesDownloaded": "백업 코드가 다운로드되었습니다", + "pleaseEnterSixDigitCode": "6자리 코드를 입력하세요", + "invalidVerificationCode": "잘못된 인증 코드", + "failedToDisableTotp": "TOTP 비활성화 실패", + "failedToGenerateBackupCodes": "백업 코드 생성 실패", + "enterPassword": "비밀번호 입력", + "lockedOidcAuth": "잠김 (OIDC 인증)", + "twoFactorTitle": "2단계 인증", + "twoFactorProtected": "귀하의 계정은 2단계 인증으로 보호됩니다", + "twoFactorActive": "2단계 인증이 현재 계정에서 활성화되어 있습니다", + "disable2FA": "2FA 비활성화", + "disableTwoFactorWarning": "2단계 인증을 비활성화하면 계정 보안이 약화됩니다", + "passwordOrTotpCode": "비밀번호 또는 TOTP 코드", + "or": "또는", + "generateNewBackupCodesText": "기존 백업 코드를 분실한 경우 새 백업 코드를 생성하세요", + "generateNewBackupCodes": "새 백업 코드 생성", + "yourBackupCodes": "백업 코드", + "download": "다운로드", + "setupTwoFactorTitle": "2단계 인증 설정", + "sshAuthenticationRequired": "SSH 인증 필요", + "sshNoKeyboardInteractive": "키보드 대화형 인증 사용 불가", + "sshAuthenticationFailed": "인증 실패", + "sshAuthenticationTimeout": "인증 시간 초과", + "sshNoKeyboardInteractiveDescription": "서버가 키보드 대화형 인증을 지원하지 않습니다. 비밀번호 또는 SSH 키를 제공하세요.", + "sshAuthFailedDescription": "제공된 자격 증명이 올바르지 않습니다. 유효한 자격 증명으로 다시 시도하세요.", + "sshTimeoutDescription": "인증 시도가 시간 초과되었습니다. 다시 시도하세요.", + "sshProvideCredentialsDescription": "이 서버에 연결하려면 SSH 자격 증명을 제공하세요.", + "sshPasswordDescription": "이 SSH 연결의 비밀번호를 입력하세요.", + "sshKeyPasswordDescription": "SSH 키가 암호화된 경우 여기에 암호를 입력하세요.", + "step1ScanQR": "1단계: 인증 앱으로 QR 코드 스캔", + "manualEntryCode": "수동 입력 코드", + "cannotScanQRText": "QR 코드를 스캔할 수 없는 경우 인증 앱에 이 코드를 수동으로 입력하세요", + "nextVerifyCode": "다음: 코드 확인", + "verifyAuthenticator": "인증 앱 확인", + "step2EnterCode": "2단계: 인증 앱에서 6자리 코드 입력", + "verificationCode": "인증 코드", + "back": "뒤로", + "verifyAndEnable": "확인 및 활성화", + "saveBackupCodesTitle": "백업 코드 저장", + "step3StoreCodesSecurely": "3단계: 이 코드를 안전한 곳에 저장", + "importantBackupCodesText": "이 백업 코드를 안전한 위치에 저장하세요. 인증 장치를 분실한 경우 이 코드를 사용하여 계정에 액세스할 수 있습니다.", + "completeSetup": "설정 완료", + "notEnabledText": "2단계 인증은 로그인 시 인증 앱의 코드를 요구하여 추가 보안 계층을 제공합니다.", + "enableTwoFactorButton": "2단계 인증 활성화", + "addExtraSecurityLayer": "계정에 추가 보안 계층 추가", + "firstUser": "첫 번째 사용자", + "firstUserMessage": "귀하는 첫 번째 사용자이며 관리자로 지정됩니다. 사이드바 사용자 드롭다운에서 관리자 설정을 볼 수 있습니다. 이것이 실수라고 생각되면 Docker 로그를 확인하거나 GitHub 이슈를 생성하세요.", + "external": "외부", + "loginWithExternal": "외부 공급자로 로그인", + "loginWithExternalDesc": "구성된 외부 ID 공급자를 사용하여 로그인", + "externalNotSupportedInElectron": "외부 인증은 아직 Electron 앱에서 지원되지 않습니다. OIDC 로그인을 위해 웹 버전을 사용하세요.", + "resetPasswordButton": "비밀번호 재설정", + "sendResetCode": "재설정 코드 전송", + "resetCodeDesc": "사용자 이름을 입력하여 비밀번호 재설정 코드를 받으세요. 코드는 Docker 컨테이너 로그에 기록됩니다.", + "resetCode": "재설정 코드", + "verifyCodeButton": "코드 확인", + "enterResetCode": "사용자에 대한 Docker 컨테이너 로그에서 6자리 코드 입력:", + "goToLogin": "로그인으로 이동", + "newPassword": "새 비밀번호", + "confirmNewPassword": "비밀번호 확인", + "enterNewPassword": "사용자에 대한 새 비밀번호 입력:", + "signUp": "가입", + "mobileApp": "모바일 앱", + "loggingInToMobileApp": "모바일 앱에 로그인 중", + "desktopApp": "데스크톱 앱", + "loggingInToDesktopApp": "데스크톱 앱에 로그인 중", + "loggingInToDesktopAppViaWeb": "웹 인터페이스를 통해 데스크톱 앱에 로그인 중", + "loadingServer": "서버 로드 중...", + "authenticating": "인증 중...", + "dataLossWarning": "이 방법으로 비밀번호를 재설정하면 저장된 모든 SSH 호스트, 자격 증명 및 기타 암호화된 데이터가 삭제됩니다. 이 작업은 취소할 수 없습니다. 비밀번호를 잊어버렸고 로그인되어 있지 않은 경우에만 사용하세요.", + "authenticationDisabled": "인증 비활성화됨", + "authenticationDisabledDesc": "현재 모든 인증 방법이 비활성화되어 있습니다. 관리자에게 문의하세요." + }, + "errors": { + "notFound": "페이지를 찾을 수 없습니다", + "unauthorized": "권한 없는 액세스", + "forbidden": "액세스 금지", + "serverError": "서버 오류", + "networkError": "네트워크 오류", + "databaseConnection": "데이터베이스에 연결할 수 없습니다", + "unknownError": "알 수 없는 오류", + "loginFailed": "로그인 실패", + "failedPasswordReset": "비밀번호 재설정 시작 실패", + "failedVerifyCode": "재설정 코드 확인 실패", + "failedCompleteReset": "비밀번호 재설정 완료 실패", + "invalidTotpCode": "잘못된 TOTP 코드", + "failedOidcLogin": "OIDC 로그인 시작 실패", + "failedUserInfo": "OIDC 로그인 후 사용자 정보 가져오기 실패", + "oidcAuthFailed": "OIDC 인증 실패", + "noTokenReceived": "로그인에서 토큰을 받지 못했습니다", + "invalidAuthUrl": "백엔드에서 잘못된 인증 URL을 받았습니다", + "invalidInput": "잘못된 입력", + "requiredField": "이 필드는 필수입니다", + "minLength": "최소 길이는 {{min}}입니다", + "maxLength": "최대 길이는 {{max}}입니다", + "invalidEmail": "잘못된 이메일 주소", + "passwordMismatch": "비밀번호가 일치하지 않습니다", + "passwordLoginDisabled": "사용자 이름/비밀번호 로그인이 현재 비활성화되어 있습니다", + "weakPassword": "비밀번호가 너무 약합니다", + "usernameExists": "사용자 이름이 이미 존재합니다", + "emailExists": "이메일이 이미 존재합니다", + "loadFailed": "데이터 로드 실패", + "saveError": "저장 실패", + "sessionExpired": "세션이 만료되었습니다 - 다시 로그인하세요" + }, + "messages": { + "saveSuccess": "성공적으로 저장되었습니다", + "saveError": "저장 실패", + "deleteSuccess": "성공적으로 삭제되었습니다", + "deleteError": "삭제 실패", + "updateSuccess": "성공적으로 업데이트되었습니다", + "updateError": "업데이트 실패", + "copySuccess": "클립보드에 복사되었습니다", + "copyError": "복사 실패", + "copiedToClipboard": "{{item}}이(가) 클립보드에 복사되었습니다", + "connectionEstablished": "연결이 설정되었습니다", + "connectionClosed": "연결이 닫혔습니다", + "reconnecting": "재연결 중...", + "processing": "처리 중...", + "pleaseWait": "잠시 기다려주세요...", + "registrationDisabled": "새 계정 등록이 현재 관리자에 의해 비활성화되어 있습니다. 로그인하거나 관리자에게 문의하세요.", + "databaseConnected": "데이터베이스가 성공적으로 연결되었습니다", + "databaseConnectionFailed": "데이터베이스 서버에 연결하지 못했습니다", + "checkServerConnection": "서버 연결을 확인하고 다시 시도하세요", + "resetCodeSent": "재설정 코드가 Docker 로그로 전송되었습니다", + "codeVerified": "코드가 성공적으로 확인되었습니다", + "passwordResetSuccess": "비밀번호가 성공적으로 재설정되었습니다", + "loginSuccess": "로그인 성공", + "registrationSuccess": "등록 성공" + }, + "profile": { + "title": "사용자 프로필", + "description": "계정 설정 및 보안 관리", + "security": "보안", + "changePassword": "비밀번호 변경", + "twoFactorAuth": "2단계 인증", + "accountInfo": "계정 정보", + "role": "역할", + "admin": "관리자", + "user": "사용자", + "authMethod": "인증 방법", + "local": "로컬", + "external": "외부 (OIDC)", + "externalAndLocal": "이중 인증", + "selectPreferredLanguage": "인터페이스에 대한 선호 언어 선택", + "fileColorCoding": "파일 색상 코딩", + "fileColorCodingDesc": "유형별로 파일을 색상으로 구분: 폴더(빨강), 파일(파랑), 심볼릭 링크(초록)", + "commandAutocomplete": "명령 자동 완성", + "commandAutocompleteDesc": "명령 기록을 기반으로 터미널 명령에 대한 Tab 키 자동 완성 제안 활성화", + "currentPassword": "현재 비밀번호", + "passwordChangedSuccess": "비밀번호가 성공적으로 변경되었습니다! 다시 로그인하세요.", + "failedToChangePassword": "비밀번호 변경 실패. 현재 비밀번호를 확인하고 다시 시도하세요." + }, + "user": { + "failedToLoadVersionInfo": "버전 정보 로드 실패" + }, + "placeholders": { + "enterCode": "000000", + "ipAddress": "127.0.0.1", + "port": "22", + "maxRetries": "3", + "retryInterval": "10", + "language": "언어", + "username": "사용자 이름", + "hostname": "호스트 이름", + "folder": "폴더", + "password": "비밀번호", + "keyPassword": "키 비밀번호", + "pastePrivateKey": "개인 키를 여기에 붙여넣으세요...", + "pastePublicKey": "공개 키를 여기에 붙여넣으세요...", + "credentialName": "내 SSH 서버", + "description": "SSH 자격 증명 설명", + "searchCredentials": "이름, 사용자 이름 또는 태그로 자격 증명 검색...", + "sshConfig": "엔드포인트 ssh 구성", + "homePath": "/home", + "clientId": "your-client-id", + "clientSecret": "your-client-secret", + "authUrl": "https://your-provider.com/application/o/authorize/", + "redirectUrl": "https://your-provider.com/application/o/termix/", + "tokenUrl": "https://your-provider.com/application/o/token/", + "userIdField": "sub", + "usernameField": "name", + "scopes": "openid email profile", + "userinfoUrl": "https://your-provider.com/application/o/userinfo/", + "enterUsername": "관리자로 지정할 사용자 이름 입력", + "searchHosts": "이름, 사용자 이름, IP, 폴더, 태그로 호스트 검색...", + "enterPassword": "비밀번호 입력", + "totpCode": "6자리 TOTP 코드", + "searchHostsAny": "모든 정보로 호스트 검색...", + "confirmPassword": "확인을 위해 비밀번호 입력", + "typeHere": "여기에 입력", + "fileName": "파일 이름 입력 (예: example.txt)", + "folderName": "폴더 이름 입력", + "fullPath": "항목의 전체 경로 입력", + "currentPath": "항목의 현재 경로 입력", + "newName": "새 이름 입력" + }, + "leftSidebar": { + "failedToLoadHosts": "호스트 로드 실패", + "noFolder": "폴더 없음", + "passwordRequired": "비밀번호가 필요합니다", + "failedToDeleteAccount": "계정 삭제 실패", + "failedToMakeUserAdmin": "사용자를 관리자로 지정하는 데 실패했습니다", + "userIsNowAdmin": "사용자 {{username}}이(가) 이제 관리자입니다", + "removeAdminConfirm": "{{username}}의 관리자 상태를 제거하시겠습니까?", + "deleteUserConfirm": "사용자 {{username}}을(를) 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", + "deleteAccount": "계정 삭제", + "closeDeleteAccount": "계정 삭제 닫기", + "deleteAccountWarning": "이 작업은 취소할 수 없습니다. 계정과 모든 관련 데이터가 영구적으로 삭제됩니다.", + "deleteAccountWarningDetails": "계정을 삭제하면 SSH 호스트, 구성 및 설정을 포함한 모든 데이터가 제거됩니다. 이 작업은 되돌릴 수 없습니다.", + "deleteAccountWarningShort": "이 작업은 되돌릴 수 없으며 계정이 영구적으로 삭제됩니다.", + "cannotDeleteAccount": "계정을 삭제할 수 없습니다", + "lastAdminWarning": "귀하는 마지막 관리자 사용자입니다. 시스템에 관리자가 없게 되므로 계정을 삭제할 수 없습니다. 먼저 다른 사용자를 관리자로 지정하거나 시스템 지원에 문의하세요.", + "confirmPassword": "비밀번호 확인", + "deleting": "삭제 중...", + "cancel": "취소" + }, + "interface": { + "sidebar": "사이드바", + "toggleSidebar": "사이드바 토글", + "close": "닫기", + "online": "온라인", + "offline": "오프라인", + "maintenance": "유지보수", + "degraded": "저하됨", + "noTunnelConnections": "구성된 터널 연결 없음", + "discord": "Discord", + "connectToSshForOperations": "파일 작업을 사용하려면 SSH에 연결하세요", + "uploadFile": "파일 업로드", + "newFile": "새 파일", + "newFolder": "새 폴더", + "rename": "이름 변경", + "deleteItem": "항목 삭제", + "createNewFile": "새 파일 생성", + "createNewFolder": "새 폴더 생성", + "deleteItem": "항목 삭제", + "renameItem": "항목 이름 변경", + "clickToSelectFile": "클릭하여 파일 선택", + "noSshHosts": "SSH 호스트 없음", + "sshHosts": "SSH 호스트", + "importSshHosts": "JSON에서 SSH 호스트 가져오기", + "clientId": "클라이언트 ID", + "clientSecret": "클라이언트 시크릿", + "error": "오류", + "warning": "경고", + "deleteAccount": "계정 삭제", + "closeDeleteAccount": "계정 삭제 닫기", + "cannotDeleteAccount": "계정을 삭제할 수 없습니다", + "confirmPassword": "비밀번호 확인", + "deleting": "삭제 중...", + "externalAuth": "외부 인증 (OIDC)", + "configureExternalProvider": "외부 ID 공급자 구성", + "waitingForRetry": "재시도 대기 중", + "retryingConnection": "연결 재시도 중", + "resetSplitSizes": "분할 크기 재설정", + "sshManagerAlreadyOpen": "SSH 관리자가 이미 열려 있습니다", + "disabledDuringSplitScreen": "분할 화면 중 비활성화됨", + "unknown": "알 수 없음", + "connected": "연결됨", + "disconnected": "연결 해제됨", + "maxRetriesExhausted": "최대 재시도 횟수 소진", + "endpointHostNotFound": "엔드포인트 호스트를 찾을 수 없습니다", + "administrator": "관리자", + "user": "사용자", + "external": "외부", + "local": "로컬", + "saving": "저장 중...", + "saveConfiguration": "구성 저장", + "loading": "로드 중...", + "refresh": "새로 고침", + "adding": "추가 중...", + "makeAdmin": "관리자로 지정", + "verifying": "확인 중...", + "verifyAndEnable": "확인 및 활성화", + "secretKey": "비밀 키", + "totpQrCode": "TOTP QR 코드", + "passwordRequired": "비밀번호 인증을 사용할 때 비밀번호가 필요합니다", + "sshKeyRequired": "키 인증을 사용할 때 SSH 개인 키가 필요합니다", + "keyTypeRequired": "키 인증을 사용할 때 키 유형이 필요합니다", + "validSshConfigRequired": "목록에서 유효한 SSH 구성을 선택해야 합니다", + "updateHost": "호스트 업데이트", + "addHost": "호스트 추가", + "editHost": "호스트 편집", + "pinConnection": "연결 고정", + "authentication": "인증", + "password": "비밀번호", + "key": "키", + "sshPrivateKey": "SSH 개인 키", + "keyPassword": "키 비밀번호", + "keyType": "키 유형", + "enableTerminal": "터미널 활성화", + "enableTunnel": "터널 활성화", + "enableFileManager": "파일 관리자 활성화", + "defaultPath": "기본 경로", + "tunnelConnections": "터널 연결", + "maxRetries": "최대 재시도 횟수", + "upload": "업로드", + "updateKey": "키 업데이트", + "productionFolder": "프로덕션", + "databaseServer": "데이터베이스 서버", + "developmentServer": "개발 서버", + "developmentFolder": "개발", + "webServerProduction": "웹 서버 - 프로덕션", + "unknownError": "알 수 없는 오류", + "failedToInitiatePasswordReset": "비밀번호 재설정 시작 실패", + "failedToVerifyResetCode": "재설정 코드 확인 실패", + "failedToCompletePasswordReset": "비밀번호 재설정 완료 실패", + "invalidTotpCode": "잘못된 TOTP 코드", + "failedToStartOidcLogin": "OIDC 로그인 시작 실패", + "failedToGetUserInfoAfterOidc": "OIDC 로그인 후 사용자 정보 가져오기 실패", + "loginWithExternalProvider": "외부 공급자로 로그인", + "loginWithExternal": "외부 공급자로 로그인", + "sendResetCode": "재설정 코드 전송", + "verifyCode": "코드 확인", + "resetPassword": "비밀번호 재설정", + "login": "로그인", + "signUp": "가입", + "failedToUpdateOidcConfig": "OIDC 구성 업데이트 실패", + "failedToMakeUserAdmin": "사용자를 관리자로 지정하는 데 실패했습니다", + "failedToStartTotpSetup": "TOTP 설정 시작 실패", + "invalidVerificationCode": "잘못된 인증 코드", + "failedToDisableTotp": "TOTP 비활성화 실패", + "failedToGenerateBackupCodes": "백업 코드 생성 실패" + }, + "mobile": { + "selectHostToStart": "터미널 세션을 시작하려면 호스트를 선택하세요", + "limitedSupportMessage": "웹사이트 모바일 지원은 아직 진행 중입니다. 더 나은 경험을 위해 모바일 앱을 사용하세요.", + "mobileAppInProgress": "모바일 앱 개발 중", + "mobileAppInProgressDesc": "모바일 장치에서 더 나은 경험을 제공하기 위해 전용 모바일 앱을 개발 중입니다.", + "viewMobileAppDocs": "모바일 앱 설치", + "mobileAppDocumentation": "모바일 앱 문서" + }, + "dashboard": { + "title": "대시보드", + "github": "GitHub", + "support": "지원", + "discord": "Discord", + "donate": "기부", + "serverOverview": "서버 개요", + "version": "버전", + "upToDate": "최신 상태", + "updateAvailable": "업데이트 사용 가능", + "uptime": "가동 시간", + "database": "데이터베이스", + "healthy": "정상", + "error": "오류", + "totalServers": "총 서버", + "totalTunnels": "총 터널", + "totalCredentials": "총 자격 증명", + "recentActivity": "최근 활동", + "reset": "재설정", + "loadingRecentActivity": "최근 활동 로드 중...", + "noRecentActivity": "최근 활동 없음", + "quickActions": "빠른 작업", + "addHost": "호스트 추가", + "addCredential": "자격 증명 추가", + "adminSettings": "관리자 설정", + "userProfile": "사용자 프로필", + "serverStats": "서버 통계", + "loadingServerStats": "서버 통계 로드 중...", + "noServerData": "사용 가능한 서버 데이터 없음", + "cpu": "CPU", + "ram": "RAM", + "notAvailable": "N/A" + }, + "commandPalette": { + "searchPlaceholder": "호스트 또는 빠른 작업 검색...", + "recentActivity": "최근 활동", + "navigation": "탐색", + "addHost": "호스트 추가", + "addCredential": "자격 증명 추가", + "adminSettings": "관리자 설정", + "userProfile": "사용자 프로필", + "updateLog": "업데이트 로그", + "hosts": "호스트", + "openServerDetails": "서버 세부 정보 열기", + "openFileManager": "파일 관리자 열기", + "edit": "편집", + "links": "링크", + "github": "GitHub", + "support": "지원", + "discord": "Discord", + "donate": "기부", + "press": "누르기", + "toToggle": "토글하려면", + "close": "닫기", + "hostManager": "호스트 관리자" + } +} \ No newline at end of file diff --git a/src/ui/desktop/user/LanguageSwitcher.tsx b/src/ui/desktop/user/LanguageSwitcher.tsx index 0adc6658..3a9828fc 100644 --- a/src/ui/desktop/user/LanguageSwitcher.tsx +++ b/src/ui/desktop/user/LanguageSwitcher.tsx @@ -20,6 +20,7 @@ const languages = [ }, { code: "ru", name: "Russian", nativeName: "Русский" }, { code: "fr", name: "French", nativeName: "Français" }, + { code: "ko", name: "Korean", nativeName: "한국어" }, ]; export function LanguageSwitcher() { From 69dfebab371ea2e2fa6234414555a0fa7ae444bd Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 14:32:59 -0600 Subject: [PATCH 05/24] feat: Automate flatpak --- .github/workflows/electron.yml | 75 ++++++++++++++++++++++++++ flatpak/com.karmaa.termix.flatpakref | 12 +++++ flatpak/com.karmaa.termix.metainfo.xml | 2 +- flatpak/com.karmaa.termix.yml | 4 +- flatpak/prepare-flatpak.sh | 34 ------------ 5 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 flatpak/com.karmaa.termix.flatpakref delete mode 100644 flatpak/prepare-flatpak.sh diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 027685c9..ca1b3f4e 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -267,6 +267,81 @@ jobs: path: release/termix_linux_armv7l_portable.tar.gz retention-days: 30 + - name: Install Flatpak builder + run: | + sudo apt-get update + sudo apt-get install -y flatpak flatpak-builder + + - name: Add Flathub repository + run: | + sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + + - name: Install Flatpak runtime and SDK + run: | + sudo flatpak install -y flathub org.freedesktop.Platform//23.08 + sudo flatpak install -y flathub org.freedesktop.Sdk//23.08 + sudo flatpak install -y flathub org.electronjs.Electron2.BaseApp//23.08 + + - name: Get version for Flatpak + id: flatpak-version + run: | + VERSION=$(node -p "require('./package.json').version") + RELEASE_DATE=$(date +%Y-%m-%d) + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT + + - name: Prepare Flatpak files + run: | + VERSION="${{ steps.flatpak-version.outputs.version }}" + RELEASE_DATE="${{ steps.flatpak-version.outputs.release_date }}" + + CHECKSUM_X64=$(sha256sum "release/termix_linux_x64_appimage.AppImage" | awk '{print $1}') + CHECKSUM_ARM64=$(sha256sum "release/termix_linux_arm64_appimage.AppImage" | awk '{print $1}') + + mkdir -p flatpak-build + cp flatpak/com.karmaa.termix.yml flatpak-build/ + cp flatpak/com.karmaa.termix.desktop flatpak-build/ + cp flatpak/com.karmaa.termix.metainfo.xml flatpak-build/ + cp public/icon.svg flatpak-build/com.karmaa.termix.svg + convert public/icon.png -resize 256x256 flatpak-build/icon-256.png + convert public/icon.png -resize 128x128 flatpak-build/icon-128.png + + cd flatpak-build + sed -i "s|https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_x64_appimage.AppImage|file://$(realpath ../release/termix_linux_x64_appimage.AppImage)|g" com.karmaa.termix.yml + sed -i "s|https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_arm64_appimage.AppImage|file://$(realpath ../release/termix_linux_arm64_appimage.AppImage)|g" com.karmaa.termix.yml + sed -i "s/CHECKSUM_X64_PLACEHOLDER/$CHECKSUM_X64/g" com.karmaa.termix.yml + sed -i "s/CHECKSUM_ARM64_PLACEHOLDER/$CHECKSUM_ARM64/g" com.karmaa.termix.yml + sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" com.karmaa.termix.metainfo.xml + sed -i "s/DATE_PLACEHOLDER/$RELEASE_DATE/g" com.karmaa.termix.metainfo.xml + + - name: Build Flatpak bundle + run: | + cd flatpak-build + flatpak-builder --repo=repo --force-clean --disable-rofiles-fuse build-dir com.karmaa.termix.yml + flatpak build-bundle repo ../release/termix_linux_flatpak.flatpak com.karmaa.termix stable + + - name: Create flatpakref file + run: | + VERSION="${{ steps.flatpak-version.outputs.version }}" + cp flatpak/com.karmaa.termix.flatpakref release/ + sed -i "s|VERSION_PLACEHOLDER|release-${VERSION}-tag|g" release/com.karmaa.termix.flatpakref + + - name: Upload Flatpak bundle + uses: actions/upload-artifact@v4 + if: hashFiles('release/termix_linux_flatpak.flatpak') != '' && github.event.inputs.artifact_destination != 'none' + with: + name: termix_linux_flatpak + path: release/termix_linux_flatpak.flatpak + retention-days: 30 + + - name: Upload Flatpakref + uses: actions/upload-artifact@v4 + if: hashFiles('release/com.karmaa.termix.flatpakref') != '' && github.event.inputs.artifact_destination != 'none' + with: + name: termix_linux_flatpakref + path: release/com.karmaa.termix.flatpakref + retention-days: 30 + build-macos: runs-on: macos-latest if: github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == 'all' diff --git a/flatpak/com.karmaa.termix.flatpakref b/flatpak/com.karmaa.termix.flatpakref new file mode 100644 index 00000000..7d2e9892 --- /dev/null +++ b/flatpak/com.karmaa.termix.flatpakref @@ -0,0 +1,12 @@ +[Flatpak Ref] +Name=Termix +Branch=stable +Title=Termix - SSH Server Management Platform +IsRuntime=false +Url=https://github.com/Termix-SSH/Termix/releases/download/VERSION_PLACEHOLDER/termix_linux_flatpak.flatpak +GPGKey= +RuntimeRepo=https://flathub.org/repo/flathub.flatpakrepo +Comment=Web-based server management platform with SSH terminal, tunneling, and file editing +Description=Termix is an open-source, forever-free, self-hosted all-in-one server management platform. It provides SSH terminal access, tunneling capabilities, and remote file management. +Icon=https://raw.githubusercontent.com/Termix-SSH/Termix/main/public/icon.png +Homepage=https://github.com/Termix-SSH/Termix diff --git a/flatpak/com.karmaa.termix.metainfo.xml b/flatpak/com.karmaa.termix.metainfo.xml index 0c3c6895..335d902c 100644 --- a/flatpak/com.karmaa.termix.metainfo.xml +++ b/flatpak/com.karmaa.termix.metainfo.xml @@ -5,7 +5,7 @@ Web-based server management platform with SSH terminal, tunneling, and file editing CC0-1.0 - GPL-3.0-or-later + Apache-2.0 bugattiguy527 diff --git a/flatpak/com.karmaa.termix.yml b/flatpak/com.karmaa.termix.yml index 4405a10f..b8a8f0d9 100644 --- a/flatpak/com.karmaa.termix.yml +++ b/flatpak/com.karmaa.termix.yml @@ -40,14 +40,14 @@ modules: sources: - type: file - url: https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_x64_VERSION_PLACEHOLDER_appimage.AppImage + url: https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_x64_appimage.AppImage sha256: CHECKSUM_X64_PLACEHOLDER dest-filename: termix.AppImage only-arches: - x86_64 - type: file - url: https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_arm64_VERSION_PLACEHOLDER_appimage.AppImage + url: https://github.com/Termix-SSH/Termix/releases/download/release-VERSION_PLACEHOLDER-tag/termix_linux_arm64_appimage.AppImage sha256: CHECKSUM_ARM64_PLACEHOLDER dest-filename: termix.AppImage only-arches: diff --git a/flatpak/prepare-flatpak.sh b/flatpak/prepare-flatpak.sh deleted file mode 100644 index 05162b64..00000000 --- a/flatpak/prepare-flatpak.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -set -e - -VERSION="$1" -CHECKSUM="$2" -RELEASE_DATE="$3" - -if [ -z "$VERSION" ] || [ -z "$CHECKSUM" ] || [ -z "$RELEASE_DATE" ]; then - echo "Usage: $0 " - echo "Example: $0 1.8.0 abc123... 2025-10-26" - exit 1 -fi - -echo "Preparing Flatpak submission for version $VERSION" - -cp public/icon.svg flatpak/com.karmaa.termix.svg -echo "✓ Copied SVG icon" - -if command -v convert &> /dev/null; then - convert public/icon.png -resize 256x256 flatpak/icon-256.png - convert public/icon.png -resize 128x128 flatpak/icon-128.png - echo "✓ Generated PNG icons" -else - cp public/icon.png flatpak/icon-256.png - cp public/icon.png flatpak/icon-128.png - echo "⚠ ImageMagick not found, using original icon" -fi - -sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" flatpak/com.karmaa.termix.yml -sed -i "s/CHECKSUM_PLACEHOLDER/$CHECKSUM/g" flatpak/com.karmaa.termix.yml -echo "✓ Updated manifest with version $VERSION" - -sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" flatpak/com.karmaa.termix.metainfo.xml -sed -i "s/DATE_PLACEHOLDER/$RELEASE_DATE/g" flatpak/com.karmaa.termix.metainfo.xml From 2754585988a1f447920426b75efdab3a8b773ecf Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 17:13:08 -0600 Subject: [PATCH 06/24] fix: Add imagemagik to electron builder to resolve build error --- .github/workflows/electron.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index ca1b3f4e..48458f89 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -267,10 +267,10 @@ jobs: path: release/termix_linux_armv7l_portable.tar.gz retention-days: 30 - - name: Install Flatpak builder + - name: Install Flatpak builder and dependencies run: | sudo apt-get update - sudo apt-get install -y flatpak flatpak-builder + sudo apt-get install -y flatpak flatpak-builder imagemagick - name: Add Flathub repository run: | From 84c7b9f9fc244b4d6205df37a4d1283b00dfa22b Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 17:41:03 -0600 Subject: [PATCH 07/24] fix: Build error with runtime repo flag --- .github/workflows/electron.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 48458f89..5bbae501 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -318,7 +318,19 @@ jobs: run: | cd flatpak-build flatpak-builder --repo=repo --force-clean --disable-rofiles-fuse build-dir com.karmaa.termix.yml - flatpak build-bundle repo ../release/termix_linux_flatpak.flatpak com.karmaa.termix stable + + # Determine the architecture + ARCH=$(uname -m) + if [ "$ARCH" = "x86_64" ]; then + FLATPAK_ARCH="x86_64" + elif [ "$ARCH" = "aarch64" ]; then + FLATPAK_ARCH="aarch64" + else + FLATPAK_ARCH="$ARCH" + fi + + # Build bundle for the current architecture + flatpak build-bundle repo ../release/termix_linux_flatpak.flatpak com.karmaa.termix --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo - name: Create flatpakref file run: | From 4863776f9b23e5ba44e398ac584d474aacf59433 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 20:31:03 -0600 Subject: [PATCH 08/24] fix: Flatpak runtime error and install freedesktop ver warning --- flatpak/com.karmaa.termix.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/flatpak/com.karmaa.termix.yml b/flatpak/com.karmaa.termix.yml index b8a8f0d9..caa87575 100644 --- a/flatpak/com.karmaa.termix.yml +++ b/flatpak/com.karmaa.termix.yml @@ -1,9 +1,9 @@ app-id: com.karmaa.termix runtime: org.freedesktop.Platform -runtime-version: "23.08" +runtime-version: "24.08" sdk: org.freedesktop.Sdk base: org.electronjs.Electron2.BaseApp -base-version: "23.08" +base-version: "24.08" command: termix separate-locales: false @@ -30,6 +30,12 @@ modules: - cp -r squashfs-root/resources /app/bin/ - cp -r squashfs-root/locales /app/bin/ || true + - cp squashfs-root/*.so /app/bin/ || true + - cp squashfs-root/*.pak /app/bin/ || true + - cp squashfs-root/*.bin /app/bin/ || true + - cp squashfs-root/*.dat /app/bin/ || true + - cp squashfs-root/*.json /app/bin/ || true + - install -Dm644 com.karmaa.termix.desktop /app/share/applications/com.karmaa.termix.desktop - install -Dm644 com.karmaa.termix.metainfo.xml /app/share/metainfo/com.karmaa.termix.metainfo.xml From 18f31ade1e92856198c2808ee9414050aaa97fab Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 20:31:12 -0600 Subject: [PATCH 09/24] fix: Flatpak runtime error and install freedesktop ver warning --- .github/workflows/electron.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 5bbae501..b5731a5b 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -278,9 +278,9 @@ jobs: - name: Install Flatpak runtime and SDK run: | - sudo flatpak install -y flathub org.freedesktop.Platform//23.08 - sudo flatpak install -y flathub org.freedesktop.Sdk//23.08 - sudo flatpak install -y flathub org.electronjs.Electron2.BaseApp//23.08 + sudo flatpak install -y flathub org.freedesktop.Platform//24.08 + sudo flatpak install -y flathub org.freedesktop.Sdk//24.08 + sudo flatpak install -y flathub org.electronjs.Electron2.BaseApp//24.08 - name: Get version for Flatpak id: flatpak-version From 150d5796f8e1965219f24223bbf7103b14afb39c Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 20:56:00 -0600 Subject: [PATCH 10/24] feat: Re-add homebrew cask and move scripts to backend --- .github/workflows/electron.yml | 45 +++++++++++++++++++ electron-builder.json | 3 +- homebrew/termix.rb | 24 ++++++++++ .../backend/scripts}/enable-ssl.sh | 0 {scripts => src/backend/scripts}/setup-ssl.sh | 0 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 homebrew/termix.rb rename {scripts => src/backend/scripts}/enable-ssl.sh (100%) rename {scripts => src/backend/scripts}/setup-ssl.sh (100%) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index b5731a5b..70fb3a8d 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -530,6 +530,51 @@ jobs: path: release/termix_macos_arm64_dmg.dmg retention-days: 30 + - name: Get version for Homebrew + id: homebrew-version + run: | + VERSION=$(node -p "require('./package.json').version") + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Generate Homebrew Cask + if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && (github.event.inputs.artifact_destination == 'file' || github.event.inputs.artifact_destination == 'release') + run: | + VERSION="${{ steps.homebrew-version.outputs.version }}" + DMG_PATH="release/termix_macos_universal_dmg.dmg" + + CHECKSUM=$(shasum -a 256 "$DMG_PATH" | awk '{print $1}') + + mkdir -p homebrew-generated + cp homebrew/termix.rb homebrew-generated/termix.rb + + sed -i '' "s/VERSION_PLACEHOLDER/$VERSION/g" homebrew-generated/termix.rb + sed -i '' "s/CHECKSUM_PLACEHOLDER/$CHECKSUM/g" homebrew-generated/termix.rb + sed -i '' "s|version \".*\"|version \"$VERSION\"|g" homebrew-generated/termix.rb + sed -i '' "s|sha256 \".*\"|sha256 \"$CHECKSUM\"|g" homebrew-generated/termix.rb + sed -i '' "s|release-[0-9.]*-tag|release-$VERSION-tag|g" homebrew-generated/termix.rb + + - name: Upload Homebrew Cask as artifact + uses: actions/upload-artifact@v4 + if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'file' + with: + name: homebrew-cask + path: homebrew-generated/termix.rb + retention-days: 30 + + - name: Upload Homebrew Cask to release + if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'release' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.homebrew-version.outputs.version }}" + RELEASE_TAG="release-$VERSION-tag" + + gh release list --repo ${{ github.repository }} --limit 100 | grep -q "$RELEASE_TAG" || { + echo "Release $RELEASE_TAG not found" + exit 1 + } + + gh release upload "$RELEASE_TAG" homebrew-generated/termix.rb --repo ${{ github.repository }} --clobber - name: Clean up keychains if: always() diff --git a/electron-builder.json b/electron-builder.json index 218153e1..8137f73d 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -124,5 +124,6 @@ "ITSAppUsesNonExemptEncryption": false, "NSAppleEventsUsageDescription": "Termix needs access to control other applications for terminal operations." } - } + }, + "generateUpdatesFilesForAllChannels": true } diff --git a/homebrew/termix.rb b/homebrew/termix.rb new file mode 100644 index 00000000..6f6d0d86 --- /dev/null +++ b/homebrew/termix.rb @@ -0,0 +1,24 @@ +cask "termix" do + version "1.9.0" + sha256 "8fedd242b3cae1ebfd0c391a36f1c246a26ecac258b02478ee8dea2f33cd6d96" + + url "https://github.com/Termix-SSH/Termix/releases/download/release-#{version}-tag/termix_macos_universal_dmg.dmg" + name "Termix" + desc "Web-based server management platform with SSH terminal, tunneling, and file editing" + homepage "https://github.com/Termix-SSH/Termix" + + livecheck do + url :url + strategy :github_latest + end + + app "Termix.app" + + zap trash: [ + "~/Library/Application Support/termix", + "~/Library/Caches/com.karmaa.termix", + "~/Library/Caches/com.karmaa.termix.ShipIt", + "~/Library/Preferences/com.karmaa.termix.plist", + "~/Library/Saved Application State/com.karmaa.termix.savedState", + ] +end diff --git a/scripts/enable-ssl.sh b/src/backend/scripts/enable-ssl.sh similarity index 100% rename from scripts/enable-ssl.sh rename to src/backend/scripts/enable-ssl.sh diff --git a/scripts/setup-ssl.sh b/src/backend/scripts/setup-ssl.sh similarity index 100% rename from scripts/setup-ssl.sh rename to src/backend/scripts/setup-ssl.sh From f8de3369c3183367e4edb79db3d4a96af84eb1ba Mon Sep 17 00:00:00 2001 From: LukeGus Date: Tue, 25 Nov 2025 20:57:24 -0600 Subject: [PATCH 11/24] fix: No sandbox flag issue --- electron/main.cjs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/electron/main.cjs b/electron/main.cjs index 97ced567..18bbfe2e 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -11,13 +11,12 @@ const fs = require("fs"); const os = require("os"); if (process.platform === "linux") { - app.commandLine.appendSwitch("--no-sandbox"); - app.commandLine.appendSwitch("--disable-setuid-sandbox"); app.commandLine.appendSwitch("--disable-dev-shm-usage"); - app.disableHardwareAcceleration(); - app.commandLine.appendSwitch("--disable-gpu"); - app.commandLine.appendSwitch("--disable-gpu-compositing"); + app.commandLine.appendSwitch("--use-gl=angle"); + app.commandLine.appendSwitch("--use-angle=swiftshader"); + + app.commandLine.appendSwitch("--in-process-gpu"); } app.commandLine.appendSwitch("--ignore-certificate-errors"); From e405f8a6fab30ce71151216cdaf601d34a68fedc Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 26 Nov 2025 02:52:30 -0600 Subject: [PATCH 12/24] fix: Change name for electron macos cask output --- .github/workflows/electron.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml index 70fb3a8d..b0dcec0e 100644 --- a/.github/workflows/electron.yml +++ b/.github/workflows/electron.yml @@ -557,7 +557,7 @@ jobs: uses: actions/upload-artifact@v4 if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'file' with: - name: homebrew-cask + name: termix_macos_homebrew_cask path: homebrew-generated/termix.rb retention-days: 30 @@ -713,7 +713,7 @@ jobs: run: | VERSION="${{ steps.package-version.outputs.version }}" mkdir -p release_assets - + APPIMAGE_X64_NAME="termix_linux_x64_appimage.AppImage" URL_X64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_X64_NAME" PATH_X64="release_assets/$APPIMAGE_X64_NAME" @@ -721,7 +721,7 @@ jobs: curl -L -o "$PATH_X64" "$URL_X64" chmod +x "$PATH_X64" CHECKSUM_X64=$(sha256sum "$PATH_X64" | awk '{print $1}') - + APPIMAGE_ARM64_NAME="termix_linux_arm64_appimage.AppImage" URL_ARM64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_ARM64_NAME" PATH_ARM64="release_assets/$APPIMAGE_ARM64_NAME" @@ -729,7 +729,7 @@ jobs: curl -L -o "$PATH_ARM64" "$URL_ARM64" chmod +x "$PATH_ARM64" CHECKSUM_ARM64=$(sha256sum "$PATH_ARM64" | awk '{print $1}') - + echo "appimage_x64_name=$APPIMAGE_X64_NAME" >> $GITHUB_OUTPUT echo "checksum_x64=$CHECKSUM_X64" >> $GITHUB_OUTPUT echo "appimage_arm64_name=$APPIMAGE_ARM64_NAME" >> $GITHUB_OUTPUT @@ -799,14 +799,14 @@ jobs: VERSION="${{ steps.package-version.outputs.version }}" DMG_NAME="termix_macos_universal_dmg.dmg" URL="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$DMG_NAME" - + mkdir -p release_asset PATH="release_asset/$DMG_NAME" echo "Downloading DMG from $URL" curl -L -o "$PATH" "$URL" - + CHECKSUM=$(shasum -a 256 "$PATH" | awk '{print $1}') - + echo "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT @@ -995,4 +995,4 @@ jobs: - name: Clean up keychains if: always() run: | - security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true \ No newline at end of file + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true From dfb9e7afe778a86070a0c134c4f4db1522f1ad7a Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 26 Nov 2025 19:44:19 -0600 Subject: [PATCH 13/24] fix: Sandbox error with Linux --- {homebrew => Casks}/termix.rb | 0 electron/main.cjs | 9 ++++----- flatpak/com.karmaa.termix.desktop | 2 +- flatpak/com.karmaa.termix.yml | 16 ++++++++++++++-- 4 files changed, 19 insertions(+), 8 deletions(-) rename {homebrew => Casks}/termix.rb (100%) diff --git a/homebrew/termix.rb b/Casks/termix.rb similarity index 100% rename from homebrew/termix.rb rename to Casks/termix.rb diff --git a/electron/main.cjs b/electron/main.cjs index 18bbfe2e..9cd63d58 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -11,12 +11,11 @@ const fs = require("fs"); const os = require("os"); if (process.platform === "linux") { - app.commandLine.appendSwitch("--disable-dev-shm-usage"); + // Enable Ozone platform auto-detection for Wayland/X11 support + app.commandLine.appendSwitch("--ozone-platform-hint=auto"); - app.commandLine.appendSwitch("--use-gl=angle"); - app.commandLine.appendSwitch("--use-angle=swiftshader"); - - app.commandLine.appendSwitch("--in-process-gpu"); + // Enable hardware video decoding if available + app.commandLine.appendSwitch("--enable-features=VaapiVideoDecoder"); } app.commandLine.appendSwitch("--ignore-certificate-errors"); diff --git a/flatpak/com.karmaa.termix.desktop b/flatpak/com.karmaa.termix.desktop index 3aabfd06..59d27c13 100644 --- a/flatpak/com.karmaa.termix.desktop +++ b/flatpak/com.karmaa.termix.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Name=Termix Comment=Web-based server management platform with SSH terminal, tunneling, and file editing -Exec=termix %U +Exec=run.sh %U Icon=com.karmaa.termix Terminal=false Type=Application diff --git a/flatpak/com.karmaa.termix.yml b/flatpak/com.karmaa.termix.yml index caa87575..7b67c0e7 100644 --- a/flatpak/com.karmaa.termix.yml +++ b/flatpak/com.karmaa.termix.yml @@ -4,7 +4,7 @@ runtime-version: "24.08" sdk: org.freedesktop.Sdk base: org.electronjs.Electron2.BaseApp base-version: "24.08" -command: termix +command: run.sh separate-locales: false finish-args: @@ -16,8 +16,11 @@ finish-args: - --device=dri - --filesystem=home - --socket=ssh-auth - - --talk-name=org.freedesktop.Notifications + - --socket=session-bus - --talk-name=org.freedesktop.secrets + - --env=ELECTRON_TRASH=gio + - --env=XCURSOR_PATH=/run/host/user-share/icons:/run/host/share/icons + - --env=ELECTRON_OZONE_PLATFORM_HINT=auto modules: - name: termix @@ -36,6 +39,15 @@ modules: - cp squashfs-root/*.dat /app/bin/ || true - cp squashfs-root/*.json /app/bin/ || true + - | + cat > run.sh << 'EOF' + #!/bin/bash + export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID" + exec zypak-wrapper /app/bin/termix "$@" + EOF + - chmod +x run.sh + - install -Dm755 run.sh /app/bin/run.sh + - install -Dm644 com.karmaa.termix.desktop /app/share/applications/com.karmaa.termix.desktop - install -Dm644 com.karmaa.termix.metainfo.xml /app/share/metainfo/com.karmaa.termix.metainfo.xml From 05a1b3bfafd9de4e8e4cc4b2356d88147a42dfcf Mon Sep 17 00:00:00 2001 From: LukeGus Date: Wed, 26 Nov 2025 23:39:22 -0600 Subject: [PATCH 14/24] fix: Remove comming soon for app stores in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0e9c10f0..80fc69e9 100644 --- a/README.md +++ b/README.md @@ -80,16 +80,16 @@ Supported Devices: - Windows (x64/ia32) - Portable - MSI Installer - - Chocolatey Package Manager (coming soon) + - Chocolatey Package Manager - Linux (x64/ia32) - Portable - AppImage - Deb - - Flatpak (coming soon) + - Flatpak - macOS (x64/ia32 on v12.0+) - - Apple App Store (coming soon) + - Apple App Store - DMG - - Homebrew (coming soon) + - Homebrew - iOS/iPadOS (v15.1+) - Apple App Store - ISO From a98359ebc170ecc1bae9f46accce72318f8888b7 Mon Sep 17 00:00:00 2001 From: SlimGary Date: Mon, 8 Dec 2025 06:03:40 +0100 Subject: [PATCH 15/24] Adding Comment at the end of the public_key on the host on deploy (#440) * Add termix.rb Cask file * Update Termix to version 1.9.0 with new checksum * Update README to remove 'coming soon' notes * -Add New Interface for Credential DB -Add Credential Name as a comment into the server authorized_key file --------- Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> --- src/backend/database/routes/credentials.ts | 17 +++++++++-------- src/types/index.ts | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/backend/database/routes/credentials.ts b/src/backend/database/routes/credentials.ts index df9ab936..60a53d9c 100644 --- a/src/backend/database/routes/credentials.ts +++ b/src/backend/database/routes/credentials.ts @@ -1,4 +1,7 @@ -import type { AuthenticatedRequest } from "../../../types/index.js"; +import type { + AuthenticatedRequest, + CredentialBackend, +} from "../../../types/index.js"; import express from "express"; import { db } from "../db/index.js"; import { sshCredentials, sshCredentialUsage, sshData } from "../db/schema.js"; @@ -1124,10 +1127,9 @@ router.post( async function deploySSHKeyToHost( hostConfig: Record, - publicKey: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _credentialData: Record, + credData: CredentialBackend, ): Promise<{ success: boolean; message?: string; error?: string }> { + const publicKey = credData.public_key as string; return new Promise((resolve) => { const conn = new Client(); @@ -1248,7 +1250,7 @@ async function deploySSHKeyToHost( .replace(/'/g, "'\\''"); conn.exec( - `printf '%s\\n' '${escapedKey}' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys`, + `printf '%s\\n' '${escapedKey} ${credData.name}@Termix' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys`, (err, stream) => { if (err) { clearTimeout(addTimeout); @@ -1510,7 +1512,7 @@ router.post( }); } - const credData = credential[0]; + const credData = credential[0] as unknown as CredentialBackend; if (credData.authType !== "key") { return res.status(400).json({ @@ -1519,7 +1521,7 @@ router.post( }); } - const publicKey = credData.public_key || credData.publicKey; + const publicKey = credData.public_key; if (!publicKey) { return res.status(400).json({ success: false, @@ -1601,7 +1603,6 @@ router.post( const deployResult = await deploySSHKeyToHost( hostConfig, - publicKey as string, credData, ); diff --git a/src/types/index.ts b/src/types/index.ts index 7adc1ab6..5d9f4980 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,6 +119,28 @@ export interface Credential { updatedAt: string; } +export interface CredentialBackend { + id: number; + userId: string; + name: string; + description: string | null; + folder: string | null; + tags: string; + authType: "password" | "key"; + username: string; + password: string | null; + key: string; + private_key?: string; + public_key?: string; + key_password: string | null; + keyType?: string; + detectedKeyType: string; + usageCount: number; + lastUsed: string | null; + createdAt: string; + updatedAt: string; +} + export interface CredentialData { name: string; description?: string; From 208110a4337c6ce1c6aebda2b7e95f7fcc1f9080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nunzio=20Marf=C3=A8?= Date: Mon, 8 Dec 2025 06:04:48 +0100 Subject: [PATCH 16/24] Sudo auto fill password (#441) * Add termix.rb Cask file * Update Termix to version 1.9.0 with new checksum * Update README to remove 'coming soon' notes * Feature Sudo password auto-fill; * Fix locale json shema; --------- Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com> --- src/constants/terminal-themes.ts | 1 + src/locales/de/translation.json | 12 +- src/locales/en/translation.json | 12 +- src/locales/fr/translation.json | 12 +- src/locales/pt-BR/translation.json | 13 +- src/locales/ru/translation.json | 12 +- src/locales/zh/translation.json | 12 +- src/types/index.ts | 15 +- .../apps/host-manager/HostManagerEditor.tsx | 26 +- .../apps/terminal/SudoPasswordPopup.tsx | 82 + src/ui/desktop/apps/terminal/Terminal.tsx | 2810 +++++++++-------- 11 files changed, 1585 insertions(+), 1422 deletions(-) create mode 100644 src/ui/desktop/apps/terminal/SudoPasswordPopup.tsx diff --git a/src/constants/terminal-themes.ts b/src/constants/terminal-themes.ts index 63f55055..393188f8 100644 --- a/src/constants/terminal-themes.ts +++ b/src/constants/terminal-themes.ts @@ -705,6 +705,7 @@ export const DEFAULT_TERMINAL_CONFIG = { startupSnippetId: null as number | null, autoMosh: false, moshCommand: "mosh-server new -s -l LANG=en_US.UTF-8", + sudoPasswordAutoFill: false, }; export type TerminalConfigType = typeof DEFAULT_TERMINAL_CONFIG; diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index c6cdb3fa..54982342 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -799,7 +799,9 @@ "searchServers": "Server durchsuchen...", "noServerFound": "Kein Server gefunden", "jumpHostsOrder": "Verbindungen werden in dieser Reihenfolge hergestellt: Jump-Host 1 → Jump-Host 2 → ... → Ziel-Server", - "advancedAuthSettings": "Erweiterte Authentifizierungseinstellungen" + "advancedAuthSettings": "Erweiterte Authentifizierungseinstellungen", + "sudoPasswordAutoFill": "Sudo-Passwort automatisch ausfüllen", + "sudoPasswordAutoFillDesc": "Popup anzeigen, um das Passwort bei sudo-Befehlen automatisch einzufügen" }, "terminal": { "title": "Terminal", @@ -833,7 +835,11 @@ "connectionTimeout": "Zeitüberschreitung der Verbindung", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}} : {{path}}", - "runTitle": "Ausführen von {{command}} – {{host}}" + "runTitle": "Ausführen von {{command}} – {{host}}", + "sudoPasswordPopupTitle": "Passwort einfügen?", + "sudoPasswordPopupHint": "Drücken Sie Enter zum Einfügen, Esc zum Abbrechen", + "sudoPasswordPopupConfirm": "Einfügen", + "sudoPasswordPopupDismiss": "Abbrechen" }, "fileManager": { "title": "Dateimanager", @@ -1635,4 +1641,4 @@ "close": "Schließen", "hostManager": "Host-Manager" } -} +} \ No newline at end of file diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 60e2ec3a..c21d4a16 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -908,7 +908,9 @@ "quickActionName": "Action name", "noSnippetFound": "No snippet found", "quickActionsOrder": "Quick action buttons will appear in the order listed above on the Server Stats page", - "advancedAuthSettings": "Advanced Authentication Settings" + "advancedAuthSettings": "Advanced Authentication Settings", + "sudoPasswordAutoFill": "Sudo Password Auto-Fill", + "sudoPasswordAutoFillDesc": "Automatically offer to insert SSH password when sudo prompts for password" }, "terminal": { "title": "Terminal", @@ -946,7 +948,11 @@ "totpRequired": "Two-Factor Authentication Required", "totpCodeLabel": "Verification Code", "totpPlaceholder": "000000", - "totpVerify": "Verify" + "totpVerify": "Verify", + "sudoPasswordPopupTitle": "Insert Password?", + "sudoPasswordPopupHint": "Press Enter to insert, Esc to dismiss", + "sudoPasswordPopupConfirm": "Insert", + "sudoPasswordPopupDismiss": "Dismiss" }, "fileManager": { "title": "File Manager", @@ -1825,4 +1831,4 @@ "close": "Close", "hostManager": "Host Manager" } -} +} \ No newline at end of file diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index 7f111fb8..33370499 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -790,7 +790,9 @@ "searchServers": "Rechercher des serveurs...", "noServerFound": "Aucun serveur trouvé", "jumpHostsOrder": "Les connexions seront établies dans l'ordre : Serveur de rebond 1 → Serveur de rebond 2 → ... → Serveur cible", - "advancedAuthSettings": "Paramètres d'authentification avancés" + "advancedAuthSettings": "Paramètres d'authentification avancés", + "sudoPasswordAutoFill": "Remplissage automatique du mot de passe sudo", + "sudoPasswordAutoFillDesc": "Proposer automatiquement d’insérer le mot de passe SSH lorsque sudo demande un mot de passe" }, "terminal": { "title": "Terminal", @@ -828,7 +830,11 @@ "totpRequired": "Authentification à deux facteurs requise", "totpCodeLabel": "Code de vérification", "totpPlaceholder": "000000", - "totpVerify": "Vérifier" + "totpVerify": "Vérifier", + "sudoPasswordPopupTitle": "Insérer le mot de passe ?", + "sudoPasswordPopupHint": "Appuyez sur Entrée pour insérer, Échap pour annuler", + "sudoPasswordPopupConfirm": "Insérer", + "sudoPasswordPopupDismiss": "Annuler" }, "fileManager": { "title": "Gestionnaire de fichiers", @@ -1606,4 +1612,4 @@ "ram": "Mémoire (RAM)", "notAvailable": "N/D" } -} +} \ No newline at end of file diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 9e9350e3..fd008b73 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -745,7 +745,9 @@ "searchServers": "Pesquisar servidores...", "noServerFound": "Nenhum servidor encontrado", "jumpHostsOrder": "As conexões serão feitas na ordem: Host de Salto 1 → Host de Salto 2 → ... → Servidor de Destino", - "advancedAuthSettings": "Configurações Avançadas de Autenticação" + "advancedAuthSettings": "Configurações Avançadas de Autenticação", + "sudoPasswordAutoFill": "Preenchimento automático da senha do sudo", + "sudoPasswordAutoFillDesc": "Oferecer automaticamente inserir a senha SSH quando o sudo solicitar uma senha" }, "terminal": { "title": "Terminal", @@ -779,7 +781,11 @@ "connectionTimeout": "Tempo limite de conexão esgotado", "terminalTitle": "Terminal - {{host}}", "terminalWithPath": "Terminal - {{host}}:{{path}}", - "runTitle": "Executando {{command}} - {{host}}" + "runTitle": "Executando {{command}} - {{host}}", + "sudoPasswordPopupTitle": "Inserir Senha?", + "sudoPasswordPopupHint": "Pressione Enter para inserir, Esc para cancelar", + "sudoPasswordPopupConfirm": "Inserir", + "sudoPasswordPopupDismiss": "Cancelar" }, "fileManager": { "title": "Gerenciador de Arquivos", @@ -1034,7 +1040,6 @@ "fileSavedSuccessfully": "Arquivo salvo com sucesso", "autoSaveFailed": "Falha no salvamento automático", "fileAutoSaved": "Arquivo salvo automaticamente", - "moveFileFailed": "Falha ao mover {{name}}", "moveOperationFailed": "Falha na operação de mover", "canOnlyCompareFiles": "Só é possível comparar dois arquivos", @@ -1528,4 +1533,4 @@ "viewMobileAppDocs": "Instalar Aplicativo Móvel", "mobileAppDocumentation": "Documentação do Aplicativo Móvel" } -} +} \ No newline at end of file diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 0f523deb..4b6d94c9 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -858,7 +858,9 @@ "searchServers": "Поиск серверов...", "noServerFound": "Сервер не найден", "jumpHostsOrder": "Подключения будут выполнены в порядке: Промежуточный хост 1 → Промежуточный хост 2 → ... → Целевой сервер", - "advancedAuthSettings": "Расширенные настройки аутентификации" + "advancedAuthSettings": "Расширенные настройки аутентификации", + "sudoPasswordAutoFill": "Автозаполнение пароля sudo", + "sudoPasswordAutoFillDesc": "Показывать всплывающее окно для автоматического ввода пароля при выполнении команд sudo" }, "terminal": { "title": "Терминал", @@ -896,7 +898,11 @@ "totpRequired": "Требуется двухфакторная аутентификация", "totpCodeLabel": "Код проверки", "totpPlaceholder": "000000", - "totpVerify": "Проверить" + "totpVerify": "Проверить", + "sudoPasswordPopupTitle": "Вставить пароль?", + "sudoPasswordPopupHint": "Нажмите Enter для вставки, Esc для отмены", + "sudoPasswordPopupConfirm": "Вставить", + "sudoPasswordPopupDismiss": "Отмена" }, "fileManager": { "title": "Файловый менеджер", @@ -1722,4 +1728,4 @@ "close": "Закрыть", "hostManager": "Менеджер хостов" } -} +} \ No newline at end of file diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 3b15347d..73bb39e1 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -893,7 +893,9 @@ "searchServers": "搜索服务器...", "noServerFound": "未找到服务器", "jumpHostsOrder": "连接将按顺序进行:跳板主机 1 → 跳板主机 2 → ... → 目标服务器", - "advancedAuthSettings": "高级身份验证设置" + "advancedAuthSettings": "高级身份验证设置", + "sudoPasswordAutoFill": "Sudo 密码自动填充", + "sudoPasswordAutoFillDesc": "在 sudo 命令时显示弹窗以自动输入密码" }, "terminal": { "title": "终端", @@ -931,7 +933,11 @@ "reconnecting": "重新连接中... ({{attempt}}/{{max}})", "reconnected": "重新连接成功", "maxReconnectAttemptsReached": "已达到最大重连尝试次数", - "connectionTimeout": "连接超时" + "connectionTimeout": "连接超时", + "sudoPasswordPopupTitle": "插入密码?", + "sudoPasswordPopupHint": "按 Enter 插入,Esc 取消", + "sudoPasswordPopupConfirm": "插入", + "sudoPasswordPopupDismiss": "取消" }, "fileManager": { "title": "文件管理器", @@ -1686,4 +1692,4 @@ "close": "关闭", "hostManager": "主机管理器" } -} +} \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 5d9f4980..83dad26a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -329,6 +329,7 @@ export interface TerminalConfig { startupSnippetId: number | null; autoMosh: boolean; moshCommand: string; + sudoPasswordAutoFill: boolean; } // ============================================================================ @@ -338,13 +339,13 @@ export interface TerminalConfig { export interface TabContextTab { id: number; type: - | "home" - | "terminal" - | "ssh_manager" - | "server" - | "admin" - | "file_manager" - | "user_profile"; + | "home" + | "terminal" + | "ssh_manager" + | "server" + | "admin" + | "file_manager" + | "user_profile"; title: string; hostConfig?: SSHHost; terminalRef?: any; diff --git a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx index 552d0b23..c1d595d2 100644 --- a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx +++ b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx @@ -547,6 +547,7 @@ export function HostManagerEditor({ startupSnippetId: z.number().nullable(), autoMosh: z.boolean(), moshCommand: z.string(), + sudoPasswordAutoFill: z.boolean(), }) .optional(), forceKeyboardInteractive: z.boolean().optional(), @@ -631,7 +632,7 @@ export function HostManagerEditor({ type FormData = z.infer; const form = useForm({ - resolver: zodResolver(formSchema), + resolver: zodResolver(formSchema) as any, defaultValues: { name: "", ip: "", @@ -2443,6 +2444,29 @@ export function HostManagerEditor({ /> )} + ( + +
+ + {t("hosts.sudoPasswordAutoFill")} + + + {t("hosts.sudoPasswordAutoFillDesc")} + +
+ + + +
+ )} + /> +