Compare commits

...

2 Commits
main ... air

Author SHA1 Message Date
Tiger Ren 3d8cffac3f Refactor sync process to use new token retrieval method and update dependencies
- Updated node-fetch to version 2.6.7 to resolve compatibility issues
- Modified main.js to handle new async token retrieval approach
- Simplified start-sync IPC handler to prepare for new authentication flow
- Updated package.json and package-lock.json to reflect dependency changes
- Adjusted renderer.js to work with new GraphApiClient token retrieval method
2025-01-29 01:25:31 +08:00
Tiger Ren edec168bad use new lib to get token 2025-01-23 00:36:44 +08:00
8 changed files with 253 additions and 305 deletions

42
main.js
View File

@ -209,39 +209,23 @@ ipcMain.on('select-folder', (event) => {
}); });
}); });
ipcMain.on('start-sync', async (event, { destFolder, onedriveSource, items }) => { ipcMain.handle('start-sync', async (event, { destFolder, onedriveSource }) => {
console.log('Starting sync with config:', { destFolder, onedriveSource }); console.log('main.js start-sync:', { destFolder, onedriveSource });
console.log(`Processing ${items.length} items...`);
try { try {
// Filter for video files (Live Photos) const graphHelper = require('./renderer/graphHelper');
const videoItems = items.filter(item => const result = await graphHelper.listFolderContents(onedriveSource);
item.name.toLowerCase().endsWith('.mov') ||
item.name.toLowerCase().endsWith('.mp4')
);
console.log(`Found ${videoItems.length} video items`); // Send back the results using event.reply
// event.reply('sync-complete', {
// Process each video item // totalItems: result.value.length,
for (const item of videoItems) { // videoItems: result.value.filter(item =>
if (item['@microsoft.graph.downloadUrl']) { // item.name.toLowerCase().endsWith('.mov') ||
const filename = `${item.id}${path.extname(item.name)}`; // item.name.toLowerCase().endsWith('.mp4')
const filepath = path.join(destFolder, filename); // ).length
// });
console.log(`Downloading: ${item.name} as ${filename}`);
await downloadVideo(item['@microsoft.graph.downloadUrl'], filepath);
} else {
console.warn(`No download URL for item: ${item.name}`);
}
}
event.reply('sync-complete', {
totalItems: items.length,
videoItems: videoItems.length
});
} catch (error) { } catch (error) {
console.error('Sync error:', error); console.error('Sync error:', error);
event.reply('sync-error', error.message); // event.reply('sync-error', error.message);
} }
}); });

191
package-lock.json generated
View File

