Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
3d8cffac3f | |
|
|
edec168bad |
46
main.js
46
main.js
|
|
@ -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')
|
// Send back the results using event.reply
|
||||||
);
|
// event.reply('sync-complete', {
|
||||||
|
// totalItems: result.value.length,
|
||||||
console.log(`Found ${videoItems.length} video items`);
|
// videoItems: result.value.filter(item =>
|
||||||
|
// item.name.toLowerCase().endsWith('.mov') ||
|
||||||
// Process each video item
|
// item.name.toLowerCase().endsWith('.mp4')
|
||||||
for (const item of videoItems) {
|
// ).length
|
||||||
if (item['@microsoft.graph.downloadUrl']) {
|
// });
|
||||||
const filename = `${item.id}${path.extname(item.name)}`;
|
|
||||||
const filepath = path.join(destFolder, filename);
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
10
package.json
10
package.json
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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`;
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
console.log('Querying OneDrive items from:', url);
|
const authWindow = new BrowserWindow({
|
||||||
|
width: 800,
|
||||||
const response = await fetch(url, {
|
height: 600,
|
||||||
headers: {
|
show: true,
|
||||||
'Authorization': `Bearer ${auth.accessToken}`,
|
webPreferences: {
|
||||||
'Accept': 'application/json',
|
nodeIntegration: false,
|
||||||
'Cookie': auth.cookies,
|
contextIsolation: true,
|
||||||
'Origin': 'https://onedrive.live.com',
|
webSecurity: false // Allow cross-origin requests
|
||||||
'Referer': 'https://onedrive.live.com/'
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue