解决应用程序在macos上访问权限的问题。

This commit is contained in:
Hommy
2026-01-12 08:42:30 +08:00
parent 26d4f9542b
commit a9e4370100
6 changed files with 129 additions and 6 deletions
+10
View File
@@ -192,6 +192,16 @@ curl -X POST "http://localhost:30000/openapi/capcut-mate/v1/add_videos" \
剪映小助手客户端提供了桌面端的便捷操作界面,以下是启动方法:
### macOS 沙箱权限说明
在 macOS 上运行时,应用可能会请求访问特定文件夹的权限。请按照以下步骤操作:
1. 如果首次运行时出现权限提示,请允许应用访问所需文件夹
2. 如需手动配置,请前往 `系统偏好设置 > 安全性与隐私 > 隐私 > 文件夹访问`
3. 确保 CapCut Mate 应用已被添加到允许列表中
更多详细信息,请参阅 [macOS 沙箱权限配置指南](./docs/macos_sandbox_setup.md)。
1. 安装依赖
```bash
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Enable app sandbox -->
<key>com.apple.security.app-sandbox</key>
<true/>
<!-- File access permissions -->
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<!-- Access to user's Downloads folder -->
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<!-- Access to user's Documents folder -->
<key>com.apple.security.files.documents.read-write</key>
<true/>
<!-- Access to user's Pictures folder -->
<key>com.apple.security.files.pictures.read-write</key>
<true/>
<!-- Access to user's Music folder -->
<key>com.apple.security.files.music.read-write</key>
<true/>
<!-- Access to user's Movies folder -->
<key>com.apple.security.files.movies.read-write</key>
<true/>
<!-- Network access -->
<key>com.apple.security.network.client</key>
<true/>
<!-- Hardware access for media files -->
<key>com.apple.security.device.audio-input</key>
<false/>
<key>com.apple.security.device.camera</key>
<false/>
</dict>
</plist>
+18 -1
View File
@@ -1,4 +1,4 @@
const { app, BrowserWindow } = require('electron');
const { app, BrowserWindow, dialog } = require('electron');
const path = require('path');
const logger = require('./nodeapi/logger');
@@ -67,6 +67,23 @@ function createWindow() {
return mainWindow;
}
// 处理未捕获的异常,特别是与文件权限相关的错误
process.on('uncaughtException', (error) => {
logger.error('未捕获的异常:', error);
// 在 macOS 沙箱环境下,某些权限错误可能在这里捕获
if (process.platform === 'darwin' && error.message &&
(error.message.includes('Operation not permitted') || error.message.includes('EPERM'))) {
dialog.showMessageBoxSync({
type: 'error',
title: '权限错误',
message: '应用缺少必要的文件访问权限,请检查系统偏好设置中的隐私与安全性设置。',
detail: '请在 系统偏好设置 > 安全性与隐私 > 隐私 > 文件夹访问 中允许本应用访问相关文件夹。',
buttons: ['确定']
});
}
});
// 当Electron完成初始化并准备创建浏览器窗口时调用此方法
app.whenReady().then(() => {
createWindow();
+49 -4
View File
@@ -187,6 +187,24 @@ async function updateDraftPath(parentWindow) {
return { success: false, error: "用户取消了目录选择" };
}
try {
// 验证目录权限
try {
await fs.access(targetDir, fs.constants.R_OK | fs.constants.W_OK);
} catch (accessError) {
logger.error('所选目录无读写权限:', accessError);
// 尝试使用 dialog 显示错误消息
if (parentWindow) {
const { dialog } = require('electron');
await dialog.showMessageBox(parentWindow, {
type: 'error',
title: '权限不足',
message: '所选目录没有足够的读写权限,请选择其他目录。',
buttons: ['确定']
});
}
return { success: false, error: '所选目录没有足够的读写权限' };
}
const configPath = getConfigPath();
let config = {};
@@ -215,15 +233,15 @@ async function getTargetDirectory(parentWindow = null, isUpdate = false) {
let config = await readConfig();
if (!isUpdate && config.targetDirectory) {
try {
await fs.access(config.targetDirectory);
await fs.access(config.targetDirectory, fs.constants.R_OK | fs.constants.W_OK);
return config.targetDirectory;
} catch (accessErr) {
logger.warn("配置的目录已不存在,将重新选择。");
logger.warn("配置的目录已不存在或无访问权限,将重新选择。", accessErr.message);
}
}
const dialogOptions = {
properties: ["openDirectory"],
properties: ["openDirectory", "createDirectory"], // 允许创建新目录
title: "请选择目标目录",
buttonLabel: "选择此目录",
defaultPath: isUpdate ? config.targetDirectory : undefined
@@ -238,6 +256,24 @@ async function getTargetDirectory(parentWindow = null, isUpdate = false) {
if (!result.canceled && result.filePaths.length > 0) {
const selectedDir = result.filePaths[0];
// 再次验证目录权限
try {
await fs.access(selectedDir, fs.constants.R_OK | fs.constants.W_OK);
} catch (accessErr) {
logger.error('所选目录无读写权限:', accessErr);
if (parentWindow) {
const { dialog } = require('electron');
await dialog.showMessageBox(parentWindow, {
type: 'error',
title: '权限不足',
message: '所选目录没有足够的读写权限,请重新选择。',
buttons: ['确定']
});
}
return ''; // 返回空字符串表示失败
}
config.targetDirectory = selectedDir;
await writeConfig(config);
return selectedDir;
@@ -477,7 +513,16 @@ async function downloadFiles(
logger.info("[log] targetDir: " + targetDir);
// 3. 确保目标目录存在
await fs.mkdir(targetDir, { recursive: true }); // recursive: true 可以创建多级目录
try {
await fs.mkdir(targetDir, { recursive: true }); // recursive: true 可以创建多级目录
} catch (mkdirError) {
logger.error(`创建目录失败: ${targetDir}`, mkdirError);
await appendDownloadLog(
{ level: "error", message: `创建目录失败: ${targetDir} - ${mkdirError.message}` },
parentWindow
);
throw mkdirError;
}
// return { success: true, message: targetDir };
// 4. 下载文件
@@ -11,6 +11,10 @@ module.exports = {
mac: {
icon: "assets/icons/logo.icns",
artifactName: "capcut-mate-macos-\${arch}-green.zip",
hardenedRuntime: true,
gatekeeperAssess: false,
entitlements: "assets/entitlements.mac.plist",
entitlementsInherit: "assets/entitlements.mac.plist"
},
files: [
"!node_modules/**/*",
@@ -43,7 +43,11 @@ module.exports = {
}
],
artifactName: "capcut-mate-macos-${arch}-installer.dmg",
category: "public.app-category.productivity"
category: "public.app-category.productivity",
hardenedRuntime: true,
gatekeeperAssess: false,
entitlements: "assets/entitlements.mac.plist",
entitlementsInherit: "assets/entitlements.mac.plist"
},
dmg: {
background: null,