@ -13,7 +13,7 @@
"@electron/remote": "^2.1.2", "@electron/remote": "^2.1.2",
"@microsoft/microsoft-graph-client": "^3.0.2", "@microsoft/microsoft-graph-client": "^3.0.2",
"electron-store": "^10.0.0", "electron-store": "^10.0.0",
"node-fetch": "^3.3.0", "node-fetch": "^2.6.7",
"uuid": "^11.0.4" "uuid": "^11.0.4"
}, },
"devDependencies": { "devDependencies": {
@ -51,9 +51,9 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.26.0", "version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@ -162,9 +162,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.17.12", "version": "20.17.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.12.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz",
"integrity": "sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==",
"dependencies": { "dependencies": {
"undici-types": "~6.19.2" "undici-types": "~6.19.2"
} }
@ -326,14 +326,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/debounce-fn": { "node_modules/debounce-fn": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-6.0.0.tgz", "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-6.0.0.tgz",
@ -451,17 +443,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/dot-prop/node_modules/type-fest": {
"version": "4.31.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz",
"integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ecdsa-sig-formatter": { "node_modules/ecdsa-sig-formatter": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@ -471,9 +452,9 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "33.2.1", "version": "33.3.2",
"resolved": "https://registry.npmjs.org/electron/-/electron-33.2.1.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-33.3.2.tgz",
"integrity": "sha512-SG/nmSsK9Qg1p6wAW+ZfqU+AV8cmXMTIklUL18NnOKfZLlum4ZsDoVdmmmlL39ZmeCaq27dr7CgslRPahfoVJg==", "integrity": "sha512-2pWr0frM9UrZGX1d7eoFdMROw10h2vXIWJmXdjwlKnSWWUm18GCrEOUeDUr+IMgz5EjO7JM7FQDHDMApeMgHyg==",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
@ -488,9 +469,9 @@
} }
}, },
"node_modules/electron-store": { "node_modules/electron-store": {
"version": "10.0.0", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/electron-store/-/electron-store-10.0.0.tgz", "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-10.0.1.tgz",
"integrity": "sha512-BU/QZh+5twHBprRdLu3YZX/rIarmZzhTNpJvAvqG1/yN0mNCrsMh0kl7bM4xaUKDNRiHz1r7wP/7Prjh7cleIw==", "integrity": "sha512-Ok0bF13WWdTzZi9rCtPN8wUfwx+yDMmV6PAnCMqjNRKEXHmklW/rV+6DofV/Vf5qoAh+Bl9Bj7dQ+0W+IL2psg==",
"dependencies": { "dependencies": {
"conf": "^13.0.0", "conf": "^13.0.0",
"type-fest": "^4.20.0" "type-fest": "^4.20.0"
@ -502,17 +483,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/electron-store/node_modules/type-fest": {
"version": "4.31.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz",
"integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/end-of-stream": { "node_modules/end-of-stream": {
"version": "1.4.4", "version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@ -590,9 +560,9 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"node_modules/fast-uri": { "node_modules/fast-uri": {
"version": "3.0.5", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
"integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -612,39 +582,6 @@
"pend": "~1.2.0" "pend": "~1.2.0"
} }
}, },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@ -954,39 +891,23 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "3.3.2", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": { "dependencies": {
"data-uri-to-buffer": "^4.0.0", "whatwg-url": "^5.0.0"
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}, },
"engines": { "engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0" "node": "4.x || >=6.0.0"
}, },
"funding": { "peerDependencies": {
"type": "opencollective", "encoding": "^0.1.0"
"url": "https://opencollective.com/node-fetch" },
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
} }
}, },
"node_modules/normalize-url": { "node_modules/normalize-url": {
@ -1152,6 +1073,18 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/serialize-error/node_modules/type-fest": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"optional": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@ -1174,18 +1107,22 @@
"node": ">= 8.0" "node": ">= 8.0"
} }
}, },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
}, },
"node_modules/type-fest": { "node_modules/type-fest": {
"version": "0.13.1", "version": "4.33.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==",
"optional": true,
"engines": { "engines": {
"node": ">=10" "node": ">=16"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -1216,9 +1153,9 @@
} }
}, },
"node_modules/uuid": { "node_modules/uuid": {
"version": "11.0.4", "version": "11.0.5",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
"integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==", "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
"funding": [ "funding": [
"https://github.com/sponsors/broofa", "https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan" "https://github.com/sponsors/ctavan"
@ -1227,18 +1164,24 @@
"uuid": "dist/esm/bin/uuid" "uuid": "dist/esm/bin/uuid"
} }
}, },
"node_modules/web-streams-polyfill": { "node_modules/webidl-conversions": {
"version": "3.3.3", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
"engines": { },
"node": ">= 8" "node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/when-exit": { "node_modules/when-exit": {
"version": "2.1.3", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.3.tgz", "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz",
"integrity": "sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==" "integrity": "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg=="
}, },
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",

View File

@ -14,11 +14,11 @@
"electron": "^33.2.1" "electron": "^33.2.1"
}, },
"dependencies": { "dependencies": {
"@electron/remote": "^2.1.2",
"electron-store": "^10.0.0",
"node-fetch": "^3.3.0",
"uuid": "^11.0.4",
"@azure/msal-node": "^1.18.4", "@azure/msal-node": "^1.18.4",
"@microsoft/microsoft-graph-client": "^3.0.2" "@electron/remote": "^2.1.2",
"@microsoft/microsoft-graph-client": "^3.0.2",
"electron-store": "^10.0.0",
"node-fetch": "^2.6.7",
"uuid": "^11.0.4"
} }
} }

