4 Commits

Author SHA1 Message Date
renovate[bot]
8c914aea9f Update dependency typescript to v5.9.3 2025-11-08 01:56:18 +00:00
Suyunmeng
e4ab2184eb fix(data): fix data directory errors and log display issues 2025-11-08 09:53:57 +08:00
Suyunjing
4c76f31885 chore(ci): update ci configurations 2025-10-30 15:48:58 +08:00
Yinan Qin
68411aaaf3 fix!: update log and data dirs for linux and windows (#108)
* fix: update log dir for linux and windows

Signed-off-by: Yinan Qin <39023210+elysia-best@users.noreply.github.com>

* fix: update log dirs for linux and windows

Signed-off-by: Yinan Qin <39023210+elysia-best@users.noreply.github.com>

* chore: refactor APPDATA variable retrieval for clarity

Signed-off-by: Yinan Qin <39023210+elysia-best@users.noreply.github.com>

* fix: update version to 0.7.0 and improve data directory handling

* fix: fix clippy error

---------

Signed-off-by: Yinan Qin <39023210+elysia-best@users.noreply.github.com>
2025-10-27 11:06:31 +08:00
11 changed files with 1404 additions and 844 deletions

View File

@@ -0,0 +1,230 @@
# Connect-SimplySign-Enhanced.ps1
# Registry-Enhanced TOTP Authentication for SimplySign Desktop
# Uses registry pre-configuration + TOTP credential injection approach
param(
[string]$OtpUri = $env:CERTUM_OTP_URI,
[string]$UserId = $env:CERTUM_USERNAME,
[string]$ExePath = $env:CERTUM_EXE_PATH
)
# Validate required parameters
if (-not $OtpUri) {
Write-Host "ERROR: CERTUM_OTP_URI environment variable not provided"
exit 1
}
if (-not $UserId) {
Write-Host "ERROR: CERTUM_USERNAME environment variable not provided"
exit 1
}
if (-not $ExePath) {
$ExePath = "C:\Program Files\Certum\SimplySign Desktop\SimplySignDesktop.exe"
}
Write-Host "=== REGISTRY-ENHANCED TOTP AUTHENTICATION ==="
Write-Host "Using registry pre-configuration + credential injection"
Write-Host "OTP URI provided (length: $($OtpUri.Length))"
Write-Host "User ID: $UserId"
Write-Host "Executable: $ExePath"
Write-Host ""
# Verify SimplySign Desktop exists
if (-not (Test-Path $ExePath)) {
Write-Host "ERROR: SimplySign Desktop not found at: $ExePath"
exit 1
}
# Parse the otpauth:// URI
$uri = [Uri]$OtpUri
# Parse query parameters (compatible with both PowerShell 5.1 and 7+)
try {
$q = [System.Web.HttpUtility]::ParseQueryString($uri.Query)
} catch {
$q = @{}
foreach ($part in $uri.Query.TrimStart('?') -split '&') {
$kv = $part -split '=', 2
if ($kv.Count -eq 2) {
$q[$kv[0]] = [Uri]::UnescapeDataString($kv[1])
}
}
}
$Base32 = $q['secret']
$Digits = if ($q['digits']) { [int]$q['digits'] } else { 6 }
$Period = if ($q['period']) { [int]$q['period'] } else { 30 }
$Algorithm = if ($q['algorithm']) { $q['algorithm'].ToUpper() } else { 'SHA256' }
# Validate supported algorithms
$SupportedAlgorithms = @('SHA1', 'SHA256', 'SHA512')
if ($Algorithm -notin $SupportedAlgorithms) {
Write-Host "ERROR: Unsupported algorithm: $Algorithm. Supported: $($SupportedAlgorithms -join ', ')"
exit 1
}
# TOTP Generator (inline C# implementation)
Add-Type -Language CSharp @"
using System;
using System.Security.Cryptography;
public static class Totp
{
private const string B32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
private static byte[] Base32Decode(string s)
{
s = s.TrimEnd('=').ToUpperInvariant();
int byteCount = s.Length * 5 / 8;
byte[] bytes = new byte[byteCount];
int bitBuffer = 0, bitsLeft = 0, idx = 0;
foreach (char c in s)
{
int val = B32.IndexOf(c);
if (val < 0) throw new ArgumentException("Invalid Base32 char: " + c);
bitBuffer = (bitBuffer << 5) | val;
bitsLeft += 5;
if (bitsLeft >= 8)
{
bytes[idx++] = (byte)(bitBuffer >> (bitsLeft - 8));
bitsLeft -= 8;
}
}
return bytes;
}
private static HMAC GetHmacAlgorithm(string algorithm, byte[] key)
{
switch (algorithm.ToUpper())
{
case "SHA1":
return new HMACSHA1(key);
case "SHA256":
return new HMACSHA256(key);
case "SHA512":
return new HMACSHA512(key);
default:
throw new ArgumentException("Unsupported algorithm: " + algorithm);
}
}
public static string Now(string secret, int digits, int period, string algorithm = "SHA256")
{
byte[] key = Base32Decode(secret);
long counter = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / period;
byte[] cnt = BitConverter.GetBytes(counter);
if (BitConverter.IsLittleEndian) Array.Reverse(cnt);
byte[] hash;
using (var hmac = GetHmacAlgorithm(algorithm, key))
{
hash = hmac.ComputeHash(cnt);
}
int offset = hash[hash.Length - 1] & 0x0F;
int binary =
((hash[offset] & 0x7F) << 24) |
((hash[offset + 1] & 0xFF) << 16) |
((hash[offset + 2] & 0xFF) << 8) |
(hash[offset + 3] & 0xFF);
int otp = binary % (int)Math.Pow(10, digits);
return otp.ToString(new string('0', digits));
}
}
"@
function Get-TotpCode {
param([string]$Secret, [int]$Digits = 6, [int]$Period = 30, [string]$Algorithm = 'SHA256')
[Totp]::Now($Secret, $Digits, $Period, $Algorithm)
}
# Generate current TOTP code
$otp = Get-TotpCode -Secret $Base32 -Digits $Digits -Period $Period -Algorithm $Algorithm
Write-Host "Generated TOTP: $otp (using $Algorithm algorithm)"
Write-Host ""
# Launch SimplySign Desktop (registry should auto-open login dialog)
Write-Host "Launching SimplySign Desktop..."
Write-Host "Registry pre-configuration should auto-open login dialog"
$proc = Start-Process -FilePath $ExePath -PassThru
Write-Host "Process started with ID: $($proc.Id)"
Write-Host ""
# Wait for the application to initialize
Write-Host "Waiting for SimplySign Desktop to initialize..."
Start-Sleep -Seconds 3
# Create WScript.Shell for window interaction
$wshell = New-Object -ComObject WScript.Shell
# Try to focus the SimplySign Desktop window
Write-Host "Attempting to focus SimplySign Desktop window..."
$focused = $false
# Method 1: Focus by process ID (most reliable)
$focused = $wshell.AppActivate($proc.Id)
# Method 2: Focus by window title (fallback)
if (-not $focused) {
$focused = $wshell.AppActivate('SimplySign Desktop')
}
# Method 3: Multiple attempts with slight delays
for ($i = 0; (-not $focused) -and ($i -lt 10); $i++) {
Start-Sleep -Milliseconds 500
$focused = $wshell.AppActivate($proc.Id) -or $wshell.AppActivate('SimplySign Desktop')
Write-Host "Focus attempt $($i + 1): $focused"
}
if (-not $focused) {
Write-Host "ERROR: Could not bring SimplySign Desktop to foreground"
Write-Host "Login dialog may not be visible for credential injection"
exit 1
}
Write-Host "Successfully focused SimplySign Desktop window"
Write-Host ""
# Small delay to ensure window is ready for input
Start-Sleep -Milliseconds 400
# Inject credentials: Username + TAB + TOTP + ENTER
Write-Host "Injecting credentials into login dialog..."
Write-Host "Sending: Username -> TAB -> TOTP -> ENTER"
# Send the credential sequence
$wshell.SendKeys($UserId)
Start-Sleep -Milliseconds 200
$wshell.SendKeys("{TAB}")
Start-Sleep -Milliseconds 200
$wshell.SendKeys($otp)
Start-Sleep -Milliseconds 200
$wshell.SendKeys("{ENTER}")
Write-Host "Credentials injected successfully"
Write-Host ""
# Wait for authentication to process
Write-Host "Waiting for authentication to complete..."
Start-Sleep -Seconds 5
# Verify SimplySign Desktop is still running
$stillRunning = Get-Process -Id $proc.Id -ErrorAction SilentlyContinue
if ($stillRunning) {
Write-Host "SUCCESS: SimplySign Desktop is running"
Write-Host "Authentication should be complete"
Write-Host "Cloud certificate should now be available"
} else {
Write-Host "WARNING: SimplySign Desktop process has exited"
Write-Host "This may indicate authentication failure"
}
Write-Host ""
Write-Host "=== TOTP AUTHENTICATION COMPLETE ==="
Write-Host "Registry pre-configuration + credential injection finished"

View File

@@ -0,0 +1,252 @@
param(
[switch]$DebugMode = $false,
[switch]$VerifyOnly = $false
)
# SimplySign Desktop Registry Configuration Script
# Pre-configures optimal registry settings for automated login dialog display
Write-Host "=== SimplySign Desktop Registry Configuration ==="
if ($DebugMode) {
Write-Host "Debug mode enabled - verbose logging active"
}
# Registry path for SimplySign Desktop settings
$RegistryPath = "HKCU:\Software\Certum\SimplySign"
# Optimal configuration values for automation
$OptimalSettings = @{
"ShowLoginDialogOnStart" = 1
"ShowLoginDialogOnAppRequest" = 1
"RememberLastUserName" = 1
"Autostart" = 0
"UnregisterCertificatesOnDisconnect" = 0
"RememberPINinCSP" = 1
"ForgetPINinCSPonDisconnect" = 1
"LangID" = 9
}
# Function to check if registry path exists
function Test-RegistryPath {
param([string]$Path)
try {
$null = Get-Item -Path $Path -ErrorAction Stop
return $true
} catch {
return $false
}
}
# Function to get current registry value
function Get-RegistryValue {
param(
[string]$Path,
[string]$Name
)
try {
$value = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop
return $value.$Name
} catch {
return $null
}
}
# Function to set registry value safely
function Set-RegistryValue {
param(
[string]$Path,
[string]$Name,
[int]$Value
)
try {
Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type DWord -ErrorAction Stop
if ($DebugMode) {
Write-Host " Set $Name = $Value"
}
return $true
} catch {
Write-Host " ERROR: Failed to set $Name = $Value - $($_.Exception.Message)"
return $false
}
}
# Function to display current settings
function Show-CurrentSettings {
Write-Host "Current SimplySign Desktop registry settings:"
Write-Host "============================================="
if (-not (Test-RegistryPath $RegistryPath)) {
Write-Host "Registry path does not exist: $RegistryPath"
return
}
foreach ($setting in $OptimalSettings.Keys) {
$currentValue = Get-RegistryValue -Path $RegistryPath -Name $setting
if ($null -eq $currentValue) {
Write-Host " $setting : NOT SET"
} else {
Write-Host " $setting : $currentValue"
}
}
Write-Host ""
}
# Function to create registry structure
function Initialize-RegistryStructure {
Write-Host "Initializing registry structure..."
# Create parent keys if they don't exist
$ParentPaths = @(
"HKCU:\Software\Certum",
$RegistryPath
)
$allCreated = $true
foreach ($path in $ParentPaths) {
if (-not (Test-RegistryPath $path)) {
try {
New-Item -Path $path -Force -ErrorAction Stop | Out-Null
if ($DebugMode) {
Write-Host " Created registry path: $path"
}
} catch {
Write-Host " ERROR: Failed to create registry path: $path - $($_.Exception.Message)"
$allCreated = $false
}
} else {
if ($DebugMode) {
Write-Host " Registry path exists: $path"
}
}
}
return $allCreated
}
# Function to apply optimal configuration
function Set-OptimalConfiguration {
Write-Host "Applying optimal configuration for automation..."
$successCount = 0
$totalSettings = $OptimalSettings.Count
foreach ($setting in $OptimalSettings.Keys) {
$value = $OptimalSettings[$setting]
if (Set-RegistryValue -Path $RegistryPath -Name $setting -Value $value) {
$successCount++
}
}
Write-Host "Applied $successCount of $totalSettings settings successfully"
return ($successCount -eq $totalSettings)
}
# Function to verify configuration
function Test-Configuration {
Write-Host "Verifying configuration..."
$verificationResults = @{}
$allCorrect = $true
foreach ($setting in $OptimalSettings.Keys) {
$expectedValue = $OptimalSettings[$setting]
$actualValue = Get-RegistryValue -Path $RegistryPath -Name $setting
$isCorrect = ($actualValue -eq $expectedValue)
$verificationResults[$setting] = @{
Expected = $expectedValue
Actual = $actualValue
Correct = $isCorrect
}
if (-not $isCorrect) {
$allCorrect = $false
}
if ($DebugMode -or -not $isCorrect) {
$status = if ($isCorrect) { "OK" } else { "MISMATCH" }
Write-Host " $setting : Expected=$expectedValue, Actual=$actualValue [$status]"
}
}
return $verificationResults, $allCorrect
}
# Main execution
try {
Write-Host "Starting registry configuration process..."
Write-Host ""
# Show current state
Write-Host "BEFORE CONFIGURATION:"
Show-CurrentSettings
if ($VerifyOnly) {
Write-Host "Verification-only mode - no changes will be made"
$verificationResults, $allCorrect = Test-Configuration
if ($allCorrect) {
Write-Host "SUCCESS: All settings are correctly configured"
exit 0
} else {
Write-Host "CONFIGURATION NEEDED: Some settings require adjustment"
exit 1
}
}
# Initialize registry structure
if (-not (Initialize-RegistryStructure)) {
Write-Host "FATAL ERROR: Failed to initialize registry structure"
exit 1
}
# Apply optimal configuration
if (-not (Set-OptimalConfiguration)) {
Write-Host "ERROR: Failed to apply complete configuration"
exit 1
}
Write-Host ""
Write-Host "AFTER CONFIGURATION:"
Show-CurrentSettings
# Verify the configuration was applied correctly
$verificationResults, $allCorrect = Test-Configuration
if ($allCorrect) {
Write-Host "SUCCESS: Registry configuration completed successfully"
Write-Host ""
Write-Host "Key automation settings enabled:"
Write-Host " ShowLoginDialogOnStart = 1 (Login dialog will appear automatically)"
Write-Host " ShowLoginDialogOnAppRequest = 1 (Dialog appears when apps request access)"
Write-Host " RememberLastUserName = 1 (Username persistence for efficiency)"
Write-Host ""
Write-Host "Next steps:"
Write-Host "1. Launch SimplySign Desktop"
Write-Host "2. Login dialog should appear automatically"
Write-Host "3. Complete authentication process"
# Create a status file for the workflow to check
"REGISTRY_CONFIGURATION_SUCCESS" | Out-File -FilePath "registry_config_status.log" -Encoding UTF8
exit 0
} else {
Write-Host "ERROR: Configuration verification failed"
Write-Host "Some settings were not applied correctly"
"REGISTRY_CONFIGURATION_PARTIAL" | Out-File -FilePath "registry_config_status.log" -Encoding UTF8
exit 1
}
} catch {
Write-Host "FATAL ERROR: Registry configuration failed - $($_.Exception.Message)"
"REGISTRY_CONFIGURATION_FAILED" | Out-File -FilePath "registry_config_status.log" -Encoding UTF8
exit 1
}

112
.github/scripts/install-simplysign.sh vendored Normal file
View File

@@ -0,0 +1,112 @@
#!/bin/bash
# Install SimplySign Desktop - Clean MSI Installation
set -euo pipefail
echo "=== INSTALLING SIMPLYSIGN DESKTOP ==="
echo "Using proven installation method from successful testing..."
# Download SimplySign Desktop MSI
CERTUM_INSTALLER="SimplySignDesktop.msi"
echo "Downloading SimplySign Desktop MSI..."
if curl -L "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.2.67/SimplySignDesktop-9.3.2.67-64-bit-en.msi" -o "$CERTUM_INSTALLER" --fail --max-time 60; then
echo "✅ Downloaded SimplySign Desktop MSI ($(ls -lh "$CERTUM_INSTALLER" | awk '{print $5}'))"
else
echo "❌ Failed to download SimplySign Desktop"
exit 1
fi
# Install with proven method (matching successful test)
echo "Installing SimplySign Desktop..."
echo "Full command: msiexec /i \"$CERTUM_INSTALLER\" /quiet /norestart /l*v install.log ALLUSERS=1 REBOOT=ReallySuppress"
# Check for administrative privileges (like the successful test)
ADMIN_RIGHTS=false
if powershell -Command "([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)" 2>/dev/null; then
echo "✅ Running with administrative privileges"
ADMIN_RIGHTS=true
else
echo "⚠️ No explicit administrative privileges detected"
fi
# Use the exact method that worked: PowerShell with admin privileges
if [ "$ADMIN_RIGHTS" = true ]; then
echo "Running MSI installation with administrator privileges..."
powershell -Command "Start-Process -FilePath 'msiexec.exe' -ArgumentList '/i', '\"$CERTUM_INSTALLER\"', '/quiet', '/norestart', '/l*v', 'install.log', 'ALLUSERS=1', 'REBOOT=ReallySuppress' -Wait -NoNewWindow -PassThru" &
INSTALL_PID=$!
else
echo "Running MSI installation without explicit admin elevation..."
timeout 300 msiexec /i "$CERTUM_INSTALLER" /quiet /norestart /l*v install.log ALLUSERS=1 REBOOT=ReallySuppress &
INSTALL_PID=$!
fi
# Monitor with the same logic as successful test
echo "Monitoring installation progress..."
INSTALL_START_TIME=$(date +%s)
sleep 10
# Check if msiexec process is actually running (like successful test)
if kill -0 $INSTALL_PID 2>/dev/null; then
echo "MSI installation process is running (PID: $INSTALL_PID)"
# Monitor for up to 3 minutes with status updates
for i in {1..18}; do
sleep 10
CURRENT_TIME=$(date +%s)
ELAPSED=$((CURRENT_TIME - INSTALL_START_TIME))
if kill -0 $INSTALL_PID 2>/dev/null; then
echo "Installation still running after ${ELAPSED} seconds..."
# Check log file growth
if [ -f "install.log" ]; then
LOG_SIZE=$(stat -c%s "install.log" 2>/dev/null || stat -f%z "install.log" 2>/dev/null || echo 0)
echo " Log file size: $LOG_SIZE bytes"
fi
else
echo "MSI installation completed after ${ELAPSED} seconds"
break
fi
done
# Final wait if still running
if kill -0 $INSTALL_PID 2>/dev/null; then
echo "Installation taking longer, waiting for completion..."
wait $INSTALL_PID 2>/dev/null || echo "Installation process ended"
fi
else
echo "MSI installation process ended quickly"
fi
# Quick success check using proven patterns
INSTALLATION_SUCCESSFUL=false
if [ -f "install.log" ]; then
if grep -qi "Installation.*operation.*completed.*successfully\|Installation.*success.*or.*error.*status.*0\|MainEngineThread.*is.*returning.*0\|Windows.*Installer.*installed.*the.*product" install.log 2>/dev/null; then
echo "✅ Installation successful (confirmed by log patterns)"
INSTALLATION_SUCCESSFUL=true
fi
fi
# Verify installation directory
INSTALL_PATH="/c/Program Files/Certum/SimplySign Desktop"
if [ -d "$INSTALL_PATH" ]; then
echo "✅ SimplySign Desktop installed successfully"
echo "✅ Virtual card emulation now active for code signing"
INSTALLATION_SUCCESSFUL=true
# Set output for GitHub Actions
if [ -n "${GITHUB_OUTPUT:-}" ]; then
echo "SIMPLYSIGN_PATH=$INSTALL_PATH" >> "$GITHUB_OUTPUT"
fi
fi
if [ "$INSTALLATION_SUCCESSFUL" = false ]; then
echo "❌ Installation verification failed"
echo "Last 10 lines of install log:"
tail -10 install.log 2>/dev/null || echo "No install log available"
exit 1
fi
echo "🎉 SimplySign Desktop installation completed successfully!"

View File

@@ -21,6 +21,10 @@ env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Certum cloud code signing for Windows
CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }}
CERTUM_USERNAME: ${{ secrets.CERTUM_USERNAME }}
CERTUM_CERTIFICATE_SHA1: ${{ secrets.CERTUM_CERTIFICATE_SHA1 }}
concurrency:
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
@@ -174,43 +178,64 @@ jobs:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Import Windows certificate
if: matrix.os == 'windows-latest' && env.WINDOWS_CERTIFICATE != ''
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
- name: Setup Certum Code Signing (Windows)
if: matrix.platform == 'windows'
run: |
if ($env:WINDOWS_CERTIFICATE) {
New-Item -ItemType directory -Path certificate
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
}
echo "=== SETTING UP CERTUM CODE SIGNING FOR WINDOWS ==="
echo "Installing SimplySign Desktop and configuring for automatic authentication"
# Install SimplySign Desktop
chmod +x ./.github/scripts/install-simplysign.sh
./.github/scripts/install-simplysign.sh
# Configure registry for auto-login dialog
echo "Configuring registry for automatic login dialog..."
powershell -ExecutionPolicy Bypass -File "./.github/scripts/configure-simplysign-registry.ps1"
echo "Certum signing environment ready"
shell: bash
- name: Authenticate Certum (Windows)
if: matrix.platform == 'windows'
env:
CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }}
CERTUM_USERNAME: ${{ secrets.CERTUM_USERNAME }}
run: |
echo "=== CERTUM AUTHENTICATION ==="
echo "Authenticating with Certum cloud certificate using TOTP"
# Authenticate with Certum using our enhanced script
powershell -ExecutionPolicy Bypass -File "./.github/scripts/Connect-SimplySign-Enhanced.ps1"
echo "Authentication completed"
shell: bash
- name: Configure Certum Certificate Thumbprint (Windows)
if: matrix.platform == 'windows'
shell: bash
run: |
echo "=== CONFIGURING CERTUM CERTIFICATE THUMBPRINT ==="
CONFIG_PATH="src-tauri/tauri.windows.conf.json"
THUMBPRINT="${{ secrets.CERTUM_CERTIFICATE_SHA1 }}"
# Update the certificateThumbprint field using jq
jq --arg thumbprint "$THUMBPRINT" '.bundle.windows.certificateThumbprint = $thumbprint' "$CONFIG_PATH" > tmp.$$ && mv tmp.$$ "$CONFIG_PATH"
echo "Certificate thumbprint configured: $THUMBPRINT"
- name: Build the app
if: matrix.platform == 'windows' || matrix.platform == 'linux'
run: |
yarn build --target ${{ matrix.target }}
uses: tauri-apps/tauri-action@v0
env:
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
# macOS signing and notarization environment variables
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
with:
args: --target ${{ matrix.target }}
- name: Build the app (macOS)
uses: tauri-apps/tauri-action@v0
if: matrix.platform == 'macos'
run: |
export TAURI_SKIP_SIDECAR_SIGNATURE_CHECK=true
yarn build --target ${{ matrix.target }}
env:
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -222,8 +247,9 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
TAURI_SKIP_SIDECAR_SIGNATURE_CHECK: "true"
with:
args: --target ${{ matrix.target }}
- name: Upload artifacts
uses: actions/upload-artifact@v4

