diff --git a/public/i18n/en.yml b/public/i18n/en.yml index 650917a..caf0374 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -252,6 +252,18 @@ SETTINGS_SYNC_CONFIG_GITEE_BRANCH_PLACEHOLDER: Please enter Gitee branch name SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: Please enter GitHub Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: Please enter Gitee Token SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: Please enter proxy +SETTINGS_UP_DOWN_DESC: Upload and download configuration files +SETTINGS_UP_DOWN_TITLE: Please use this after filled the synchronization configuration +SETTINGS_SYNC_UPLOAD: Upload +SETTINGS_SYNC_DOWNLOAD: Download +SETTINGS_SYNC_UPLOAD_ALL: Upload all +SETTINGS_SYNC_DOWNLOAD_ALL: Download all +SETTINGS_SYNC_UPLOAD_SUCCESS: Successfully uploaded +SETTINGS_SYNC_UPLOAD_FAILED: Upload failed +SETTINGS_SYNC_DOWNLOAD_SUCCESS: Successfully downloaded +SETTINGS_SYNC_DOWNLOAD_FAILED: Download failed +SETTINGS_SYNC_COMMON_CONFIG: Common configuration +SETTINGS_SYNC_MANAGE_CONFIG: Manage configuration SETTINGS_AUTO_IMPORT: Auto import config in manage page # shortcut-page diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 76629ad..f5e4d63 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -255,6 +255,18 @@ SETTINGS_SYNC_CONFIG_GITEE_BRANCH_PLACEHOLDER: 请输入Gitee分支名 SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 请输入GitHub Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 请输入Gitee Token SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: 请输入代理地址 +SETTINGS_UP_DOWN_DESC: 上传下载配置文件 +SETTINGS_UP_DOWN_TITLE: 请在设置完同步配置后使用 +SETTINGS_SYNC_UPLOAD: 上传 +SETTINGS_SYNC_DOWNLOAD: 下载 +SETTINGS_SYNC_UPLOAD_ALL: 上传所有 +SETTINGS_SYNC_DOWNLOAD_ALL: 下载所有 +SETTINGS_SYNC_UPLOAD_SUCCESS: 上传成功 +SETTINGS_SYNC_UPLOAD_FAILED: 上传失败 +SETTINGS_SYNC_DOWNLOAD_SUCCESS: 下载成功 +SETTINGS_SYNC_DOWNLOAD_FAILED: 下载失败 +SETTINGS_SYNC_COMMON_CONFIG: 通用配置 +SETTINGS_SYNC_MANAGE_CONFIG: 管理配置 SETTINGS_AUTO_IMPORT: 管理页面自动导入配置 # shortcut-page diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index 6097b77..394800e 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -253,6 +253,18 @@ SETTINGS_SYNC_CONFIG_GITEE_BRANCH_PLACEHOLDER: 請輸入 Gitee 分支名稱 SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 請輸入 GitHub Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 請輸入 Gitee Token SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: 請輸入代理地址 +SETTINGS_UP_DOWN_DESC: 上傳和下載配置檔案 +SETTINGS_UP_DOWN_TITLE: 請在設置完同步配置後再使用 +SETTINGS_SYNC_UPLOAD: 上傳 +SETTINGS_SYNC_DOWNLOAD: 下載 +SETTINGS_SYNC_UPLOAD_ALL: 上傳全部 +SETTINGS_SYNC_DOWNLOAD_ALL: 下載全部 +SETTINGS_SYNC_UPLOAD_SUCCESS: 上傳成功 +SETTINGS_SYNC_UPLOAD_FAILED: 上傳失敗 +SETTINGS_SYNC_DOWNLOAD_SUCCESS: 下載成功 +SETTINGS_SYNC_DOWNLOAD_FAILED: 下載失敗 +SETTINGS_SYNC_COMMON_CONFIG: 通用配置 +SETTINGS_SYNC_MANAGE_CONFIG: 管理配置 SETTINGS_AUTO_IMPORT: 管理頁面自動導入配置 # shortcut-page diff --git a/src/main/events/ipcList.ts b/src/main/events/ipcList.ts index 51f52d4..d4c97b6 100644 --- a/src/main/events/ipcList.ts +++ b/src/main/events/ipcList.ts @@ -44,6 +44,7 @@ import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedL import path from 'path' import { T } from '~/main/i18n' import { generateShortUrl } from '~/universal/utils/common' +import { uploadFile, downloadFile } from '../utils/syncSettings' const STORE_PATH = app.getPath('userData') @@ -164,6 +165,46 @@ export default { return shortUrl }) + ipcMain.handle('uploadCommonConfig', async () => { + const dataResult = await uploadFile('data.json') + const bakResult = await uploadFile('data.bak.json') + return dataResult + bakResult + }) + + ipcMain.handle('downloadCommonConfig', async () => { + const dataResult = await downloadFile('data.json') + const bakResult = await downloadFile('data.bak.json') + return dataResult + bakResult + }) + + ipcMain.handle('uploadManageConfig', async () => { + const manageResult = await uploadFile('manage.json') + const bakResult = await uploadFile('manage.bak.json') + return manageResult + bakResult + }) + + ipcMain.handle('downloadManageConfig', async () => { + const manageResult = await downloadFile('manage.json') + const bakResult = await downloadFile('manage.bak.json') + return manageResult + bakResult + }) + + ipcMain.handle('uploadAllConfig', async () => { + const dataResult = await uploadFile('data.json') + const bakResult = await uploadFile('data.bak.json') + const manageResult = await uploadFile('manage.json') + const manageBakResult = await uploadFile('manage.bak.json') + return dataResult + bakResult + manageResult + manageBakResult + }) + + ipcMain.handle('downloadAllConfig', async () => { + const dataResult = await downloadFile('data.json') + const bakResult = await downloadFile('data.bak.json') + const manageResult = await downloadFile('manage.json') + const manageBakResult = await downloadFile('manage.bak.json') + return dataResult + bakResult + manageResult + manageBakResult + }) + ipcMain.on('openSettingWindow', () => { windowManager.get(IWindowList.SETTING_WINDOW)!.show() if (windowManager.has(IWindowList.MINI_WINDOW)) { diff --git a/src/main/lifeCycle/index.ts b/src/main/lifeCycle/index.ts index a0756fc..2db337d 100644 --- a/src/main/lifeCycle/index.ts +++ b/src/main/lifeCycle/index.ts @@ -48,7 +48,6 @@ import clipboardPoll from '../utils/clipboardPoll' import path from 'path' import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' import fs from 'fs-extra' -import { syncInterval } from '../utils/syncSettings' const isDevelopment = process.env.NODE_ENV !== 'production' const handleStartUpFiles = (argv: string[], cwd: string) => { @@ -202,7 +201,6 @@ class LifeCycle { } const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER) fs.ensureDir(clipboardDir) - syncInterval() } app.whenReady().then(readyFunction) } diff --git a/src/main/utils/syncSettings.ts b/src/main/utils/syncSettings.ts index 6efdd10..ee523c6 100644 --- a/src/main/utils/syncSettings.ts +++ b/src/main/utils/syncSettings.ts @@ -46,7 +46,7 @@ function getOctokit (syncConfig: SyncConfig) { } function getSyncConfig () { - const syncConfig = db.get('settings.sync') || { + return db.get('settings.sync') || { type: 'github', username: '', repo: '', @@ -54,7 +54,6 @@ function getSyncConfig () { token: '', proxy: '' } - return syncConfig } function syncConfigValidator (syncConfig: SyncConfig) { @@ -62,67 +61,6 @@ function syncConfigValidator (syncConfig: SyncConfig) { return type && username && repo && branch && token } -async function getModifiedTime (syncConfig: SyncConfig, filePath: string) { - const { username, repo, branch, token, type } = syncConfig - if (type === 'gitee') { - const url = `https://gitee.com/api/v5/repos/${username}/${repo}/commits` - const res = await axios.get(url, { - params: { - access_token: token, - ref: branch, - path: filePath - } - }) - const data = res.data - if (data.length > 0) { - return data[0].commit.committer.date - } else { - return null - } - } else { - const octokit = getOctokit(syncConfig) - try { - const res = await octokit.rest.repos.listCommits({ - owner: username, - repo, - ref: branch, - path: filePath, - per_page: 1 - }) - if (res.status === 200) { - return res.data.length > 0 ? res.data[0].commit.committer?.date : null - } else { - return null - } - } catch (error: any) { - logger.error(error) - return null - } - } -} - -async function getModifiedTimeOfLocal (filePath: string) { - if (!fs.existsSync(filePath)) { - return new Date(0) - } - const stat = await fs.stat(filePath) - return stat.mtime -} - -async function compareNewerFile (syncConfig: SyncConfig, fileName: string): Promise<'upload' | 'download' | 'update' | undefined> { - const localFilePath = path.join(STORE_PATH, fileName) - const remoteModifiedTime = await getModifiedTime(syncConfig, fileName) - if (remoteModifiedTime === null) { - return 'upload' - } - const localModifiedTime = await getModifiedTimeOfLocal(localFilePath) - if (remoteModifiedTime && localModifiedTime) { - return Date.parse(remoteModifiedTime) > localModifiedTime.getTime() ? 'download' : 'update' - } else { - throw new Error('get modified time failed') - } -} - async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) { const localFilePath = path.join(STORE_PATH, fileName) if (!fs.existsSync(localFilePath)) { @@ -130,14 +68,19 @@ async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) { } const { username, repo, branch, token, type } = syncConfig if (type === 'gitee') { - const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${fileName}` - const res = await axios.post(url, { - access_token: token, - branch, - content: fs.readFileSync(localFilePath, { encoding: 'base64' }), - message: `upload ${fileName} from PicList` - }) - return res.status >= 200 && res.status < 300 + try { + const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${fileName}` + const res = await axios.post(url, { + access_token: token, + branch, + content: fs.readFileSync(localFilePath, { encoding: 'base64' }), + message: `upload ${fileName} from PicList` + }) + return res.status >= 200 && res.status < 300 + } catch (error: any) { + logger.error(error) + return false + } } else { const octokit = getOctokit(syncConfig) try { @@ -269,65 +212,78 @@ async function downloadRemoteToLocal (syncConfig: SyncConfig, fileName: string) } } -async function syncFile (syncConfig: SyncConfig, fileName: string) { - const compareResult = await compareNewerFile(syncConfig, fileName) - let result = false - if (compareResult === 'upload') { - result = await uploadLocalToRemote(syncConfig, fileName) - } else if (compareResult === 'update') { - try { - result = await updateLocalToRemote(syncConfig, fileName) - } catch (error: any) { - result = await uploadLocalToRemote(syncConfig, fileName) - } - } else if (compareResult === 'download') { - result = await downloadRemoteToLocal(syncConfig, fileName) +async function uploadFile (fileName: string, all = false) { + const syncConfig = getSyncConfig() + if (!syncConfigValidator(syncConfig)) { + logger.error('sync config is invalid') + return 0 } - return result -} - -async function syncAllFiles (syncConfig: SyncConfig) { - for (const file of configFileNames) { - try { - const result = await syncFile(syncConfig, file) + let successCount = 0 + if (all) { + for (const file of configFileNames) { + const result = await uploadFunc(syncConfig, file) if (result) { - logger.info(`sync file ${file} success`) - } else { - logger.error(`sync file ${file} failed`) + successCount++ } - } catch (error: any) { - logger.error(`sync file ${file} failed`) - logger.error(error) } + logger.info(`upload all files at ${new Date().toLocaleString()}`) + return successCount + } else { + const ressult = await uploadFunc(syncConfig, fileName) + return ressult ? 1 : 0 } } -async function syncFunc () { - const syncConfig = await getSyncConfig() - if (!syncConfigValidator(syncConfig)) { - return +async function uploadFunc (syncConfig: SyncConfig, fileName: string) { + let result = false + try { + result = await updateLocalToRemote(syncConfig, fileName) + } catch (error: any) { + result = await uploadLocalToRemote(syncConfig, fileName) + } + if (!result) { + logger.error(`upload ${fileName} failed`) + return false + } else { + logger.info(`upload ${fileName} success`) + return true } - await syncAllFiles(syncConfig) - logger.info(`sync all files at ${new Date().toLocaleString()}`) } -async function syncInterval () { - const syncConfig = await getSyncConfig() +async function downloadFile (fileName: string, all = false) { + const syncConfig = getSyncConfig() if (!syncConfigValidator(syncConfig)) { - return + logger.error('sync config is invalid') + return 0 } - const syncFunc = async () => { - await syncAllFiles(syncConfig) - logger.info(`sync all files at ${new Date().toLocaleString()}`) + if (all) { + let successCount = 0 + for (const file of configFileNames) { + const result = await downloadFunc(syncConfig, file) + if (result) { + successCount++ + } + } + logger.info(`download all files at ${new Date().toLocaleString()}`) + return successCount + } else { + const result = await downloadFunc(syncConfig, fileName) + return result ? 1 : 0 + } +} + +async function downloadFunc (syncConfig: SyncConfig, fileName: string) { + const result = await downloadRemoteToLocal(syncConfig, fileName) + if (!result) { + logger.error(`download ${fileName} failed`) + return false + } else { + logger.info(`download ${fileName} success`) + return true } - await syncFunc() - const interval = Number(syncConfig.interval) || 60 - setInterval(async () => { - syncFunc() - }, 1000 * 60 * interval) } export { - syncFunc, - syncInterval + uploadFile, + downloadFile } diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 20b4e69..fc91dab 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -99,6 +99,18 @@ {{ $T('SETTINGS_CLICK_TO_SET') }} + + + {{ $T('SETTINGS_CLICK_TO_SET') }} + + @@ -818,15 +830,6 @@ :placeholder="$T('SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER')" /> - - - + + + + + + {{ $T('SETTINGS_SYNC_COMMON_CONFIG') }} + + + {{ $T('SETTINGS_SYNC_MANAGE_CONFIG') }} + + + {{ $T('SETTINGS_SYNC_UPLOAD_ALL') }} + + + + + + + {{ $T('SETTINGS_SYNC_COMMON_CONFIG') }} + + + {{ $T('SETTINGS_SYNC_MANAGE_CONFIG') }} + + + {{ $T('SETTINGS_SYNC_DOWNLOAD_ALL') }} + + + + + { - return sync.value.username && sync.value.repo && sync.value.branch && sync.value.token -}) - async function cancelSyncSetting () { syncVisible.value = false sync.value = await getConfig('settings.sync') || { @@ -1309,9 +1382,6 @@ function confirmSyncSetting () { 'settings.sync': sync.value }) syncVisible.value = false - if (allSynFilled.value) { - sendRPC(IRPCActionType.RELOAD_APP) - } } const version = pkg.version @@ -1674,6 +1744,66 @@ async function cancelLogLevelSetting () { form.logFileSizeLimit = logFileSizeLimit } +function DownMessage (failed: number) { + if (failed) { + $message.error($T('SETTINGS_SYNC_DOWNLOAD_FAILED', { failed })) + } else { + $message.success($T('SETTINGS_SYNC_DOWNLOAD_SUCCESS')) + } +} + +function upMessage (failed: number) { + if (failed) { + $message.error($T('SETTINGS_SYNC_UPLOAD_FAILED', { failed })) + } else { + $message.success($T('SETTINGS_SYNC_UPLOAD_SUCCESS')) + } +} + +async function uploadCommonConfig () { + const result = await invokeToMain('uploadCommonConfig') + const failed = 2 - result + upMessage(failed) +} + +async function downloadCommonConfig () { + const result = await invokeToMain('downloadCommonConfig') + const failed = 2 - result + DownMessage(failed) +} + +async function uploadManageConfig () { + const result = await invokeToMain('uploadManageConfig') + const failed = 2 - result + upMessage(failed) +} + +async function downloadManageConfig () { + const result = await invokeToMain('downloadManageConfig') + const failed = 2 - result + DownMessage(failed) +} + +async function uploadAll () { + const result = await invokeToMain('uploadAllConfig') + const failed = 4 - result + if (result === 4) { + $message.success($T('SETTINGS_SYNC_UPLOAD_SUCCESS')) + } else { + $message.error($T('SETTINGS_SYNC_UPLOAD_FAILED') + `(${failed})`) + } +} + +async function downloadAll () { + const result = await invokeToMain('downloadAllConfig') + const failed = 4 - result + if (result === 4) { + $message.success($T('SETTINGS_SYNC_DOWNLOAD_SUCCESS')) + } else { + $message.error($T('SETTINGS_SYNC_DOWNLOAD_FAILED') + `(${failed})`) + } +} + function confirmServerSetting () { server.value.port = parseInt(server.value.port as unknown as string, 10) saveConfig({ diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index df45647..cb6e517 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -248,6 +248,18 @@ interface ILocales { SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: string SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: string SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: string + SETTINGS_UP_DOWN_DESC: string + SETTINGS_UP_DOWN_TITLE: string + SETTINGS_SYNC_UPLOAD: string + SETTINGS_SYNC_DOWNLOAD: string + SETTINGS_SYNC_UPLOAD_ALL: string + SETTINGS_SYNC_DOWNLOAD_ALL: string + SETTINGS_SYNC_UPLOAD_SUCCESS: string + SETTINGS_SYNC_UPLOAD_FAILED: string + SETTINGS_SYNC_DOWNLOAD_SUCCESS: string + SETTINGS_SYNC_DOWNLOAD_FAILED: string + SETTINGS_SYNC_COMMON_CONFIG: string + SETTINGS_SYNC_MANAGE_CONFIG: string SETTINGS_AUTO_IMPORT: string SHORTCUT_NAME: string SHORTCUT_BIND: string