View File

@ -34,8 +34,14 @@ class AuthProvider {
} }
async logout() { async logout() {
if (!this.account) return; if (!this.account){
this.account = await this.getAccount();
console.log('AuthProvider logout: ', this.account);
}
if (!this.account) {
console.log('AuthProvider logout: no account');
return;
}
try { try {
/** /**
* If you would like to end the session with AAD, use the logout endpoint. You'll need to enable * If you would like to end the session with AAD, use the logout endpoint. You'll need to enable

View File

@ -15,17 +15,16 @@ const AAD_ENDPOINT_HOST = "https://login.microsoftonline.com/"; // include the t
const msalConfig = { const msalConfig = {
auth: { auth: {
clientId: "d581ab07-3a21-44d3-84c4-16b06bef6266", clientId: "d581ab07-3a21-44d3-84c4-16b06bef6266",
authority: `${AAD_ENDPOINT_HOST}common`, authority: "https://login.microsoftonline.com/consumers",
redirectUri: "msal" + "d581ab07-3a21-44d3-84c4-16b06bef6266" + "://auth"
}, },
system: { system: {
loggerOptions: { loggerOptions: {
loggerCallback(loglevel, message, containsPii) { loggerCallback: (loglevel, message) => console.log(message),
console.log(message);
},
piiLoggingEnabled: false, piiLoggingEnabled: false,
logLevel: LogLevel.Verbose, logLevel: LogLevel.Verbose
}, }
}, }
}; };
/** /**
@ -38,12 +37,14 @@ const protectedResources = {
graphMe: { graphMe: {
endpoint: `${GRAPH_ENDPOINT_HOST}v1.0/me`, endpoint: `${GRAPH_ENDPOINT_HOST}v1.0/me`,
// scopes: ["User.Read"], // scopes: ["User.Read"],
scopes: ["OneDrive.ReadWrite", "offline_access", "openid", "profile", "User.Read"] scopes: ["Files.ReadWrite"]
} }
}; };
const graphScopes = ["Files.ReadWrite"]; // Modern scope for OneDrive access
module.exports = { module.exports = {
msalConfig: msalConfig, msalConfig,
protectedResources: protectedResources, protectedResources: protectedResources,
graphScopes: graphScopes
}; };

View File

@ -1,110 +1,14 @@
const { BrowserWindow } = require('@electron/remote'); const { BrowserWindow } = require('@electron/remote');
const { v4: uuidv4 } = require('uuid'); // Add this for GUID generation const { v4: uuidv4 } = require('uuid'); // Add this for GUID generation
const { protectedResources, msalConfig } = require("./authConfig");
class GraphApiClient { class GraphApiClient {
constructor() { constructor() {
this.baseUrl = 'https://graph.microsoft.com/v1.0'; this.baseUrl = 'https://graph.microsoft.com/v1.0';
this.clientId = "073204aa-c1e0-4e66-a200-e5815a0aa93d"; this.clientId = "d581ab07-3a21-44d3-84c4-16b06bef6266";
this.scopes = "OneDrive.ReadWrite offline_access openid profile"; this.scopes = "Files.ReadWrite offline_access";
this.redirectUrl = "https://photos.onedrive.com/auth/login"; this.redirectUrl = "https://login.microsoftonline.com/common/oauth2/nativeclient";
}
async getAccessToken() {
return new Promise((resolve, reject) => {
const authWindow = new BrowserWindow({
width: 800,
height: 600,
show: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true
}
});
const authUrl = `https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?` +
`client_id=${this.clientId}` +
`&nonce=uv.${uuidv4()}` +
`&response_mode=form_post` +
`&scope=${this.scopes}` +
`&response_type=code` +
`&redirect_uri=${encodeURIComponent(this.redirectUrl)}`;
console.log('Loading auth URL:', authUrl);
authWindow.loadURL(authUrl);
// Handle the navigation events
authWindow.webContents.on('will-navigate', (event, url) => {
console.log('Navigation detected:', url);
handleCallback(url);
});
authWindow.webContents.on('will-redirect', (event, url) => {
console.log('Redirect detected:', url);
handleCallback(url);
});
const handleCallback = async (callbackUrl) => {
// Check if this is our redirect URI
if (callbackUrl.startsWith(this.redirectUrl)) {
console.log('Redirect URI matched, getting cookies...');
try {
// Get all cookies
const cookies = await authWindow.webContents.session.cookies.get({});
console.log('Found cookies:', cookies.length);
console.log('Cookies:', cookies);
// Find the access token cookie
const accessTokenCookie = cookies.find(
c => c.name === 'AccessToken-OneDrive.ReadWrite'
);
console.log('Access token cookie:', accessTokenCookie);
if (!accessTokenCookie) {
console.log('Access token not found in cookies');
return;
}
// Clean and format the access token
let accessToken = accessTokenCookie.value;
console.log('Access token:', accessToken);
// Remove any URL encoding
accessToken = decodeURIComponent(accessToken);
console.log('Access token decoded:', accessToken);
// Remove any extra dots beyond the two expected in a JWT
const tokenParts = accessToken.split('.');
if (tokenParts.length > 3) {
accessToken = tokenParts.slice(0, 3).join('.');
}
console.log('Access token formatted:', accessToken);
// Convert cookies to a cookie string
const cookieString = cookies
.map(cookie => `${cookie.name}=${cookie.value}`)
.join('; ');
authWindow.close();
resolve({
cookies: cookieString,
accessToken: accessToken
});
} catch (error) {
console.error('Error getting cookies:', error);
reject(error);
}
}
};
// Handle window closing
authWindow.on('closed', () => {
console.log('Auth window closed');
reject(new Error('Authentication window was closed'));
});
});
} }
cleanPath(path) { cleanPath(path) {
@ -116,39 +20,98 @@ class GraphApiClient {
} }
async listFolderContents(folderPath) { async listFolderContents(folderPath) {
const accessToken = await this.getAccessToken();
console.log("listFolderContents accessToken", accessToken);
// Remove AuthProvider usage completely
}
async clearCaches() {
const session = require('@electron/remote').session;
const defaultSession = session.defaultSession;
try { try {
console.log('graphApiClient listFolderContents:', folderPath); await Promise.all([
const auth = await this.getAccessToken(); defaultSession.clearStorageData({
console.log('graphApiClient Auth received'); storages: [
'cookies',
'localstorage',
'cachestorage',
'appcache',
'filesystem',
'indexdb',
'shadercache',
'websql',
'serviceworkers',
]
}),
defaultSession.clearCache(),
defaultSession.clearAuthCache()
]);
console.log('All caches cleared successfully');
} catch (error) {
console.error('Error clearing caches:', error);
throw error;
}
}
const cleanPath = this.cleanPath(folderPath); async getAccessToken() {
console.log('graphApiClient Clean path:', cleanPath); await this.clearCaches();
const url = `${this.baseUrl}/me/drive/root:/${cleanPath}:/children`;
console.log('Querying OneDrive items from:', url); return new Promise((resolve, reject) => {
const authWindow = new BrowserWindow({
const response = await fetch(url, { width: 800,
headers: { height: 600,
'Authorization': `Bearer ${auth.accessToken}`, show: true,
'Accept': 'application/json', webPreferences: {
'Cookie': auth.cookies, nodeIntegration: false,
'Origin': 'https://onedrive.live.com', contextIsolation: true,
'Referer': 'https://onedrive.live.com/' webSecurity: false // Allow cross-origin requests
} }
}); });
if (!response.ok) { // Handle navigation events
const errorData = await response.json(); authWindow.webContents.on('will-redirect', (event, url) => {
console.error('Graph API error details:', errorData); console.log('Redirect detected:', url);
throw new Error(`Graph API error: ${response.status} ${response.statusText}`); this.handleAuthResponse(url, authWindow, resolve, reject);
});
authWindow.webContents.on('did-finish-load', () => {
console.log('Finished loading:', authWindow.webContents.getURL());
});
const authUrl = `https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?` +
`client_id=${this.clientId}` +
`&response_type=token` + // Use token flow directly
`&scope=${encodeURIComponent(this.scopes)}` +
`&redirect_uri=${encodeURIComponent(this.redirectUrl)}` +
`&response_mode=fragment` + // Required for token flow
`&prompt=select_account`;
console.log('Loading auth URL:', authUrl);
authWindow.loadURL(authUrl);
});
}
handleAuthResponse(url, authWindow, resolve, reject) {
if (url.startsWith(this.redirectUrl)) {
try {
const hash = new URL(url).hash.substring(1);
const params = new URLSearchParams(hash);
const accessToken = params.get('access_token');
if (accessToken) {
authWindow.close();
resolve({
accessToken: accessToken,
expiresIn: params.get('expires_in')
});
} else {
reject(new Error('No access token found in response'));
}
} catch (error) {
console.error('Error processing auth response:', error);
reject(error);
} }
const data = await response.json();
return data.value;
} catch (error) {
console.error('Graph API error:', error);
throw error;
} }
} }
} }

47
renderer/graphHelper.js Normal file
View File

@ -0,0 +1,47 @@
const { PublicClientApplication } = require('@azure/msal-node');
const { msalConfig, graphScopes } = require('./authConfig');
const fetch = (...args) =>
import('node-fetch').then(({default: fetch}) => fetch(...args));
class GraphHelper {
constructor() {
this.pca = new PublicClientApplication(msalConfig);
}
async getAccessToken() {
const accounts = await this.pca.getTokenCache().getAllAccounts();
if (accounts.length > 0) {
try {
return await this.pca.acquireTokenSilent({
account: accounts[0],
scopes: graphScopes
});
} catch (error) {
return this.login();
}
}
return this.login();
}
async login() {
return this.pca.acquireTokenInteractive({
scopes: graphScopes,
openBrowser: async (url) => {
const { shell } = require('electron');
await shell.openExternal(url);
}
});
}
async listFolderContents(folderPath) {
const token = await this.getAccessToken();
const response = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:${folderPath}:/children`, {
headers: {
Authorization: `Bearer ${token.accessToken}`
}
});
return response.json();
}
}
module.exports = new GraphHelper();

View File

@ -74,25 +74,29 @@ document.getElementById('sync-button').addEventListener('click', async () => {
alert('Please set both destination folder and OneDrive source path first'); alert('Please set both destination folder and OneDrive source path first');
return; return;
} }
let originalText = "Sync Now";
try { try {
// Show loading state // Show loading state
const syncButton = document.getElementById('sync-button'); const syncButton = document.getElementById('sync-button');
const originalText = syncButton.textContent; originalText = syncButton.textContent;
// const originalText = syncButton.textContent;
syncButton.textContent = 'Syncing...'; syncButton.textContent = 'Syncing...';
syncButton.disabled = true; syncButton.disabled = true;
// Get folder contents using GraphApiClient // Get folder contents using GraphApiClient
console.log('renderer: Getting folder contents:', onedriveSource); console.log('renderer: Getting folder contents:', onedriveSource);
const items = await graphApi.listFolderContents(onedriveSource); // const items = await graphApi.listFolderContents(onedriveSource);
console.log('renderer: Found items:', items.length); // console.log('renderer: Found items:', items.length);
// Process the items // Process the items
// TODO: Handle the items as needed // TODO: Handle the items as needed
showStatus(`Found ${items.length} items`);
// Send the items to the main process // Send the items to the main process
ipcRenderer.send('start-sync', { destFolder, onedriveSource , items}); // ipcRenderer.send('start-sync', { destFolder, onedriveSource });
await ipcRenderer.invoke('start-sync', { destFolder, onedriveSource });
} catch (error) { } catch (error) {
console.error('Sync error:', error); console.error('Sync error:', error);