View File

@@ -26,6 +26,10 @@ env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Certum cloud code signing for Windows
CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }}
CERTUM_USERNAME: ${{ secrets.CERTUM_USERNAME }}
CERTUM_CERTIFICATE_SHA1: ${{ secrets.CERTUM_CERTIFICATE_SHA1 }}
PERSONAL_GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
concurrency:
group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}"
@@ -396,17 +400,50 @@ jobs:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: import windows certificate
- name: Setup Certum Code Signing (Windows)
if: matrix.os == 'windows-latest'
run: |
echo "=== SETTING UP CERTUM CODE SIGNING FOR WINDOWS ==="
echo "Installing SimplySign Desktop and configuring for automatic authentication"
# Install SimplySign Desktop
chmod +x ./.github/scripts/install-simplysign.sh
./.github/scripts/install-simplysign.sh
# Configure registry for auto-login dialog
echo "Configuring registry for automatic login dialog..."
powershell -ExecutionPolicy Bypass -File "./.github/scripts/configure-simplysign-registry.ps1"
echo "Certum signing environment ready"
shell: bash
- name: Authenticate Certum (Windows)
if: matrix.os == 'windows-latest'
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
CERTUM_OTP_URI: ${{ secrets.CERTUM_OTP_URI }}
CERTUM_USERNAME: ${{ secrets.CERTUM_USERNAME }}
run: |
New-Item -ItemType directory -Path certificate
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
Remove-Item -path certificate -include tempCert.txt
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
echo "=== CERTUM AUTHENTICATION ==="
echo "Authenticating with Certum cloud certificate using TOTP"
# Authenticate with Certum using our enhanced script
powershell -ExecutionPolicy Bypass -File "./.github/scripts/Connect-SimplySign-Enhanced.ps1"
echo "Authentication completed"
shell: bash
- name: Configure Certum Certificate Thumbprint (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
echo "=== CONFIGURING CERTUM CERTIFICATE THUMBPRINT ==="
CONFIG_PATH="src-tauri/tauri.windows.conf.json"
THUMBPRINT="${{ secrets.CERTUM_CERTIFICATE_SHA1 }}"
# Update the certificateThumbprint field using jq
jq --arg thumbprint "$THUMBPRINT" '.bundle.windows.certificateThumbprint = $thumbprint' "$CONFIG_PATH" > tmp.$$ && mv tmp.$$ "$CONFIG_PATH"
echo "Certificate thumbprint configured: $THUMBPRINT"
- name: Build the app
uses: tauri-apps/tauri-action@v0
@@ -423,8 +460,6 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Enable signing and notarization for macOS
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
with:
tagName: ${{ needs.changelog.outputs.tag }}
releaseName: 'OpenList Desktop ${{ needs.changelog.outputs.tag }}'

View File

@@ -98,8 +98,11 @@
"tar": "^7.4.3",
"typescript": "^5.8.3",
"typescript-eslint": "^8.41.0",
"vite": "^7.1.3",
"vite": "^7.1.11",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.0.6"
},
"overrides": {
"tmp": "^0.2.4"
}
}

