Merge branch 'dev-1.10.0' into translations
This commit is contained in:
404
.github/workflows/electron.yml
vendored
404
.github/workflows/electron.yml
vendored
@@ -27,7 +27,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-latest
|
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:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
@@ -72,10 +72,6 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: npm run build && npx electron-builder --win --x64 --ia32
|
run: npm run build && npx electron-builder --win --x64 --ia32
|
||||||
|
|
||||||
- name: List release files
|
|
||||||
run: |
|
|
||||||
dir release
|
|
||||||
|
|
||||||
- name: Upload Windows x64 NSIS Installer
|
- name: Upload Windows x64 NSIS Installer
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && github.event.inputs.artifact_destination != 'none'
|
if: hashFiles('release/termix_windows_x64_nsis.exe') != '' && github.event.inputs.artifact_destination != 'none'
|
||||||
@@ -136,7 +132,7 @@ jobs:
|
|||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
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:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
@@ -199,17 +195,6 @@ jobs:
|
|||||||
|
|
||||||
cd ..
|
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
|
- name: Upload Linux x64 AppImage
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none'
|
if: hashFiles('release/termix_linux_x64_appimage.AppImage') != '' && github.event.inputs.artifact_destination != 'none'
|
||||||
@@ -282,6 +267,93 @@ jobs:
|
|||||||
path: release/termix_linux_armv7l_portable.tar.gz
|
path: release/termix_linux_armv7l_portable.tar.gz
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Install Flatpak builder and dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y flatpak flatpak-builder imagemagick
|
||||||
|
|
||||||
|
- 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//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
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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: |
|
||||||
|
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:
|
build-macos:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
if: github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == 'all'
|
if: github.event.inputs.build_type == 'macos' || github.event.inputs.build_type == 'all'
|
||||||
@@ -425,11 +497,6 @@ jobs:
|
|||||||
export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
||||||
npx electron-builder --mac dmg --universal --x64 --arm64 --publish never
|
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
|
- 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')
|
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
|
uses: actions/upload-artifact@v4
|
||||||
@@ -463,42 +530,51 @@ jobs:
|
|||||||
path: release/termix_macos_arm64_dmg.dmg
|
path: release/termix_macos_arm64_dmg.dmg
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
- name: Check for App Store Connect API credentials
|
- name: Get version for Homebrew
|
||||||
if: steps.check_certs.outputs.has_certs == 'true'
|
id: homebrew-version
|
||||||
id: check_asc_creds
|
|
||||||
run: |
|
run: |
|
||||||
if [ -n "${{ secrets.APPLE_KEY_ID }}" ] && [ -n "${{ secrets.APPLE_ISSUER_ID }}" ] && [ -n "${{ secrets.APPLE_KEY_CONTENT }}" ]; then
|
VERSION=$(node -p "require('./package.json').version")
|
||||||
echo "has_credentials=true" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup Ruby for Fastlane
|
- name: Generate Homebrew Cask
|
||||||
if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit'
|
if: hashFiles('release/termix_macos_universal_dmg.dmg') != '' && (github.event.inputs.artifact_destination == 'file' || github.event.inputs.artifact_destination == 'release')
|
||||||
uses: ruby/setup-ruby@v1
|
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:
|
with:
|
||||||
ruby-version: "3.2"
|
name: termix_macos_homebrew_cask
|
||||||
bundler-cache: false
|
path: homebrew-generated/termix.rb
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
- name: Install Fastlane
|
- name: Upload Homebrew Cask to release
|
||||||
if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit'
|
if: hashFiles('homebrew-generated/termix.rb') != '' && github.event.inputs.artifact_destination == 'release'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
gem install fastlane -N
|
VERSION="${{ steps.homebrew-version.outputs.version }}"
|
||||||
|
RELEASE_TAG="release-$VERSION-tag"
|
||||||
|
|
||||||
- name: Deploy to App Store Connect (TestFlight)
|
gh release list --repo ${{ github.repository }} --limit 100 | grep -q "$RELEASE_TAG" || {
|
||||||
if: steps.check_asc_creds.outputs.has_credentials == 'true' && github.event.inputs.artifact_destination == 'submit'
|
echo "Release $RELEASE_TAG not found"
|
||||||
run: |
|
|
||||||
PKG_FILE=$(find release -name "*.pkg" -type f | head -n 1)
|
|
||||||
if [ -z "$PKG_FILE" ]; then
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
mkdir -p ~/private_keys
|
gh release upload "$RELEASE_TAG" homebrew-generated/termix.rb --repo ${{ github.repository }} --clobber
|
||||||
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
|
- name: Clean up keychains
|
||||||
if: always()
|
if: always()
|
||||||
@@ -509,7 +585,6 @@ jobs:
|
|||||||
submit-to-chocolatey:
|
submit-to-chocolatey:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
if: github.event.inputs.artifact_destination == 'submit'
|
if: github.event.inputs.artifact_destination == 'submit'
|
||||||
needs: [build-windows]
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -525,20 +600,25 @@ jobs:
|
|||||||
$VERSION = (Get-Content package.json | ConvertFrom-Json).version
|
$VERSION = (Get-Content package.json | ConvertFrom-Json).version
|
||||||
echo "version=$VERSION" >> $env:GITHUB_OUTPUT
|
echo "version=$VERSION" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Download Windows x64 MSI artifact
|
- name: Download and prepare MSI info from public release
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termix_windows_x64_msi
|
|
||||||
path: artifact
|
|
||||||
|
|
||||||
- name: Get MSI file info
|
|
||||||
id: msi-info
|
id: msi-info
|
||||||
run: |
|
run: |
|
||||||
$VERSION = "${{ steps.package-version.outputs.version }}"
|
$VERSION = "${{ steps.package-version.outputs.version }}"
|
||||||
$MSI_FILE = Get-ChildItem -Path artifact -Filter "*.msi" | Select-Object -First 1
|
$MSI_NAME = "termix_windows_x64_msi.msi"
|
||||||
$MSI_NAME = $MSI_FILE.Name
|
$DOWNLOAD_URL = "https://github.com/Termix-SSH/Termix/releases/download/release-$($VERSION)-tag/$($MSI_NAME)"
|
||||||
$CHECKSUM = (Get-FileHash -Path $MSI_FILE.FullName -Algorithm SHA256).Hash
|
|
||||||
|
|
||||||
|
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 "msi_name=$MSI_NAME" >> $env:GITHUB_OUTPUT
|
||||||
echo "checksum=$CHECKSUM" >> $env:GITHUB_OUTPUT
|
echo "checksum=$CHECKSUM" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
@@ -610,7 +690,7 @@ jobs:
|
|||||||
submit-to-flatpak:
|
submit-to-flatpak:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.inputs.artifact_destination == 'submit'
|
if: github.event.inputs.artifact_destination == 'submit'
|
||||||
needs: [build-linux]
|
needs: []
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -628,30 +708,27 @@ jobs:
|
|||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT
|
echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Download Linux x64 AppImage artifact
|
- name: Download and prepare AppImage info from public release
|
||||||
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
|
|
||||||
id: appimage-info
|
id: appimage-info
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.package-version.outputs.version }}"
|
VERSION="${{ steps.package-version.outputs.version }}"
|
||||||
|
mkdir -p release_assets
|
||||||
|
|
||||||
APPIMAGE_X64_FILE=$(find artifact-x64 -name "*.AppImage" -type f | head -n 1)
|
APPIMAGE_X64_NAME="termix_linux_x64_appimage.AppImage"
|
||||||
APPIMAGE_X64_NAME=$(basename "$APPIMAGE_X64_FILE")
|
URL_X64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_X64_NAME"
|
||||||
CHECKSUM_X64=$(sha256sum "$APPIMAGE_X64_FILE" | awk '{print $1}')
|
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_FILE=$(find artifact-arm64 -name "*.AppImage" -type f | head -n 1)
|
APPIMAGE_ARM64_NAME="termix_linux_arm64_appimage.AppImage"
|
||||||
APPIMAGE_ARM64_NAME=$(basename "$APPIMAGE_ARM64_FILE")
|
URL_ARM64="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$APPIMAGE_ARM64_NAME"
|
||||||
CHECKSUM_ARM64=$(sha256sum "$APPIMAGE_ARM64_FILE" | awk '{print $1}')
|
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 "appimage_x64_name=$APPIMAGE_X64_NAME" >> $GITHUB_OUTPUT
|
||||||
echo "checksum_x64=$CHECKSUM_X64" >> $GITHUB_OUTPUT
|
echo "checksum_x64=$CHECKSUM_X64" >> $GITHUB_OUTPUT
|
||||||
@@ -690,10 +767,6 @@ jobs:
|
|||||||
sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" flatpak-submission/com.karmaa.termix.metainfo.xml
|
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
|
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
|
- name: Upload Flatpak submission as artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -704,7 +777,7 @@ jobs:
|
|||||||
submit-to-homebrew:
|
submit-to-homebrew:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
if: github.event.inputs.artifact_destination == 'submit'
|
if: github.event.inputs.artifact_destination == 'submit'
|
||||||
needs: [build-macos]
|
needs: []
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -720,19 +793,19 @@ jobs:
|
|||||||
VERSION=$(node -p "require('./package.json').version")
|
VERSION=$(node -p "require('./package.json').version")
|
||||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Download macOS Universal DMG artifact
|
- name: Download and prepare DMG info from public release
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termix_macos_universal_dmg
|
|
||||||
path: artifact
|
|
||||||
|
|
||||||
- name: Get DMG file info
|
|
||||||
id: dmg-info
|
id: dmg-info
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.package-version.outputs.version }}"
|
VERSION="${{ steps.package-version.outputs.version }}"
|
||||||
DMG_FILE=$(find artifact -name "*.dmg" -type f | head -n 1)
|
DMG_NAME="termix_macos_universal_dmg.dmg"
|
||||||
DMG_NAME=$(basename "$DMG_FILE")
|
URL="https://github.com/Termix-SSH/Termix/releases/download/release-$VERSION-tag/$DMG_NAME"
|
||||||
CHECKSUM=$(shasum -a 256 "$DMG_FILE" | awk '{print $1}')
|
|
||||||
|
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 "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT
|
||||||
echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT
|
echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT
|
||||||
@@ -752,16 +825,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Verify Cask syntax
|
- name: Verify Cask syntax
|
||||||
run: |
|
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
|
ruby -c homebrew-submission/Casks/t/termix.rb
|
||||||
|
|
||||||
- name: List submission files
|
|
||||||
run: |
|
|
||||||
find homebrew-submission -type f
|
|
||||||
|
|
||||||
- name: Upload Homebrew submission as artifact
|
- name: Upload Homebrew submission as artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
@@ -789,10 +854,6 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
- name: Display artifact structure
|
|
||||||
run: |
|
|
||||||
ls -R artifacts/
|
|
||||||
|
|
||||||
- name: Upload artifacts to latest release
|
- name: Upload artifacts to latest release
|
||||||
run: |
|
run: |
|
||||||
cd artifacts
|
cd artifacts
|
||||||
@@ -808,3 +869,130 @@ jobs:
|
|||||||
done
|
done
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
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
|
||||||
|
|||||||
@@ -124,5 +124,6 @@
|
|||||||
"ITSAppUsesNonExemptEncryption": false,
|
"ITSAppUsesNonExemptEncryption": false,
|
||||||
"NSAppleEventsUsageDescription": "Termix needs access to control other applications for terminal operations."
|
"NSAppleEventsUsageDescription": "Termix needs access to control other applications for terminal operations."
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"generateUpdatesFilesForAllChannels": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,11 @@ const fs = require("fs");
|
|||||||
const os = require("os");
|
const os = require("os");
|
||||||
|
|
||||||
if (process.platform === "linux") {
|
if (process.platform === "linux") {
|
||||||
app.commandLine.appendSwitch("--no-sandbox");
|
// Enable Ozone platform auto-detection for Wayland/X11 support
|
||||||
app.commandLine.appendSwitch("--disable-setuid-sandbox");
|
app.commandLine.appendSwitch("--ozone-platform-hint=auto");
|
||||||
app.commandLine.appendSwitch("--disable-dev-shm-usage");
|
|
||||||
|
|
||||||
app.disableHardwareAcceleration();
|
// Enable hardware video decoding if available
|
||||||
app.commandLine.appendSwitch("--disable-gpu");
|
app.commandLine.appendSwitch("--enable-features=VaapiVideoDecoder");
|
||||||
app.commandLine.appendSwitch("--disable-gpu-compositing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.commandLine.appendSwitch("--ignore-certificate-errors");
|
app.commandLine.appendSwitch("--ignore-certificate-errors");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Termix
|
Name=Termix
|
||||||
Comment=Web-based server management platform with SSH terminal, tunneling, and file editing
|
Comment=Web-based server management platform with SSH terminal, tunneling, and file editing
|
||||||
Exec=termix %U
|
Exec=run.sh %U
|
||||||
Icon=com.karmaa.termix
|
Icon=com.karmaa.termix
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
|
|||||||
12
flatpak/com.karmaa.termix.flatpakref
Normal file
12
flatpak/com.karmaa.termix.flatpakref
Normal file
@@ -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
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<summary>Web-based server management platform with SSH terminal, tunneling, and file editing</summary>
|
<summary>Web-based server management platform with SSH terminal, tunneling, and file editing</summary>
|
||||||
|
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<project_license>GPL-3.0-or-later</project_license>
|
<project_license>Apache-2.0</project_license>
|
||||||
|
|
||||||
<developer_name>bugattiguy527</developer_name>
|
<developer_name>bugattiguy527</developer_name>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
app-id: com.karmaa.termix
|
app-id: com.karmaa.termix
|
||||||
runtime: org.freedesktop.Platform
|
runtime: org.freedesktop.Platform
|
||||||
runtime-version: "23.08"
|
runtime-version: "24.08"
|
||||||
sdk: org.freedesktop.Sdk
|
sdk: org.freedesktop.Sdk
|
||||||
base: org.electronjs.Electron2.BaseApp
|
base: org.electronjs.Electron2.BaseApp
|
||||||
base-version: "23.08"
|
base-version: "24.08"
|
||||||
command: termix
|
command: run.sh
|
||||||
separate-locales: false
|
separate-locales: false
|
||||||
|
|
||||||
finish-args:
|
finish-args:
|
||||||
@@ -16,8 +16,11 @@ finish-args:
|
|||||||
- --device=dri
|
- --device=dri
|
||||||
- --filesystem=home
|
- --filesystem=home
|
||||||
- --socket=ssh-auth
|
- --socket=ssh-auth
|
||||||
- --talk-name=org.freedesktop.Notifications
|
- --socket=session-bus
|
||||||
- --talk-name=org.freedesktop.secrets
|
- --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:
|
modules:
|
||||||
- name: termix
|
- name: termix
|
||||||
@@ -30,6 +33,21 @@ modules:
|
|||||||
- cp -r squashfs-root/resources /app/bin/
|
- cp -r squashfs-root/resources /app/bin/
|
||||||
- cp -r squashfs-root/locales /app/bin/ || true
|
- 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
|
||||||
|
|
||||||
|
- |
|
||||||
|
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.desktop /app/share/applications/com.karmaa.termix.desktop
|
||||||
|
|
||||||
- install -Dm644 com.karmaa.termix.metainfo.xml /app/share/metainfo/com.karmaa.termix.metainfo.xml
|
- install -Dm644 com.karmaa.termix.metainfo.xml /app/share/metainfo/com.karmaa.termix.metainfo.xml
|
||||||
@@ -40,14 +58,14 @@ modules:
|
|||||||
|
|
||||||
sources:
|
sources:
|
||||||
- type: file
|
- 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
|
sha256: CHECKSUM_X64_PLACEHOLDER
|
||||||
dest-filename: termix.AppImage
|
dest-filename: termix.AppImage
|
||||||
only-arches:
|
only-arches:
|
||||||
- x86_64
|
- x86_64
|
||||||
|
|
||||||
- type: file
|
- 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
|
sha256: CHECKSUM_ARM64_PLACEHOLDER
|
||||||
dest-filename: termix.AppImage
|
dest-filename: termix.AppImage
|
||||||
only-arches:
|
only-arches:
|
||||||
|
|||||||
@@ -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 <version> <checksum> <release-date>"
|
|
||||||
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
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
import type { AuthenticatedRequest } from "../../../types/index.js";
|
import type {
|
||||||
|
AuthenticatedRequest,
|
||||||
|
CredentialBackend,
|
||||||
|
} from "../../../types/index.js";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { db } from "../db/index.js";
|
import { db } from "../db/index.js";
|
||||||
import { sshCredentials, sshCredentialUsage, sshData } from "../db/schema.js";
|
import { sshCredentials, sshCredentialUsage, sshData } from "../db/schema.js";
|
||||||
@@ -1124,10 +1127,9 @@ router.post(
|
|||||||
|
|
||||||
async function deploySSHKeyToHost(
|
async function deploySSHKeyToHost(
|
||||||
hostConfig: Record<string, unknown>,
|
hostConfig: Record<string, unknown>,
|
||||||
publicKey: string,
|
credData: CredentialBackend,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
_credentialData: Record<string, unknown>,
|
|
||||||
): Promise<{ success: boolean; message?: string; error?: string }> {
|
): Promise<{ success: boolean; message?: string; error?: string }> {
|
||||||
|
const publicKey = credData.public_key as string;
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const conn = new Client();
|
const conn = new Client();
|
||||||
|
|
||||||
@@ -1248,7 +1250,7 @@ async function deploySSHKeyToHost(
|
|||||||
.replace(/'/g, "'\\''");
|
.replace(/'/g, "'\\''");
|
||||||
|
|
||||||
conn.exec(
|
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) => {
|
(err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
clearTimeout(addTimeout);
|
clearTimeout(addTimeout);
|
||||||
@@ -1510,7 +1512,7 @@ router.post(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const credData = credential[0];
|
const credData = credential[0] as unknown as CredentialBackend;
|
||||||
|
|
||||||
if (credData.authType !== "key") {
|
if (credData.authType !== "key") {
|
||||||
return res.status(400).json({
|
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) {
|
if (!publicKey) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
@@ -1601,7 +1603,6 @@ router.post(
|
|||||||
|
|
||||||
const deployResult = await deploySSHKeyToHost(
|
const deployResult = await deploySSHKeyToHost(
|
||||||
hostConfig,
|
hostConfig,
|
||||||
publicKey as string,
|
|
||||||
credData,
|
credData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -705,6 +705,7 @@ export const DEFAULT_TERMINAL_CONFIG = {
|
|||||||
startupSnippetId: null as number | null,
|
startupSnippetId: null as number | null,
|
||||||
autoMosh: false,
|
autoMosh: false,
|
||||||
moshCommand: "mosh-server new -s -l LANG=en_US.UTF-8",
|
moshCommand: "mosh-server new -s -l LANG=en_US.UTF-8",
|
||||||
|
sudoPasswordAutoFill: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TerminalConfigType = typeof DEFAULT_TERMINAL_CONFIG;
|
export type TerminalConfigType = typeof DEFAULT_TERMINAL_CONFIG;
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ import deTranslation from "../locales/de/translation.json";
|
|||||||
import ptbrTranslation from "../locales/pt-BR/translation.json";
|
import ptbrTranslation from "../locales/pt-BR/translation.json";
|
||||||
import ruTranslation from "../locales/ru/translation.json";
|
import ruTranslation from "../locales/ru/translation.json";
|
||||||
import frTranslation from "../locales/fr/translation.json";
|
import frTranslation from "../locales/fr/translation.json";
|
||||||
|
import koTranslation from "../locales/ko/translation.json";
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
supportedLngs: ["en", "zh", "de", "ptbr", "ru", "fr"],
|
supportedLngs: ["en", "zh", "de", "ptbr", "ru", "fr", "ko"],
|
||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
||||||
@@ -44,6 +45,9 @@ i18n
|
|||||||
fr: {
|
fr: {
|
||||||
translation: frTranslation,
|
translation: frTranslation,
|
||||||
},
|
},
|
||||||
|
ko: {
|
||||||
|
translation: koTranslation,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
interpolation: {
|
interpolation: {
|
||||||
|
|||||||
@@ -799,7 +799,9 @@
|
|||||||
"searchServers": "Server durchsuchen...",
|
"searchServers": "Server durchsuchen...",
|
||||||
"noServerFound": "Kein Server gefunden",
|
"noServerFound": "Kein Server gefunden",
|
||||||
"jumpHostsOrder": "Verbindungen werden in dieser Reihenfolge hergestellt: Jump-Host 1 → Jump-Host 2 → ... → Ziel-Server",
|
"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": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -833,7 +835,11 @@
|
|||||||
"connectionTimeout": "Zeitüberschreitung der Verbindung",
|
"connectionTimeout": "Zeitüberschreitung der Verbindung",
|
||||||
"terminalTitle": "Terminal - {{host}}",
|
"terminalTitle": "Terminal - {{host}}",
|
||||||
"terminalWithPath": "Terminal - {{host}} : {{path}}",
|
"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": {
|
"fileManager": {
|
||||||
"title": "Dateimanager",
|
"title": "Dateimanager",
|
||||||
|
|||||||
@@ -908,7 +908,9 @@
|
|||||||
"quickActionName": "Action name",
|
"quickActionName": "Action name",
|
||||||
"noSnippetFound": "No snippet found",
|
"noSnippetFound": "No snippet found",
|
||||||
"quickActionsOrder": "Quick action buttons will appear in the order listed above on the Server Stats page",
|
"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": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -946,7 +948,11 @@
|
|||||||
"totpRequired": "Two-Factor Authentication Required",
|
"totpRequired": "Two-Factor Authentication Required",
|
||||||
"totpCodeLabel": "Verification Code",
|
"totpCodeLabel": "Verification Code",
|
||||||
"totpPlaceholder": "000000",
|
"totpPlaceholder": "000000",
|
||||||
"totpVerify": "Verify"
|
"totpVerify": "Verify",
|
||||||
|
"sudoPasswordPopupTitle": "Insert Password?",
|
||||||
|
"sudoPasswordPopupHint": "Press Enter to insert, Esc to dismiss",
|
||||||
|
"sudoPasswordPopupConfirm": "Insert",
|
||||||
|
"sudoPasswordPopupDismiss": "Dismiss"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "File Manager",
|
"title": "File Manager",
|
||||||
|
|||||||
@@ -790,7 +790,9 @@
|
|||||||
"searchServers": "Rechercher des serveurs...",
|
"searchServers": "Rechercher des serveurs...",
|
||||||
"noServerFound": "Aucun serveur trouvé",
|
"noServerFound": "Aucun serveur trouvé",
|
||||||
"jumpHostsOrder": "Les connexions seront établies dans l'ordre : Serveur de rebond 1 → Serveur de rebond 2 → ... → Serveur cible",
|
"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": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -828,7 +830,11 @@
|
|||||||
"totpRequired": "Authentification à deux facteurs requise",
|
"totpRequired": "Authentification à deux facteurs requise",
|
||||||
"totpCodeLabel": "Code de vérification",
|
"totpCodeLabel": "Code de vérification",
|
||||||
"totpPlaceholder": "000000",
|
"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": {
|
"fileManager": {
|
||||||
"title": "Gestionnaire de fichiers",
|
"title": "Gestionnaire de fichiers",
|
||||||
|
|||||||
1815
src/locales/ko/translation.json
Normal file
1815
src/locales/ko/translation.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -745,7 +745,9 @@
|
|||||||
"searchServers": "Pesquisar servidores...",
|
"searchServers": "Pesquisar servidores...",
|
||||||
"noServerFound": "Nenhum servidor encontrado",
|
"noServerFound": "Nenhum servidor encontrado",
|
||||||
"jumpHostsOrder": "As conexões serão feitas na ordem: Host de Salto 1 → Host de Salto 2 → ... → Servidor de Destino",
|
"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": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -779,7 +781,11 @@
|
|||||||
"connectionTimeout": "Tempo limite de conexão esgotado",
|
"connectionTimeout": "Tempo limite de conexão esgotado",
|
||||||
"terminalTitle": "Terminal - {{host}}",
|
"terminalTitle": "Terminal - {{host}}",
|
||||||
"terminalWithPath": "Terminal - {{host}}:{{path}}",
|
"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": {
|
"fileManager": {
|
||||||
"title": "Gerenciador de Arquivos",
|
"title": "Gerenciador de Arquivos",
|
||||||
@@ -1034,7 +1040,6 @@
|
|||||||
"fileSavedSuccessfully": "Arquivo salvo com sucesso",
|
"fileSavedSuccessfully": "Arquivo salvo com sucesso",
|
||||||
"autoSaveFailed": "Falha no salvamento automático",
|
"autoSaveFailed": "Falha no salvamento automático",
|
||||||
"fileAutoSaved": "Arquivo salvo automaticamente",
|
"fileAutoSaved": "Arquivo salvo automaticamente",
|
||||||
|
|
||||||
"moveFileFailed": "Falha ao mover {{name}}",
|
"moveFileFailed": "Falha ao mover {{name}}",
|
||||||
"moveOperationFailed": "Falha na operação de mover",
|
"moveOperationFailed": "Falha na operação de mover",
|
||||||
"canOnlyCompareFiles": "Só é possível comparar dois arquivos",
|
"canOnlyCompareFiles": "Só é possível comparar dois arquivos",
|
||||||
|
|||||||
@@ -858,7 +858,9 @@
|
|||||||
"searchServers": "Поиск серверов...",
|
"searchServers": "Поиск серверов...",
|
||||||
"noServerFound": "Сервер не найден",
|
"noServerFound": "Сервер не найден",
|
||||||
"jumpHostsOrder": "Подключения будут выполнены в порядке: Промежуточный хост 1 → Промежуточный хост 2 → ... → Целевой сервер",
|
"jumpHostsOrder": "Подключения будут выполнены в порядке: Промежуточный хост 1 → Промежуточный хост 2 → ... → Целевой сервер",
|
||||||
"advancedAuthSettings": "Расширенные настройки аутентификации"
|
"advancedAuthSettings": "Расширенные настройки аутентификации",
|
||||||
|
"sudoPasswordAutoFill": "Автозаполнение пароля sudo",
|
||||||
|
"sudoPasswordAutoFillDesc": "Показывать всплывающее окно для автоматического ввода пароля при выполнении команд sudo"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Терминал",
|
"title": "Терминал",
|
||||||
@@ -896,7 +898,11 @@
|
|||||||
"totpRequired": "Требуется двухфакторная аутентификация",
|
"totpRequired": "Требуется двухфакторная аутентификация",
|
||||||
"totpCodeLabel": "Код проверки",
|
"totpCodeLabel": "Код проверки",
|
||||||
"totpPlaceholder": "000000",
|
"totpPlaceholder": "000000",
|
||||||
"totpVerify": "Проверить"
|
"totpVerify": "Проверить",
|
||||||
|
"sudoPasswordPopupTitle": "Вставить пароль?",
|
||||||
|
"sudoPasswordPopupHint": "Нажмите Enter для вставки, Esc для отмены",
|
||||||
|
"sudoPasswordPopupConfirm": "Вставить",
|
||||||
|
"sudoPasswordPopupDismiss": "Отмена"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "Файловый менеджер",
|
"title": "Файловый менеджер",
|
||||||
|
|||||||
@@ -893,7 +893,9 @@
|
|||||||
"searchServers": "搜索服务器...",
|
"searchServers": "搜索服务器...",
|
||||||
"noServerFound": "未找到服务器",
|
"noServerFound": "未找到服务器",
|
||||||
"jumpHostsOrder": "连接将按顺序进行:跳板主机 1 → 跳板主机 2 → ... → 目标服务器",
|
"jumpHostsOrder": "连接将按顺序进行:跳板主机 1 → 跳板主机 2 → ... → 目标服务器",
|
||||||
"advancedAuthSettings": "高级身份验证设置"
|
"advancedAuthSettings": "高级身份验证设置",
|
||||||
|
"sudoPasswordAutoFill": "Sudo 密码自动填充",
|
||||||
|
"sudoPasswordAutoFillDesc": "在 sudo 命令时显示弹窗以自动输入密码"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "终端",
|
"title": "终端",
|
||||||
@@ -931,7 +933,11 @@
|
|||||||
"reconnecting": "重新连接中... ({{attempt}}/{{max}})",
|
"reconnecting": "重新连接中... ({{attempt}}/{{max}})",
|
||||||
"reconnected": "重新连接成功",
|
"reconnected": "重新连接成功",
|
||||||
"maxReconnectAttemptsReached": "已达到最大重连尝试次数",
|
"maxReconnectAttemptsReached": "已达到最大重连尝试次数",
|
||||||
"connectionTimeout": "连接超时"
|
"connectionTimeout": "连接超时",
|
||||||
|
"sudoPasswordPopupTitle": "插入密码?",
|
||||||
|
"sudoPasswordPopupHint": "按 Enter 插入,Esc 取消",
|
||||||
|
"sudoPasswordPopupConfirm": "插入",
|
||||||
|
"sudoPasswordPopupDismiss": "取消"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "文件管理器",
|
"title": "文件管理器",
|
||||||
|
|||||||
@@ -119,6 +119,28 @@ export interface Credential {
|
|||||||
updatedAt: string;
|
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 {
|
export interface CredentialData {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@@ -307,6 +329,7 @@ export interface TerminalConfig {
|
|||||||
startupSnippetId: number | null;
|
startupSnippetId: number | null;
|
||||||
autoMosh: boolean;
|
autoMosh: boolean;
|
||||||
moshCommand: string;
|
moshCommand: string;
|
||||||
|
sudoPasswordAutoFill: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -316,13 +339,13 @@ export interface TerminalConfig {
|
|||||||
export interface TabContextTab {
|
export interface TabContextTab {
|
||||||
id: number;
|
id: number;
|
||||||
type:
|
type:
|
||||||
| "home"
|
| "home"
|
||||||
| "terminal"
|
| "terminal"
|
||||||
| "ssh_manager"
|
| "ssh_manager"
|
||||||
| "server"
|
| "server"
|
||||||
| "admin"
|
| "admin"
|
||||||
| "file_manager"
|
| "file_manager"
|
||||||
| "user_profile";
|
| "user_profile";
|
||||||
title: string;
|
title: string;
|
||||||
hostConfig?: SSHHost;
|
hostConfig?: SSHHost;
|
||||||
terminalRef?: any;
|
terminalRef?: any;
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ export function HostManager({
|
|||||||
}
|
}
|
||||||
}, [initialTab]);
|
}, [initialTab]);
|
||||||
|
|
||||||
|
// Update editingHost when hostConfig changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (hostConfig) {
|
||||||
|
setEditingHost(hostConfig);
|
||||||
|
setActiveTab("add_host");
|
||||||
|
lastProcessedHostIdRef.current = hostConfig.id;
|
||||||
|
}
|
||||||
|
}, [hostConfig?.id]);
|
||||||
|
|
||||||
const handleEditHost = (host: SSHHost) => {
|
const handleEditHost = (host: SSHHost) => {
|
||||||
setEditingHost(host);
|
setEditingHost(host);
|
||||||
setActiveTab("add_host");
|
setActiveTab("add_host");
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ export function HostManagerEditor({
|
|||||||
startupSnippetId: z.number().nullable(),
|
startupSnippetId: z.number().nullable(),
|
||||||
autoMosh: z.boolean(),
|
autoMosh: z.boolean(),
|
||||||
moshCommand: z.string(),
|
moshCommand: z.string(),
|
||||||
|
sudoPasswordAutoFill: z.boolean(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
forceKeyboardInteractive: z.boolean().optional(),
|
forceKeyboardInteractive: z.boolean().optional(),
|
||||||
@@ -631,7 +632,7 @@ export function HostManagerEditor({
|
|||||||
type FormData = z.infer<typeof formSchema>;
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema) as any,
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
ip: "",
|
ip: "",
|
||||||
@@ -2443,6 +2444,29 @@ export function HostManagerEditor({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="terminalConfig.sudoPasswordAutoFill"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.sudoPasswordAutoFill")}
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
{t("hosts.sudoPasswordAutoFillDesc")}
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">
|
<label className="text-sm font-medium">
|
||||||
Environment Variables
|
Environment Variables
|
||||||
|
|||||||
82
src/ui/desktop/apps/terminal/SudoPasswordPopup.tsx
Normal file
82
src/ui/desktop/apps/terminal/SudoPasswordPopup.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { KeyRound } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button.tsx";
|
||||||
|
|
||||||
|
interface SudoPasswordPopupProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
hostPassword: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
onConfirm: (password: string) => void;
|
||||||
|
onDismiss: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SudoPasswordPopup({
|
||||||
|
isOpen,
|
||||||
|
hostPassword,
|
||||||
|
backgroundColor,
|
||||||
|
onConfirm,
|
||||||
|
onDismiss
|
||||||
|
}: SudoPasswordPopupProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
onConfirm(hostPassword);
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown, true);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown, true);
|
||||||
|
}, [isOpen, onConfirm, onDismiss, hostPassword]);
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute bottom-4 right-4 z-50 backdrop-blur-sm border border-border rounded-lg shadow-lg p-4 min-w-[280px]"
|
||||||
|
style={{ backgroundColor: backgroundColor }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3 mb-3">
|
||||||
|
<div className="p-2 bg-primary/10 rounded-full">
|
||||||
|
<KeyRound className="h-5 w-5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-sm">
|
||||||
|
{t("terminal.sudoPasswordPopupTitle", "Insert password?")}
|
||||||
|
</h4>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{t("terminal.sudoPasswordPopupHint", "Press Enter to insert, Esc to dismiss")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 justify-end">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={onDismiss}
|
||||||
|
>
|
||||||
|
{t("terminal.sudoPasswordPopupDismiss", "Dismiss")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => onConfirm(hostPassword)}
|
||||||
|
>
|
||||||
|
{t("terminal.sudoPasswordPopupConfirm", "Insert")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ const languages = [
|
|||||||
{ code: "ru", name: "Russian", nativeName: "Русский" },
|
{ code: "ru", name: "Russian", nativeName: "Русский" },
|
||||||
{ code: "fr", name: "French", nativeName: "Français" },
|
{ code: "fr", name: "French", nativeName: "Français" },
|
||||||
{ code: "it", name: "Italian", nativeName: "Italiano" },
|
{ code: "it", name: "Italian", nativeName: "Italiano" },
|
||||||
|
{ code: "ko", name: "Korean", nativeName: "한국어" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function LanguageSwitcher() {
|
export function LanguageSwitcher() {
|
||||||
|
|||||||
Reference in New Issue
Block a user