🔨 Refactor: change config sync to manual

This commit is contained in:
萌萌哒赫萝 2023-05-06 21:50:15 +08:00
parent 5c15ea1544
commit 28be3881e1
8 changed files with 309 additions and 136 deletions

View File

@ -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_GITHUB_TOKEN_PLACEHOLDER: Please enter GitHub Token
SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: Please enter Gitee Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: Please enter Gitee Token
SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: Please enter proxy 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 SETTINGS_AUTO_IMPORT: Auto import config in manage page
# shortcut-page # shortcut-page

View File

@ -255,6 +255,18 @@ SETTINGS_SYNC_CONFIG_GITEE_BRANCH_PLACEHOLDER: 请输入Gitee分支名
SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 请输入GitHub Token SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 请输入GitHub Token
SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 请输入Gitee Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 请输入Gitee Token
SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: 请输入代理地址 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: 管理页面自动导入配置 SETTINGS_AUTO_IMPORT: 管理页面自动导入配置
# shortcut-page # shortcut-page

View File

@ -253,6 +253,18 @@ SETTINGS_SYNC_CONFIG_GITEE_BRANCH_PLACEHOLDER: 請輸入 Gitee 分支名稱
SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 請輸入 GitHub Token SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: 請輸入 GitHub Token
SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 請輸入 Gitee Token SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: 請輸入 Gitee Token
SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER: 請輸入代理地址 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: 管理頁面自動導入配置 SETTINGS_AUTO_IMPORT: 管理頁面自動導入配置
# shortcut-page # shortcut-page

View File

@ -44,6 +44,7 @@ import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedL
import path from 'path' import path from 'path'
import { T } from '~/main/i18n' import { T } from '~/main/i18n'
import { generateShortUrl } from '~/universal/utils/common' import { generateShortUrl } from '~/universal/utils/common'
import { uploadFile, downloadFile } from '../utils/syncSettings'
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
@ -164,6 +165,46 @@ export default {
return shortUrl 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', () => { ipcMain.on('openSettingWindow', () => {
windowManager.get(IWindowList.SETTING_WINDOW)!.show() windowManager.get(IWindowList.SETTING_WINDOW)!.show()
if (windowManager.has(IWindowList.MINI_WINDOW)) { if (windowManager.has(IWindowList.MINI_WINDOW)) {

View File

@ -48,7 +48,6 @@ import clipboardPoll from '../utils/clipboardPoll'
import path from 'path' import path from 'path'
import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static'
import fs from 'fs-extra' import fs from 'fs-extra'
import { syncInterval } from '../utils/syncSettings'
const isDevelopment = process.env.NODE_ENV !== 'production' const isDevelopment = process.env.NODE_ENV !== 'production'
const handleStartUpFiles = (argv: string[], cwd: string) => { const handleStartUpFiles = (argv: string[], cwd: string) => {
@ -202,7 +201,6 @@ class LifeCycle {
} }
const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER) const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER)
fs.ensureDir(clipboardDir) fs.ensureDir(clipboardDir)
syncInterval()
} }
app.whenReady().then(readyFunction) app.whenReady().then(readyFunction)
} }

View File