2
src-tauri/Cargo.lock generated
View File

@@ -2940,7 +2940,7 @@ dependencies = [
[[package]]
name = "openlist-desktop"
version = "0.6.1"
version = "0.7.0"
dependencies = [
"anyhow",
"base64 0.22.1",

View File

@@ -114,7 +114,7 @@ async fn is_rclone_running() -> bool {
let mut system = System::new_all();
system.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
for (_pid, process) in system.processes() {
for process in system.processes().values() {
let process_name = process.name().to_string_lossy().to_lowercase();
if process_name.contains("rclone") {

View File

@@ -3,6 +3,31 @@ use std::{env, fs};
pub static APP_ID: &str = "io.github.openlistteam.openlist.desktop";
// Normalize path without Windows long path prefix (\\?\)
// The \\?\ prefix breaks compatibility with some applications like SQLite
fn normalize_path(path: &PathBuf) -> Result<PathBuf, String> {
#[cfg(target_os = "windows")]
{
// On Windows, use canonicalize but strip the \\?\ prefix if present
let canonical = path
.canonicalize()
.map_err(|e| format!("Failed to canonicalize path: {e}"))?;
let path_str = canonical.to_string_lossy();
if let Some(stripped) = path_str.strip_prefix(r"\\?\") {
Ok(PathBuf::from(stripped))
} else {
Ok(canonical)
}
}
#[cfg(not(target_os = "windows"))]
{
path.canonicalize()
.map_err(|e| format!("Failed to canonicalize path: {e}"))
}
}
fn get_app_dir() -> Result<PathBuf, String> {
let app_dir = env::current_exe()
.map_err(|e| format!("Failed to get current exe path: {e}"))?
@@ -17,43 +42,57 @@ fn get_app_dir() -> Result<PathBuf, String> {
}
fn get_user_data_dir() -> Result<PathBuf, String> {
#[cfg(target_os = "macos")]
{
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
let data_dir = PathBuf::from(home)
.join("Library")
.join("Application Support")
.join("OpenList Desktop");
fs::create_dir_all(&data_dir)
.map_err(|e| format!("Failed to create data directory: {e}"))?;
Ok(data_dir)
}
let data_dir = {
#[cfg(target_os = "macos")]
{
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
PathBuf::from(home)
.join("Library")
.join("Application Support")
.join("OpenList Desktop")
}
#[cfg(not(target_os = "macos"))]
{
get_app_dir()
}
#[cfg(target_os = "linux")]
{
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
PathBuf::from(home)
.join(".local")
.join("share")
.join("OpenList Desktop")
}
#[cfg(target_os = "windows")]
{
let appdata =
env::var("APPDATA").map_err(|_| "Failed to get APPDATA environment variable")?;
PathBuf::from(appdata).join("OpenList Desktop")
}
};
fs::create_dir_all(&data_dir).map_err(|e| format!("Failed to create data directory: {e}"))?;
normalize_path(&data_dir)
}
fn get_user_logs_dir() -> Result<PathBuf, String> {
#[cfg(target_os = "macos")]
{
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
let logs_dir = PathBuf::from(home)
.join("Library")
.join("Logs")
.join("OpenList Desktop");
fs::create_dir_all(&logs_dir)
.map_err(|e| format!("Failed to create logs directory: {e}"))?;
Ok(logs_dir)
}
let logs_dir = {
#[cfg(target_os = "macos")]
{
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
PathBuf::from(home)
.join("Library")
.join("Logs")
.join("OpenList Desktop")
}
#[cfg(not(target_os = "macos"))]
{
let logs = get_app_dir()?.join("logs");
fs::create_dir_all(&logs).map_err(|e| e.to_string())?;
Ok(logs)
}
#[cfg(not(target_os = "macos"))]
{
get_user_data_dir()?.join("logs")
}
};
fs::create_dir_all(&logs_dir).map_err(|e| format!("Failed to create logs directory: {e}"))?;
normalize_path(&logs_dir)
}
fn get_binary_path(binary: &str, service_name: &str) -> Result<PathBuf, String> {
@@ -96,15 +135,7 @@ pub fn get_rclone_config_path() -> Result<PathBuf, String> {
}
pub fn get_default_openlist_data_dir() -> Result<PathBuf, String> {
#[cfg(target_os = "macos")]
{
Ok(get_user_data_dir()?.join("data"))
}
#[cfg(not(target_os = "macos"))]
{
Ok(get_app_dir()?.join("data"))
}
Ok(get_user_data_dir()?.join("data"))
}
pub fn get_service_log_path() -> Result<PathBuf, String> {
@@ -113,16 +144,14 @@ pub fn get_service_log_path() -> Result<PathBuf, String> {
let home = env::var("HOME").map_err(|_| "Failed to get HOME environment variable")?;
let logs = PathBuf::from(home)
.join("Library")
.join("Application Support")
.join("io.github.openlistteam.openlist.service.bundle")
.join("Contents")
.join("MacOS")
.join("Logs")
.join("OpenList Desktop")
.join("openlist-desktop-service.log");
Ok(logs)
}
#[cfg(not(target_os = "macos"))]
{
Ok(get_app_dir()?.join("openlist-desktop-service.log"))
Ok(get_app_logs_dir()?.join("openlist-desktop-service.log"))
}
}

View File

@@ -4,9 +4,9 @@
"bundle": {
"targets": ["nsis"],
"windows": {
"certificateThumbprint": "209114AD26E9B9B5788E4E9F6E522DFE8E4FABAD",
"certificateThumbprint": "",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.comodoca.com",
"timestampUrl": "http://time.certum.pl",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true

1383
yarn.lock

File diff suppressed because it is too large Load Diff