diff --git a/build/pkg-scripts/postinstall b/build/pkg-scripts/postinstall index 83e3afd..c9b3bf5 100644 --- a/build/pkg-scripts/postinstall +++ b/build/pkg-scripts/postinstall @@ -151,4 +151,23 @@ else fi log "Installation completed successfully" + +# Fix user data directory permissions +log "Fixing user data directory permissions..." +for user_home in /Users/*; do + if [ -d "$user_home" ] && [ "$(basename "$user_home")" != "Shared" ] && [ "$(basename "$user_home")" != ".localized" ]; then + username=$(basename "$user_home") + user_data_dir="$user_home/Library/Application Support/mihomo-party" + + if [ -d "$user_data_dir" ]; then + current_owner=$(stat -f "%Su" "$user_data_dir" 2>/dev/null || echo "unknown") + if [ "$current_owner" = "root" ]; then + log "Fixing ownership for user: $username" + chown -R "$username:staff" "$user_data_dir" 2>/dev/null || true + chmod -R u+rwX "$user_data_dir" 2>/dev/null || true + fi + fi + fi +done + exit 0 diff --git a/src/main/index.ts b/src/main/index.ts index e7287a6..93dada2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -10,8 +10,10 @@ import { createTray, hideDockIcon, showDockIcon } from './resolve/tray' import { init } from './utils/init' import { join } from 'path' import { initShortcut } from './resolve/shortcut' -import { execSync, spawn } from 'child_process' +import { execSync, spawn, exec } from 'child_process' import { createElevateTask } from './sys/misc' +import { promisify } from 'util' +import { stat } from 'fs/promises' import { initProfileUpdater } from './core/profileUpdater' import { existsSync, writeFileSync } from 'fs' import { exePath, taskDir } from './utils/dirs' @@ -22,6 +24,29 @@ import iconv from 'iconv-lite' import { initI18n } from '../shared/i18n' import i18next from 'i18next' +async function fixUserDataPermissions(): Promise { + if (process.platform !== 'darwin') return + + const userDataPath = app.getPath('userData') + if (!existsSync(userDataPath)) return + + try { + const stats = await stat(userDataPath) + const currentUid = process.getuid?.() || 0 + + if (stats.uid === 0 && currentUid !== 0) { + const execPromise = promisify(exec) + const username = process.env.USER || process.env.LOGNAME + if (username) { + await execPromise(`chown -R "${username}:staff" "${userDataPath}"`) + await execPromise(`chmod -R u+rwX "${userDataPath}"`) + } + } + } catch { + // ignore + } +} + let quitTimeout: NodeJS.Timeout | null = null export let mainWindow: BrowserWindow | null = null @@ -59,12 +84,27 @@ if (process.platform === 'win32' && !is.dev && !process.argv.includes('noadmin') } } -const gotTheLock = app.requestSingleInstanceLock() - -if (!gotTheLock) { - app.quit() +async function initApp(): Promise { + await fixUserDataPermissions() } +initApp() + .then(() => { + const gotTheLock = app.requestSingleInstanceLock() + + if (!gotTheLock) { + app.quit() + } + }) + .catch(() => { + // ignore permission fix errors + const gotTheLock = app.requestSingleInstanceLock() + + if (!gotTheLock) { + app.quit() + } + }) + export function customRelaunch(): void { const script = `while kill -0 ${process.pid} 2>/dev/null; do sleep 0.1 diff --git a/src/main/utils/init.ts b/src/main/utils/init.ts index 5a6e3ea..f4fb539 100644 --- a/src/main/utils/init.ts +++ b/src/main/utils/init.ts @@ -22,8 +22,10 @@ import { defaultProfileConfig } from './template' import yaml from 'yaml' -import { mkdir, writeFile,rm, readdir, cp } from 'fs/promises' +import { mkdir, writeFile, rm, readdir, cp, stat } from 'fs/promises' import { existsSync } from 'fs' +import { exec } from 'child_process' +import { promisify } from 'util' import path from 'path' import { startPacServer, @@ -40,7 +42,32 @@ import { import { app } from 'electron' import { startSSIDCheck } from '../sys/ssid' +async function fixDataDirPermissions(): Promise { + if (process.platform !== 'darwin') return + + const dataDirPath = dataDir() + if (!existsSync(dataDirPath)) return + + try { + const stats = await stat(dataDirPath) + const currentUid = process.getuid?.() || 0 + + if (stats.uid === 0 && currentUid !== 0) { + const execPromise = promisify(exec) + const username = process.env.USER || process.env.LOGNAME + if (username) { + await execPromise(`chown -R "${username}:staff" "${dataDirPath}"`) + await execPromise(`chmod -R u+rwX "${dataDirPath}"`) + } + } + } catch { + // ignore + } +} + async function initDirs(): Promise { + await fixDataDirPermissions() + if (!existsSync(dataDir())) { await mkdir(dataDir()) }