@ -46,7 +46,7 @@ function getOctokit (syncConfig: SyncConfig) {
} }
function getSyncConfig () { function getSyncConfig () {
const syncConfig = db.get('settings.sync') || { return db.get('settings.sync') || {
type: 'github', type: 'github',
username: '', username: '',
repo: '', repo: '',
@ -54,7 +54,6 @@ function getSyncConfig () {
token: '', token: '',
proxy: '' proxy: ''
} }
return syncConfig
} }
function syncConfigValidator (syncConfig: SyncConfig) { function syncConfigValidator (syncConfig: SyncConfig) {
@ -62,67 +61,6 @@ function syncConfigValidator (syncConfig: SyncConfig) {
return type && username && repo && branch && token 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) { async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) {
const localFilePath = path.join(STORE_PATH, fileName) const localFilePath = path.join(STORE_PATH, fileName)
if (!fs.existsSync(localFilePath)) { if (!fs.existsSync(localFilePath)) {
@ -130,14 +68,19 @@ async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) {
} }
const { username, repo, branch, token, type } = syncConfig const { username, repo, branch, token, type } = syncConfig
if (type === 'gitee') { if (type === 'gitee') {
const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${fileName}` try {
const res = await axios.post(url, { const url = `https://gitee.com/api/v5/repos/${username}/${repo}/contents/${fileName}`
access_token: token, const res = await axios.post(url, {
branch, access_token: token,
content: fs.readFileSync(localFilePath, { encoding: 'base64' }), branch,
message: `upload ${fileName} from PicList` content: fs.readFileSync(localFilePath, { encoding: 'base64' }),
}) message: `upload ${fileName} from PicList`
return res.status >= 200 && res.status < 300 })
return res.status >= 200 && res.status < 300
} catch (error: any) {
logger.error(error)
return false
}
} else { } else {
const octokit = getOctokit(syncConfig) const octokit = getOctokit(syncConfig)
try { try {
@ -269,65 +212,78 @@ async function downloadRemoteToLocal (syncConfig: SyncConfig, fileName: string)
} }
} }
async function syncFile (syncConfig: SyncConfig, fileName: string) { async function uploadFile (fileName: string, all = false) {
const compareResult = await compareNewerFile(syncConfig, fileName) const syncConfig = getSyncConfig()
let result = false if (!syncConfigValidator(syncConfig)) {
if (compareResult === 'upload') { logger.error('sync config is invalid')
result = await uploadLocalToRemote(syncConfig, fileName) return 0
} 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)
} }
return result let successCount = 0
} if (all) {
for (const file of configFileNames) {
async function syncAllFiles (syncConfig: SyncConfig) { const result = await uploadFunc(syncConfig, file)
for (const file of configFileNames) {
try {
const result = await syncFile(syncConfig, file)
if (result) { if (result) {
logger.info(`sync file ${file} success`) successCount++
} else {
logger.error(`sync file ${file} failed`)
} }
} 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 () { async function uploadFunc (syncConfig: SyncConfig, fileName: string) {
const syncConfig = await getSyncConfig() let result = false
if (!syncConfigValidator(syncConfig)) { try {
return 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 () { async function downloadFile (fileName: string, all = false) {
const syncConfig = await getSyncConfig() const syncConfig = getSyncConfig()
if (!syncConfigValidator(syncConfig)) { if (!syncConfigValidator(syncConfig)) {
return logger.error('sync config is invalid')
return 0
} }
const syncFunc = async () => { if (all) {
await syncAllFiles(syncConfig) let successCount = 0
logger.info(`sync all files at ${new Date().toLocaleString()}`) 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 { export {
syncFunc, uploadFile,
syncInterval downloadFile
} }

View File

@ -99,6 +99,18 @@
{{ $T('SETTINGS_CLICK_TO_SET') }} {{ $T('SETTINGS_CLICK_TO_SET') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item
:label="$T('SETTINGS_UP_DOWN_DESC')"
>
<el-button
type="primary"
round
size="small"
@click=" upDownConfigVisible = true"
>
{{ $T('SETTINGS_CLICK_TO_SET') }}
</el-button>
</el-form-item>
<el-form-item <el-form-item
:label="$T('SETTINGS_MIGRATE_FROM_PICGO')" :label="$T('SETTINGS_MIGRATE_FROM_PICGO')"
> >
@ -818,15 +830,6 @@
:placeholder="$T('SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER')" :placeholder="$T('SETTINGS_SYNC_CONFIG_PROXY_PLACEHOLDER')"
/> />
</el-form-item> </el-form-item>
<el-form-item
:label="$T('SETTINGS_SYNC_CONFIG_INTERVAL')"
>
<el-input-number
v-model="sync.interval"
:min="10"
:step="1"
/>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button <el-button
@ -844,6 +847,80 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog
v-model="upDownConfigVisible"
class="server-dialog"
width="60%"
:title="$T('SETTINGS_UP_DOWN_DESC')"
:modal-append-to-body="false"
center
>
<el-form
label-position="right"
label-width="120px"
>
<el-form-item
:label="$T('SETTINGS_SYNC_UPLOAD')"
>
<el-button-group>
<el-button
type="primary"
plain
size="small"
@click="uploadCommonConfig"
>
{{ $T('SETTINGS_SYNC_COMMON_CONFIG') }}
</el-button>
<el-button
type="primary"
plain
size="small"
@click="uploadManageConfig"
>
{{ $T('SETTINGS_SYNC_MANAGE_CONFIG') }}
</el-button>
<el-button
type="warning"
plain
size="small"
@click="uploadAll"
>
{{ $T('SETTINGS_SYNC_UPLOAD_ALL') }}
</el-button>
</el-button-group>
</el-form-item>
<el-form-item
:label="$T('SETTINGS_SYNC_DOWNLOAD')"
>
<el-button-group>
<el-button
type="primary"
size="small"
plain
@click="downloadCommonConfig"
>
{{ $T('SETTINGS_SYNC_COMMON_CONFIG') }}
</el-button>
<el-button
type="primary"
size="small"
plain
@click="downloadManageConfig"
>
{{ $T('SETTINGS_SYNC_MANAGE_CONFIG') }}
</el-button>
<el-button
type="warning"
size="small"
plain
@click="downloadAll"
>
{{ $T('SETTINGS_SYNC_DOWNLOAD_ALL') }}
</el-button>
</el-button-group>
</el-form-item>
</el-form>
</el-dialog>
<el-dialog <el-dialog
v-model="imageProcessDialogVisible" v-model="imageProcessDialogVisible"
:title="$T('UPLOAD_PAGE_IMAGE_PROCESS_DIALOG_TITLE')" :title="$T('UPLOAD_PAGE_IMAGE_PROCESS_DIALOG_TITLE')"
@ -1087,12 +1164,11 @@ import { getLatestVersion } from '#/utils/getLatestVersion'
import { compare } from 'compare-versions' import { compare } from 'compare-versions'
import { STABLE_RELEASE_URL } from '#/utils/static' import { STABLE_RELEASE_URL } from '#/utils/static'
import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw } from 'vue' import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw } from 'vue'
import { getConfig, saveConfig, sendRPC, sendToMain } from '@/utils/dataSender' import { getConfig, saveConfig, sendToMain } from '@/utils/dataSender'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { SHORTKEY_PAGE } from '@/router/config' import { SHORTKEY_PAGE } from '@/router/config'
import { IConfig, IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist' import { IConfig, IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import { invokeToMain } from '@/manage/utils/dataSender' import { invokeToMain } from '@/manage/utils/dataSender'
import { IRPCActionType } from '~/universal/types/enum'
const imageProcessDialogVisible = ref(false) const imageProcessDialogVisible = ref(false)
@ -1227,6 +1303,7 @@ const customLinkVisible = ref(false)
const checkUpdateVisible = ref(false) const checkUpdateVisible = ref(false)
const serverVisible = ref(false) const serverVisible = ref(false)
const syncVisible = ref(false) const syncVisible = ref(false)
const upDownConfigVisible = ref(false)
const proxyVisible = ref(false) const proxyVisible = ref(false)
const mainWindowSizeVisible = ref(false) const mainWindowSizeVisible = ref(false)
@ -1287,10 +1364,6 @@ const syncType = [
} }
] ]
const allSynFilled = computed(() => {
return sync.value.username && sync.value.repo && sync.value.branch && sync.value.token
})
async function cancelSyncSetting () { async function cancelSyncSetting () {
syncVisible.value = false syncVisible.value = false
sync.value = await getConfig('settings.sync') || { sync.value = await getConfig('settings.sync') || {
@ -1309,9 +1382,6 @@ function confirmSyncSetting () {
'settings.sync': sync.value 'settings.sync': sync.value
}) })
syncVisible.value = false syncVisible.value = false
if (allSynFilled.value) {
sendRPC(IRPCActionType.RELOAD_APP)
}
} }
const version = pkg.version const version = pkg.version
@ -1674,6 +1744,66 @@ async function cancelLogLevelSetting () {
form.logFileSizeLimit = logFileSizeLimit 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 () { function confirmServerSetting () {
server.value.port = parseInt(server.value.port as unknown as string, 10) server.value.port = parseInt(server.value.port as unknown as string, 10)
saveConfig({ saveConfig({

View File

@ -248,6 +248,18 @@ interface ILocales {
SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: string SETTINGS_SYNC_CONFIG_GITHUB_TOKEN_PLACEHOLDER: string
SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: string SETTINGS_SYNC_CONFIG_GITEE_TOKEN_PLACEHOLDER: string
SETTINGS_SYNC_CONFIG_PROXY_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 SETTINGS_AUTO_IMPORT: string
SHORTCUT_NAME: string SHORTCUT_NAME: string
SHORTCUT_BIND: string SHORTCUT_BIND: string