mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-09-08 08:57:14 +08:00
🎨 Style(custom): format with prettier
This commit is contained in:
@@ -3,3 +3,6 @@ test/unit/*.js
|
||||
test/e2e/*.js
|
||||
dist/
|
||||
src/**/*.js
|
||||
node_modules
|
||||
dist
|
||||
vue.config.js
|
||||
|
||||
+20
-3
@@ -10,7 +10,8 @@ module.exports = {
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'@vue/standard',
|
||||
'@vue/typescript'
|
||||
'@vue/typescript/recommended',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
@@ -20,8 +21,24 @@ module.exports = {
|
||||
'no-async-promise-executor': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'@typescript-eslint/indent': ['error', 2],
|
||||
'vue/no-v-html': 'off'
|
||||
'@typescript-eslint/indent': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description'
|
||||
}
|
||||
],
|
||||
'vue/no-v-html': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{},
|
||||
{
|
||||
usePrettierrc: true
|
||||
}
|
||||
]
|
||||
},
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxBracketSameLine": false,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 120,
|
||||
"proseWrap": "always",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false,
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
+3
-5
@@ -1,6 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
plugins: ['@babel/plugin-proposal-optional-chaining']
|
||||
}
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
plugins: ["@babel/plugin-proposal-optional-chaining"],
|
||||
};
|
||||
|
||||
+4
-8
@@ -87,7 +87,6 @@
|
||||
"write-file-atomic": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/video.js": "^7.3.58",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@electron/notarize": "^2.1.0",
|
||||
"@types/ali-oss": "^6.16.11",
|
||||
@@ -103,14 +102,14 @@
|
||||
"@types/tunnel": "^0.0.7",
|
||||
"@types/upyun": "^3.4.3",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"@types/write-file-atomic": "^4.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-plugin-router": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-standard": "^8.0.1",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/runtime-dom": "^3.4.27",
|
||||
@@ -119,16 +118,13 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"dpdm": "^3.14.0",
|
||||
"electron": "^22.3.27",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-promise": "^6.2.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"husky": "^3.1.0",
|
||||
"node-bump-version": "^1.0.2",
|
||||
"node-loader": "^2.0.0",
|
||||
"npm-check-updates": "^16.14.20",
|
||||
"prettier": "^3.3.2",
|
||||
"stylus": "^0.59.0",
|
||||
"stylus-loader": "^7.1.3",
|
||||
"typescript": "^4.9.5",
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import axios from 'axios'
|
||||
import {
|
||||
app,
|
||||
clipboard,
|
||||
dialog,
|
||||
shell
|
||||
} from 'electron'
|
||||
import { app, clipboard, dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { gte, lte } from 'semver'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { showNotification } from '~/utils/common'
|
||||
import {
|
||||
IRemoteNoticeActionType,
|
||||
IRemoteNoticeTriggerCount,
|
||||
IRemoteNoticeTriggerHook
|
||||
} from '#/types/enum'
|
||||
import { IRemoteNoticeActionType, IRemoteNoticeTriggerCount, IRemoteNoticeTriggerHook } from '#/types/enum'
|
||||
|
||||
// for test
|
||||
const REMOTE_NOTICE_URL = 'https://release.piclist.cn/remote-notice.json'
|
||||
@@ -41,7 +32,10 @@ class RemoteNoticeHandler {
|
||||
fs.writeFileSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, JSON.stringify({}))
|
||||
}
|
||||
try {
|
||||
const localCountStorage: IRemoteNoticeLocalCountStorage = fs.readJSONSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, 'utf8')
|
||||
const localCountStorage: IRemoteNoticeLocalCountStorage = fs.readJSONSync(
|
||||
REMOTE_NOTICE_LOCAL_STORAGE_PATH,
|
||||
'utf8'
|
||||
)
|
||||
this.remoteNoticeLocalCountStorage = localCountStorage
|
||||
} catch (e) {
|
||||
this.remoteNoticeLocalCountStorage = localCountStorage
|
||||
@@ -57,11 +51,11 @@ class RemoteNoticeHandler {
|
||||
|
||||
private async getRemoteNoticeInfo(): Promise<IRemoteNotice | null> {
|
||||
try {
|
||||
const noticeInfo = await axios({
|
||||
const noticeInfo = (await axios({
|
||||
method: 'get',
|
||||
url: REMOTE_NOTICE_URL,
|
||||
responseType: 'json'
|
||||
}).then(res => res.data) as IRemoteNotice
|
||||
}).then(res => res.data)) as IRemoteNotice
|
||||
return noticeInfo
|
||||
} catch {
|
||||
return null
|
||||
@@ -143,12 +137,14 @@ class RemoteNoticeHandler {
|
||||
break
|
||||
case IRemoteNoticeActionType.SHOW_MESSAGE_BOX: {
|
||||
const currentWindow = windowManager.getAvailableWindow()
|
||||
dialog.showMessageBox(currentWindow, {
|
||||
dialog
|
||||
.showMessageBox(currentWindow, {
|
||||
title: action.data?.title || '',
|
||||
message: action.data?.content || '',
|
||||
type: 'info',
|
||||
buttons: action.data?.buttons?.map(item => item.label) || ['Yes']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
const button = action.data?.buttons?.[res.response]
|
||||
if (button?.type === 'cancel') {
|
||||
// do nothing
|
||||
@@ -198,6 +194,4 @@ class RemoteNoticeHandler {
|
||||
|
||||
const remoteNoticeHandler = new RemoteNoticeHandler()
|
||||
|
||||
export {
|
||||
remoteNoticeHandler
|
||||
}
|
||||
export { remoteNoticeHandler }
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
import {
|
||||
globalShortcut
|
||||
} from 'electron'
|
||||
import { globalShortcut } from 'electron'
|
||||
|
||||
import shortKeyService from 'apis/app/shortKey/shortKeyService'
|
||||
import GuiApi from 'apis/gui'
|
||||
@@ -72,7 +69,12 @@ class ShortKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
|
||||
private registerShortKey(
|
||||
config: IShortKeyConfig | IPluginShortKeyConfig,
|
||||
command: string,
|
||||
handler: IShortKeyHandler,
|
||||
writeFlag: boolean
|
||||
) {
|
||||
shortKeyService.registerCommand(command, handler)
|
||||
if (config.key) {
|
||||
globalShortcut.register(config.key, () => {
|
||||
|
||||
@@ -75,7 +75,13 @@ export function createMenu () {
|
||||
label: 'PicList',
|
||||
submenu: [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } }
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
@@ -93,9 +99,7 @@ export function createMenu () {
|
||||
},
|
||||
{
|
||||
label: T('QUIT'),
|
||||
submenu: [
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
submenu: [{ label: T('QUIT'), role: 'quit' }]
|
||||
}
|
||||
])
|
||||
Menu.setApplicationMenu(appMenu)
|
||||
@@ -104,7 +108,8 @@ export function createMenu () {
|
||||
export function createContextMenu() {
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const isMiniWindowVisible = windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
|
||||
const isMiniWindowVisible =
|
||||
windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
|
||||
|
||||
const startWatchClipboard = () => {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
@@ -125,18 +130,44 @@ export function createContextMenu () {
|
||||
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } },
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click: startWatchClipboard,
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click: stopWatchClipboard,
|
||||
visible: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
if (process.platform === 'win32') {
|
||||
template.splice(2, 0,
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible }
|
||||
template.splice(
|
||||
2,
|
||||
0,
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click: hideMiniWindow,
|
||||
visible: isMiniWindowVisible
|
||||
}
|
||||
)
|
||||
}
|
||||
contextMenu = Menu.buildFromTemplate(template)
|
||||
@@ -150,10 +181,28 @@ export function createContextMenu () {
|
||||
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click: hideMiniWindow,
|
||||
visible: isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click: startWatchClipboard,
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click: stopWatchClipboard,
|
||||
visible: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('ABOUT'),
|
||||
click() {
|
||||
@@ -260,9 +309,7 @@ export function createTray (tooltip: string) {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
const rawInput = cloneDeep(files)
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const imgs = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.upload(files)
|
||||
const imgs = await uploader.setWebContents(trayWindow.webContents).upload(files)
|
||||
const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
@@ -270,8 +317,11 @@ export function createTray (tooltip: string) {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import { Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
@@ -19,7 +16,10 @@ import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {
|
||||
const useBuiltinClipboard = db.get(configPaths.settings.useBuiltinClipboard) === undefined ? true : !!db.get(configPaths.settings.useBuiltinClipboard)
|
||||
const useBuiltinClipboard =
|
||||
db.get(configPaths.settings.useBuiltinClipboard) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.useBuiltinClipboard)
|
||||
const win = windowManager.getAvailableWindow()
|
||||
if (useBuiltinClipboard) {
|
||||
return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboard()
|
||||
@@ -33,8 +33,11 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
||||
if (img.length > 0) {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
handleCopyUrl(await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
@@ -75,7 +78,10 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<IStringKeyMap[]> => {
|
||||
export const uploadChoosedFiles = async (
|
||||
webContents: WebContents,
|
||||
files: IFileWithPath[]
|
||||
): Promise<IStringKeyMap[]> => {
|
||||
const input = files.map(item => item.path)
|
||||
const rawInput = cloneDeep(input)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
@@ -86,14 +92,19 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (deleteLocalFile) {
|
||||
fs.remove(rawInput[i]).then(() => {
|
||||
fs.remove(rawInput[i])
|
||||
.then(() => {
|
||||
picgo.log.info(`delete local file: ${rawInput[i]}`)
|
||||
}).catch((err: Error) => {
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
picgo.log.error(err)
|
||||
})
|
||||
}
|
||||
pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
BrowserWindow,
|
||||
clipboard,
|
||||
ipcMain,
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import { BrowserWindow, clipboard, ipcMain, Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import util from 'util'
|
||||
import path from 'path'
|
||||
@@ -21,17 +15,13 @@ import logger from '@core/picgo/logger'
|
||||
import { T } from '~/i18n'
|
||||
import { showNotification, getClipboardFilePath, calcDurationRange } from '~/utils/common'
|
||||
|
||||
import {
|
||||
GET_RENAME_FILE_NAME,
|
||||
RENAME_FILE_NAME,
|
||||
TALKING_DATA_EVENT
|
||||
} from '#/events/constants'
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME, TALKING_DATA_EVENT } from '#/events/constants'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
|
||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const windowId = window.id
|
||||
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: Event, newName: string) => {
|
||||
resolve(newName)
|
||||
@@ -91,7 +81,8 @@ class Uploader {
|
||||
const rename = db.get(configPaths.settings.rename)
|
||||
const autoRename = db.get(configPaths.settings.autoRename)
|
||||
if (autoRename || rename) {
|
||||
await Promise.all(ctx.output.map(async (item, index) => {
|
||||
await Promise.all(
|
||||
ctx.output.map(async (item, index) => {
|
||||
let name: undefined | string | null
|
||||
let fileName: string | undefined
|
||||
if (autoRename) {
|
||||
@@ -102,7 +93,7 @@ class Uploader {
|
||||
if (rename) {
|
||||
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
||||
logger.info('create rename window')
|
||||
ipcMain.on(GET_RENAME_FILE_NAME, (evt) => {
|
||||
ipcMain.on(GET_RENAME_FILE_NAME, evt => {
|
||||
try {
|
||||
if (evt.sender.id === window.webContents.id) {
|
||||
logger.info('rename window ready, wait for rename...')
|
||||
@@ -115,7 +106,8 @@ class Uploader {
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
item.fileName = name || fileName
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
export const MANUAL_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#documents`
|
||||
export const MANUAL_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#documents`
|
||||
: 'picgo://./index.html#documents'
|
||||
|
||||
export const MINI_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#mini-page`
|
||||
: 'picgo://./index.html#mini-page'
|
||||
|
||||
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
|
||||
export const RENAME_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#rename-page`
|
||||
: 'picgo://./index.html#rename-page'
|
||||
|
||||
export const SETTING_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload`
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#main-page/upload`
|
||||
: 'picgo://./index.html#main-page/upload'
|
||||
|
||||
export const TRAY_WINDOW_URL = isDevelopment
|
||||
? (process.env.WEBPACK_DEV_SERVER_URL as string)
|
||||
: 'picgo://./index.html'
|
||||
export const TRAY_WINDOW_URL = isDevelopment ? (process.env.WEBPACK_DEV_SERVER_URL as string) : 'picgo://./index.html'
|
||||
|
||||
export const TOOLBOX_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#toolbox-page`
|
||||
export const TOOLBOX_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#toolbox-page`
|
||||
: 'picgo://./index.html#toolbox-page'
|
||||
|
||||
@@ -23,8 +23,11 @@ const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
|
||||
const handleWindowParams = (windowURL: string) => windowURL
|
||||
|
||||
const getDefaultWindowSizes = (): { width: number, height: number } => {
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([configPaths.settings.mainWindowWidth, configPaths.settings.mainWindowHeight])
|
||||
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([
|
||||
configPaths.settings.mainWindowWidth,
|
||||
configPaths.settings.mainWindowHeight
|
||||
])
|
||||
return {
|
||||
width: mainWindowWidth || 1200,
|
||||
height: mainWindowHeight || 800
|
||||
@@ -237,7 +240,8 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
||||
// if is the settingWindow
|
||||
if (bounds.height > 400) {
|
||||
positionY = bounds.y + bounds.height / 2 - 88
|
||||
} else { // if is the miniWindow
|
||||
} else {
|
||||
// if is the miniWindow
|
||||
positionY = bounds.y + bounds.height / 2
|
||||
}
|
||||
window.setPosition(positionX, positionY, false)
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
} from '@core/bus/constants'
|
||||
|
||||
export const uploadWithClipboardFiles = (): Promise<{
|
||||
success: boolean,
|
||||
success: boolean
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
|
||||
if (result) {
|
||||
return resolve({
|
||||
@@ -31,11 +31,13 @@ export const uploadWithClipboardFiles = (): Promise<{
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
success: boolean,
|
||||
export const uploadWithFiles = (
|
||||
pathList: IFileWithPath[]
|
||||
): Promise<{
|
||||
success: boolean
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
|
||||
if (result.length) {
|
||||
return resolve({
|
||||
@@ -55,7 +57,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
// get available window id:
|
||||
// miniWindow or settingWindow or trayWindow
|
||||
export const getWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
@@ -65,7 +67,7 @@ export const getWindowId = (): Promise<number> => {
|
||||
|
||||
// get settingWindow id:
|
||||
export const getSettingWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
|
||||
@@ -54,7 +54,9 @@ function dbChecker () {
|
||||
fs.unlinkSync(configFilePath)
|
||||
if (fs.existsSync(configFileBackupPath)) {
|
||||
try {
|
||||
configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' })
|
||||
configFile = fs.readFileSync(configFileBackupPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
JSON.parse(configFile)
|
||||
writeFile.sync(configFilePath, configFile, { encoding: 'utf-8' })
|
||||
const stats = fs.statSync(configFileBackupPath)
|
||||
@@ -91,7 +93,9 @@ function dbPathChecker (): string {
|
||||
return _configFilePath
|
||||
}
|
||||
try {
|
||||
const configString = fs.readFileSync(defaultConfigPath, { encoding: 'utf-8' })
|
||||
const configString = fs.readFileSync(defaultConfigPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const config = JSON.parse(configString)
|
||||
const userConfigPath: string = config.configPath || ''
|
||||
if (userConfigPath) {
|
||||
@@ -135,9 +139,4 @@ function getGalleryDBPath (): {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
dbChecker,
|
||||
dbPathChecker,
|
||||
dbPathDir,
|
||||
getGalleryDBPath
|
||||
}
|
||||
export { dbChecker, dbPathChecker, dbPathDir, getGalleryDBPath }
|
||||
|
||||
@@ -115,6 +115,4 @@ class GalleryDB {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
GalleryDB
|
||||
}
|
||||
export { GalleryDB }
|
||||
|
||||
@@ -94,6 +94,4 @@ const getLogger = (logPath: string, logType: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getLogger
|
||||
}
|
||||
export { getLogger }
|
||||
|
||||
+32
-27
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
BrowserWindow,
|
||||
dialog,
|
||||
ipcMain,
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { BrowserWindow, dialog, ipcMain, Notification } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { DBStore } from '@picgo/store'
|
||||
@@ -47,7 +42,7 @@ class GuiApi implements IGuiApi {
|
||||
return true
|
||||
}
|
||||
settingWindow?.show()
|
||||
return new Promise<void>((resolve) => {
|
||||
return new Promise<void>(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, 1000) // TODO: a better way to wait page loaded.
|
||||
@@ -58,13 +53,15 @@ class GuiApi implements IGuiApi {
|
||||
return BrowserWindow.fromId(id)?.webContents
|
||||
}
|
||||
|
||||
async showInputBox (options: IShowInputBoxOption = {
|
||||
async showInputBox(
|
||||
options: IShowInputBoxOption = {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
await this.showSettingWindow()
|
||||
this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
|
||||
return new Promise<string>((resolve) => {
|
||||
return new Promise<string>(resolve => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (_: Event, value: string) => {
|
||||
resolve(value)
|
||||
})
|
||||
@@ -90,8 +87,11 @@ class GuiApi implements IGuiApi {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
@@ -112,10 +112,12 @@ class GuiApi implements IGuiApi {
|
||||
return []
|
||||
}
|
||||
|
||||
showNotification (options: IShowNotificationOption = {
|
||||
showNotification(
|
||||
options: IShowNotificationOption = {
|
||||
title: '',
|
||||
body: ''
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body
|
||||
@@ -123,18 +125,17 @@ class GuiApi implements IGuiApi {
|
||||
notification.show()
|
||||
}
|
||||
|
||||
showMessageBox (options: IShowMessageBoxOption = {
|
||||
showMessageBox(
|
||||
options: IShowMessageBoxOption = {
|
||||
title: '',
|
||||
message: '',
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}) {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve) => {
|
||||
}
|
||||
) {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
this.windowId = await getWindowId()
|
||||
dialog.showMessageBox(
|
||||
BrowserWindow.fromId(this.windowId)!,
|
||||
options
|
||||
).then((res) => {
|
||||
dialog.showMessageBox(BrowserWindow.fromId(this.windowId)!, options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
@@ -162,14 +163,16 @@ class GuiApi implements IGuiApi {
|
||||
if (prop === 'overwrite') {
|
||||
return new Proxy(GalleryDB.getInstance().overwrite, {
|
||||
apply(target, ctx, args) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi.showMessageBox({
|
||||
guiApi
|
||||
.showMessageBox({
|
||||
title: T('TIPS_WARNING'),
|
||||
message: T('TIPS_PLUGIN_REMOVE_GALLERY_ITEM'),
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.result === 0) {
|
||||
resolve(Reflect.apply(target, ctx, args))
|
||||
} else {
|
||||
@@ -183,14 +186,16 @@ class GuiApi implements IGuiApi {
|
||||
if (prop === 'removeById') {
|
||||
return new Proxy(GalleryDB.getInstance().removeById, {
|
||||
apply(target, ctx, args) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi.showMessageBox({
|
||||
guiApi
|
||||
.showMessageBox({
|
||||
title: T('TIPS_WARNING'),
|
||||
message: T('TIPS_PLUGIN_REMOVE_GALLERY_ITEM'),
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.result === 0) {
|
||||
resolve(Reflect.apply(target, ctx, args))
|
||||
} else {
|
||||
|
||||
@@ -11,13 +11,8 @@ import {
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE
|
||||
} from '@core/bus/constants'
|
||||
|
||||
import {
|
||||
createMenu
|
||||
} from 'apis/app/system'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { createMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
shell,
|
||||
MenuItemConstructorOptions,
|
||||
MenuItem
|
||||
} from 'electron'
|
||||
import { app, dialog, BrowserWindow, Menu, shell, MenuItemConstructorOptions, MenuItem } from 'electron'
|
||||
import { PicGo as PicGoCore } from 'piclist'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import {
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
@@ -47,7 +37,7 @@ const buildMiniPageMenu = () => {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click: openMainWindow
|
||||
@@ -145,8 +135,7 @@ const buildMainPageMenu = (win: BrowserWindow) => {
|
||||
shell.openExternal(url)
|
||||
}
|
||||
}
|
||||
]
|
||||
// @ts-ignore
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
return Menu.buildFromTemplate(template)
|
||||
}
|
||||
|
||||
@@ -155,28 +144,33 @@ const buildPicBedListMenu = () => {
|
||||
const currentPicBed = picgo.getConfig(configPaths.picBed.uploader)
|
||||
const currentPicBedName = picBeds.find(item => item.type === currentPicBed)?.name
|
||||
const picBedConfigList = picgo.getConfig<IUploaderConfig>('uploader')
|
||||
const currentPicBedMenuItem = [{
|
||||
const currentPicBedMenuItem = [
|
||||
{
|
||||
label: `${T('CURRENT_PICBED')} - ${currentPicBedName}`,
|
||||
enabled: false
|
||||
}, {
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
}]
|
||||
let submenu = picBeds.filter(item => item.visible).map(item => {
|
||||
}
|
||||
]
|
||||
let submenu = picBeds
|
||||
.filter(item => item.visible)
|
||||
.map(item => {
|
||||
const configList = picBedConfigList?.[item.type]?.configList
|
||||
const defaultId = picBedConfigList?.[item.type]?.defaultId
|
||||
const hasSubmenu = !!configList
|
||||
return {
|
||||
label: item.name,
|
||||
type: !hasSubmenu ? 'checkbox' : undefined,
|
||||
checked: !hasSubmenu ? (currentPicBed === item.type) : undefined,
|
||||
checked: !hasSubmenu ? currentPicBed === item.type : undefined,
|
||||
submenu: hasSubmenu
|
||||
? configList.map((config) => {
|
||||
? configList.map(config => {
|
||||
return {
|
||||
label: config._configName || 'Default',
|
||||
// if only one config, use checkbox, or radio will checked as default
|
||||
// see: https://github.com/electron/electron/issues/21292
|
||||
type: 'checkbox',
|
||||
checked: config._id === defaultId && (item.type === currentPicBed),
|
||||
checked: config._id === defaultId && item.type === currentPicBed,
|
||||
click: function () {
|
||||
changeCurrentUploader(item.type, config, config._id)
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
@@ -201,9 +195,9 @@ const buildPicBedListMenu = () => {
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
// @ts-expect-error submenu type
|
||||
submenu = currentPicBedMenuItem.concat(submenu)
|
||||
// @ts-ignore
|
||||
// @ts-expect-error submenu type
|
||||
return Menu.buildFromTemplate(submenu)
|
||||
}
|
||||
|
||||
@@ -230,7 +224,8 @@ const handleRestoreState = (item: string, name: string): void => {
|
||||
}
|
||||
|
||||
const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
const menu = [{
|
||||
const menu = [
|
||||
{
|
||||
label: T('ENABLE_PLUGIN'),
|
||||
enabled: !plugin.enabled,
|
||||
click() {
|
||||
@@ -240,7 +235,8 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('DISABLE_PLUGIN'),
|
||||
enabled: plugin.enabled,
|
||||
click() {
|
||||
@@ -258,21 +254,24 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
handleRestoreState('uploader', plugin.config.uploader.name)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('UNINSTALL_PLUGIN'),
|
||||
click() {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUninstall(plugin.fullName)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('UPDATE_PLUGIN'),
|
||||
click() {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUpdate(plugin.fullName)
|
||||
}
|
||||
}]
|
||||
}
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
for (const i in plugin.config) {
|
||||
if (plugin.config[i].config.length > 0) {
|
||||
const obj = {
|
||||
@@ -317,7 +316,6 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
// plugin custom menus
|
||||
if (plugin.guiMenu) {
|
||||
menu.push({
|
||||
// @ts-ignore
|
||||
type: 'separator'
|
||||
})
|
||||
for (const i of plugin.guiMenu) {
|
||||
@@ -338,13 +336,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return Menu.buildFromTemplate(menu)
|
||||
}
|
||||
|
||||
export {
|
||||
buildMiniPageMenu,
|
||||
buildMainPageMenu,
|
||||
buildPicBedListMenu,
|
||||
buildPluginPageMenu
|
||||
}
|
||||
export { buildMiniPageMenu, buildMainPageMenu, buildPicBedListMenu, buildPluginPageMenu }
|
||||
|
||||
@@ -82,6 +82,4 @@ for (const route of routes) {
|
||||
rpcServer.use(route)
|
||||
}
|
||||
|
||||
export {
|
||||
rpcServer
|
||||
}
|
||||
export { rpcServer }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { clipboard } from 'electron'
|
||||
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
@@ -8,7 +7,12 @@ import { IFilter, IObject } from '@picgo/store/dist/types'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { removeFileFromDogeInMain, removeFileFromHuaweiInMain, removeFileFromS3InMain, removeFileFromSFTPInMain } from '~/utils/deleteFunc'
|
||||
import {
|
||||
removeFileFromDogeInMain,
|
||||
removeFileFromHuaweiInMain,
|
||||
removeFileFromS3InMain,
|
||||
removeFileFromSFTPInMain
|
||||
} from '~/utils/deleteFunc'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum'
|
||||
@@ -128,6 +132,4 @@ const galleryRoutes = [
|
||||
|
||||
galleryRouter.addBatch(galleryRoutes)
|
||||
|
||||
export {
|
||||
galleryRouter
|
||||
}
|
||||
export { galleryRouter }
|
||||
|
||||
@@ -94,6 +94,4 @@ const picbedRoutes = [
|
||||
|
||||
picbedRouter.addBatch(picbedRoutes)
|
||||
|
||||
export {
|
||||
picbedRouter
|
||||
}
|
||||
export { picbedRouter }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
pluginImportLocalFunc,
|
||||
@@ -32,6 +31,4 @@ const pluginRoutes = [
|
||||
|
||||
pluginRouter.addBatch(pluginRoutes)
|
||||
|
||||
export {
|
||||
pluginRouter
|
||||
}
|
||||
export { pluginRouter }
|
||||
|
||||
@@ -104,11 +104,13 @@ const getPluginList = (): IPicGoPlugin[] => {
|
||||
const handleNPMError = (): IDispose => {
|
||||
const handler = (msg: string) => {
|
||||
if (msg === 'NPM is not installed') {
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
title: T('TIPS_ERROR'),
|
||||
message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'),
|
||||
buttons: ['Yes']
|
||||
}).then((res) => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.response === 0) {
|
||||
shell.openExternal('https://nodejs.org/')
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import server from '~/server'
|
||||
import webServer from '~/server/webServer'
|
||||
|
||||
@@ -16,19 +16,15 @@ export default [
|
||||
action: IRPCActionType.CONFIGURE_MIGRATE_FROM_PICGO,
|
||||
handler: async () => {
|
||||
const picGoConfigPath = STORE_PATH.replace('piclist', 'picgo')
|
||||
const files = [
|
||||
'data.json',
|
||||
'data.bak.json',
|
||||
'picgo.db',
|
||||
'picgo.bak.db'
|
||||
]
|
||||
const files = ['data.json', 'data.bak.json', 'picgo.db', 'picgo.bak.db']
|
||||
try {
|
||||
await Promise.all(files.map(async file => {
|
||||
await Promise.all(
|
||||
files.map(async file => {
|
||||
const sourcePath = path.join(picGoConfigPath, file)
|
||||
const targetPath = path.join(STORE_PATH, file.replace('picgo', 'piclist'))
|
||||
await fs.copy(sourcePath, targetPath, { overwrite: true })
|
||||
}
|
||||
))
|
||||
})
|
||||
)
|
||||
return true
|
||||
} catch (err: any) {
|
||||
logger.error(err)
|
||||
|
||||
@@ -7,15 +7,8 @@ import shortKeyRoutes from '~/events/rpc/routes/setting/shortKey'
|
||||
|
||||
const settingRouter = new RPCRouter()
|
||||
|
||||
const settingRoutes = [
|
||||
...advancedRoutes,
|
||||
...configureRoutes,
|
||||
...mainAppRoutes,
|
||||
...shortKeyRoutes
|
||||
]
|
||||
const settingRoutes = [...advancedRoutes, ...configureRoutes, ...mainAppRoutes, ...shortKeyRoutes]
|
||||
|
||||
settingRouter.addBatch(settingRoutes)
|
||||
|
||||
export {
|
||||
settingRouter
|
||||
}
|
||||
export { settingRouter }
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
|
||||
@@ -4,13 +4,8 @@ import windowRoutes from '~/events/rpc/routes/system/window'
|
||||
|
||||
const systemRouter = new RPCRouter()
|
||||
|
||||
const systemRoutes = [
|
||||
...appRoutes,
|
||||
...windowRoutes
|
||||
]
|
||||
const systemRoutes = [...appRoutes, ...windowRoutes]
|
||||
|
||||
systemRouter.addBatch(systemRoutes)
|
||||
|
||||
export {
|
||||
systemRouter
|
||||
}
|
||||
export { systemRouter }
|
||||
|
||||
@@ -2,12 +2,7 @@ import { app, BrowserWindow } from 'electron'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import {
|
||||
buildMainPageMenu,
|
||||
buildMiniPageMenu,
|
||||
buildPicBedListMenu,
|
||||
buildPluginPageMenu
|
||||
} from '~/events/remotes/menu'
|
||||
import { buildMainPageMenu, buildMiniPageMenu, buildPicBedListMenu, buildPluginPageMenu } from '~/events/remotes/menu'
|
||||
import { openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
|
||||
@@ -14,10 +14,8 @@ const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_
|
||||
|
||||
const defaultClipboardImagePath = path.join(defaultConfigPath, CLIPBOARD_IMAGE_FOLDER)
|
||||
|
||||
export const checkClipboardUploadMap: IToolboxCheckerMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async (event) => {
|
||||
export const checkClipboardUploadMap: IToolboxCheckerMap<IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async event => {
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
})
|
||||
@@ -54,9 +52,7 @@ IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
}
|
||||
}
|
||||
|
||||
export const fixClipboardUploadMap: IToolboxFixMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
> = {
|
||||
export const fixClipboardUploadMap: IToolboxFixMap<IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async () => {
|
||||
const configFilePath = dbPathChecker()
|
||||
const dirPath = path.dirname(configFilePath)
|
||||
|
||||
@@ -38,7 +38,7 @@ IToolboxItemType.IS_CONFIG_FILE_BROKEN | IToolboxItemType.IS_GALLERY_FILE_BROKEN
|
||||
})
|
||||
}
|
||||
},
|
||||
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: async (event) => {
|
||||
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: async event => {
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.IS_GALLERY_FILE_BROKEN)
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
import { IConfig } from 'piclist'
|
||||
@@ -27,10 +26,8 @@ function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null {
|
||||
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY)
|
||||
|
||||
export const checkProxyMap: IToolboxCheckerMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_PROXY
|
||||
> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async (event) => {
|
||||
export const checkProxyMap: IToolboxCheckerMap<IToolboxItemType.HAS_PROBLEM_WITH_PROXY> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async event => {
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
})
|
||||
@@ -38,9 +35,8 @@ IToolboxItemType.HAS_PROBLEM_WITH_PROXY
|
||||
if (fs.existsSync(configFilePath)) {
|
||||
let config: IConfig | undefined
|
||||
try {
|
||||
config = await fs.readJSON(configFilePath) as IConfig
|
||||
} catch (e) {
|
||||
}
|
||||
config = (await fs.readJSON(configFilePath)) as IConfig
|
||||
} catch (e) {}
|
||||
if (!config) {
|
||||
return sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.SUCCESS,
|
||||
|
||||
@@ -42,7 +42,8 @@ toolboxRouter
|
||||
IRPCType.SEND
|
||||
)
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX, async (event, args) => {
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX,
|
||||
async (event, args) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
const handler = toolboxFixMap[type]
|
||||
if (handler) {
|
||||
@@ -52,6 +53,4 @@ toolboxRouter
|
||||
IRPCType.INVOKE
|
||||
)
|
||||
|
||||
export {
|
||||
toolboxRouter
|
||||
}
|
||||
export { toolboxRouter }
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { generateShortUrl, setTrayToolTip, handleCopyUrl } from '~/utils/common'
|
||||
@@ -42,8 +40,11 @@ const trayRoutes = [
|
||||
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))))
|
||||
const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification)
|
||||
handleCopyUrl(await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
@@ -66,6 +67,4 @@ const trayRoutes = [
|
||||
|
||||
trayRouter.addBatch(trayRoutes)
|
||||
|
||||
export {
|
||||
trayRouter
|
||||
}
|
||||
export { trayRouter }
|
||||
|
||||
@@ -30,6 +30,4 @@ const uploadRoutes = [
|
||||
|
||||
uploadRouter.addBatch(uploadRoutes)
|
||||
|
||||
export {
|
||||
uploadRouter
|
||||
}
|
||||
export { uploadRouter }
|
||||
|
||||
@@ -27,9 +27,11 @@ export function startFileServer () {
|
||||
})
|
||||
})
|
||||
|
||||
server.listen(serverPort, () => {
|
||||
server
|
||||
.listen(serverPort, () => {
|
||||
logger.info(`File server is running, http://localhost:${serverPort}`)
|
||||
}).on('error', (err) => {
|
||||
})
|
||||
.on('error', err => {
|
||||
logger.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// TODO: so how to import pure esm module in electron main process????? help wanted
|
||||
|
||||
// just copy the fix-path because I can't import pure ESM module in electron main process
|
||||
// @ts-nocheck
|
||||
// @ts-nocheck since the module is not pure ESM
|
||||
// shell-path 3.0.0 not work
|
||||
|
||||
const shellPath = require('shell-path')
|
||||
@@ -11,10 +11,6 @@ export default function fixPath () {
|
||||
return
|
||||
}
|
||||
|
||||
process.env.PATH = shellPath.sync() || [
|
||||
'./node_modules/.bin',
|
||||
'/.nodebrew/current/bin',
|
||||
'/usr/local/bin',
|
||||
process.env.PATH
|
||||
].join(':')
|
||||
process.env.PATH =
|
||||
shellPath.sync() || ['./node_modules/.bin', '/.nodebrew/current/bin', '/usr/local/bin', process.env.PATH].join(':')
|
||||
}
|
||||
|
||||
+30
-35
@@ -1,19 +1,9 @@
|
||||
import axios from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
import {
|
||||
app,
|
||||
globalShortcut,
|
||||
protocol,
|
||||
Notification,
|
||||
dialog,
|
||||
screen,
|
||||
shell
|
||||
} from 'electron'
|
||||
import { app, globalShortcut, protocol, Notification, dialog, screen, shell } from 'electron'
|
||||
import { UpdateInfo, autoUpdater } from 'electron-updater'
|
||||
import path from 'path'
|
||||
import {
|
||||
createProtocol
|
||||
} from 'vue-cli-plugin-electron-builder/lib'
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
@@ -22,13 +12,8 @@ import logger from '@core/picgo/logger'
|
||||
|
||||
import { remoteNoticeHandler } from 'apis/app/remoteNotice'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import {
|
||||
createTray, setDockMenu
|
||||
} from 'apis/app/system'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { createTray, setDockMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import busEventList from '~/events/busEventList'
|
||||
@@ -57,7 +42,8 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
const files = getUploadFiles(argv, cwd, logger)
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null || files.length > 0) {
|
||||
// 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
logger.info('cli -> uploading file from clipboard')
|
||||
uploadClipboardFiles()
|
||||
@@ -83,34 +69,43 @@ autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
let updateLog = ''
|
||||
try {
|
||||
const url = lang === II18nLanguage.ZH_CN ? 'https://release.piclist.cn/currentVersion.md' : 'https://release.piclist.cn/currentVersion_en.md'
|
||||
const url =
|
||||
lang === II18nLanguage.ZH_CN
|
||||
? 'https://release.piclist.cn/currentVersion.md'
|
||||
: 'https://release.piclist.cn/currentVersion_en.md'
|
||||
const res = await axios.get(url)
|
||||
updateLog = res.data
|
||||
} catch (e: any) {
|
||||
logger.error(e)
|
||||
}
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: T('FIND_NEW_VERSION'),
|
||||
buttons: ['Yes', 'Go to download page'],
|
||||
message: T('TIPS_FIND_NEW_VERSION', {
|
||||
message:
|
||||
T('TIPS_FIND_NEW_VERSION', {
|
||||
v: info.version
|
||||
}) + '\n\n' + updateLog,
|
||||
}) +
|
||||
'\n\n' +
|
||||
updateLog,
|
||||
checkboxLabel: T('NO_MORE_NOTICE'),
|
||||
checkboxChecked: false
|
||||
}).then((result) => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.response === 0) {
|
||||
autoUpdater.downloadUpdate()
|
||||
} else {
|
||||
shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest')
|
||||
}
|
||||
db.set(configPaths.settings.showUpdateTip, !result.checkboxChecked)
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('download-progress', (progressObj) => {
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
const percent = {
|
||||
progress: progressObj.percent
|
||||
}
|
||||
@@ -119,23 +114,26 @@ autoUpdater.on('download-progress', (progressObj) => {
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: T('UPDATE_DOWNLOADED'),
|
||||
buttons: ['Yes', 'No'],
|
||||
message: T('TIPS_UPDATE_DOWNLOADED')
|
||||
}).then((result) => {
|
||||
})
|
||||
.then(result => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', { progress: 100 })
|
||||
if (result.response === 0) {
|
||||
autoUpdater.quitAndInstall()
|
||||
}
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
autoUpdater.on('error', err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
@@ -173,7 +171,6 @@ class LifeCycle {
|
||||
const isHideDock = db.get(configPaths.settings.isHideDock) || false
|
||||
const startMode = db.get(configPaths.settings.startMode) || ISartMode.QUIET
|
||||
const currentPicBed = db.get(configPaths.picBed.uploader) || db.get(configPaths.picBed.current) || 'smms'
|
||||
// @ts-ignore
|
||||
const currentPicBedConfig = db.get(`picBed.${currentPicBed}`)?._configName || 'Default'
|
||||
const tooltip = `${currentPicBed} ${currentPicBedConfig}`
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -319,6 +316,4 @@ class LifeCycle {
|
||||
|
||||
const bootstrap = new LifeCycle()
|
||||
|
||||
export {
|
||||
bootstrap
|
||||
}
|
||||
export { bootstrap }
|
||||
|
||||
@@ -8,7 +8,13 @@ import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { hmacSha1Base64, getFileMimeType, formatError, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import {
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
formatError,
|
||||
NewDownloader,
|
||||
ConcurrencyPromisePool
|
||||
} from '~/manage/utils/common'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
@@ -72,13 +78,19 @@ class AliyunApi {
|
||||
}, {} as IStringKeyMap)
|
||||
let canonicalizedOSSHeaders = ''
|
||||
const headerKeys = Object.keys(lowerCaseHeaders).sort()
|
||||
headerKeys.forEach((key) => {
|
||||
headerKeys.forEach(key => {
|
||||
key.startsWith('x-oss-') && (canonicalizedOSSHeaders += `${key}:${lowerCaseHeaders[key]}\n`)
|
||||
})
|
||||
return canonicalizedOSSHeaders
|
||||
}
|
||||
|
||||
authorization (method: string, canonicalizedResource: string, headers: IStringKeyMap, contentMd5: string, contentType: string) {
|
||||
authorization(
|
||||
method: string,
|
||||
canonicalizedResource: string,
|
||||
headers: IStringKeyMap,
|
||||
contentMd5: string,
|
||||
contentType: string
|
||||
) {
|
||||
const date = new Date().toUTCString()
|
||||
const stringToSign = `${method.toUpperCase()}\n${contentMd5}\n${contentType}\n${date}\n${this.getCanonicalizedOSSHeaders(headers)}${canonicalizedResource}`
|
||||
return `OSS ${this.accessKeyId}:${hmacSha1Base64(this.accessKeySecret, stringToSign)}`
|
||||
@@ -99,17 +111,21 @@ class AliyunApi {
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
const getBuckets = async (marker?: string) => {
|
||||
const res = await this.ctx.listBuckets({
|
||||
const res = (await this.ctx.listBuckets({
|
||||
marker,
|
||||
'max-keys': 1000
|
||||
}) as IStringKeyMap
|
||||
})) as IStringKeyMap
|
||||
if (res?.res?.statusCode !== 200 || !res?.buckets) return { result: [], isTruncated: false }
|
||||
const formattedBuckets = res.buckets.map((item: OSS.Bucket) => ({
|
||||
Name: item.name,
|
||||
Location: item.region,
|
||||
CreationDate: item.creationDate
|
||||
}))
|
||||
return { result: formattedBuckets, isTruncated: res.isTruncated, nextMarker: res.nextMarker }
|
||||
return {
|
||||
result: formattedBuckets,
|
||||
isTruncated: res.isTruncated,
|
||||
nextMarker: res.nextMarker
|
||||
}
|
||||
}
|
||||
const result: IStringKeyMap[] = []
|
||||
let NextMarker: string | undefined
|
||||
@@ -193,7 +209,12 @@ class AliyunApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
let marker
|
||||
@@ -212,13 +233,16 @@ class AliyunApi {
|
||||
}
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
do {
|
||||
res = await client.listV2({
|
||||
res = await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
})
|
||||
}
|
||||
)
|
||||
if (res?.res?.statusCode === 200) {
|
||||
res?.objects?.forEach((item: OSS.ObjectMeta) => {
|
||||
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
@@ -240,7 +264,12 @@ class AliyunApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
let marker
|
||||
@@ -259,14 +288,17 @@ class AliyunApi {
|
||||
}
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
do {
|
||||
res = await client.listV2({
|
||||
res = await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
delimiter: '/',
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
})
|
||||
}
|
||||
)
|
||||
if (res?.res?.statusCode === 200) {
|
||||
res?.prefixes?.forEach((item: string) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
@@ -305,19 +337,28 @@ class AliyunApi {
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
const res = await client.listV2({
|
||||
const res = (await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix || undefined,
|
||||
delimiter: '/',
|
||||
'max-keys': itemsPerPage.toString(),
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
}) as any
|
||||
}
|
||||
)) as any
|
||||
// prefixes can be null
|
||||
// objects will be [] when no file
|
||||
if (res?.res.statusCode !== 200) {
|
||||
@@ -330,7 +371,9 @@ class AliyunApi {
|
||||
}
|
||||
const fullList = [
|
||||
...(res.prefixes?.map((item: string) => this.formatFolder(item, slicedPrefix, urlPrefix)) || []),
|
||||
...(res.objects?.filter((item: OSS.ObjectMeta) => item.size !== 0).map((item: OSS.ObjectMeta) => this.formatFile(item, slicedPrefix, urlPrefix)) || [])
|
||||
...(res.objects
|
||||
?.filter((item: OSS.ObjectMeta) => item.size !== 0)
|
||||
.map((item: OSS.ObjectMeta) => this.formatFile(item, slicedPrefix, urlPrefix)) || [])
|
||||
]
|
||||
return {
|
||||
fullList,
|
||||
@@ -353,12 +396,9 @@ class AliyunApi {
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, oldKey, newKey } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const copyRes = await client.copy(
|
||||
newKey,
|
||||
oldKey
|
||||
) as any
|
||||
const copyRes = (await client.copy(newKey, oldKey)) as any
|
||||
if (copyRes?.res.statusCode === 200) {
|
||||
const deleteRes = await client.delete(oldKey) as any
|
||||
const deleteRes = (await client.delete(oldKey)) as any
|
||||
return deleteRes?.res.statusCode === 204
|
||||
}
|
||||
return false
|
||||
@@ -376,7 +416,7 @@ class AliyunApi {
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const res = await client.delete(key) as any
|
||||
const res = (await client.delete(key)) as any
|
||||
return res?.res.statusCode === 204
|
||||
}
|
||||
|
||||
@@ -394,14 +434,17 @@ class AliyunApi {
|
||||
Contents: [] as any[]
|
||||
}
|
||||
do {
|
||||
const res = await client.listV2({
|
||||
const res = (await client.listV2(
|
||||
{
|
||||
prefix: key,
|
||||
delimiter: '/',
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
}) as any
|
||||
}
|
||||
)) as any
|
||||
if (res?.res.statusCode !== 200) return false
|
||||
|
||||
res.prefixes !== null && allFileList.CommonPrefixes.push(...res.prefixes)
|
||||
@@ -423,8 +466,9 @@ class AliyunApi {
|
||||
if (allFileList.Contents.length > 0) {
|
||||
const cycle = Math.ceil(allFileList.Contents.length / 1000)
|
||||
for (let i = 0; i < cycle; i++) {
|
||||
const deleteRes = await client.deleteMulti(
|
||||
allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => item.name)) as any
|
||||
const deleteRes = (await client.deleteMulti(
|
||||
allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => item.name)
|
||||
)) as any
|
||||
if (deleteRes?.res.statusCode !== 200) return false
|
||||
}
|
||||
}
|
||||
@@ -485,10 +529,8 @@ class AliyunApi {
|
||||
targetFileBucket: bucketName,
|
||||
targetFileRegion: region
|
||||
})
|
||||
client.multipartUpload(
|
||||
key,
|
||||
filePath,
|
||||
{
|
||||
client
|
||||
.multipartUpload(key, filePath, {
|
||||
partSize: 1 * 1024 * 1024,
|
||||
mime: getFileMimeType(fileName),
|
||||
progress: (p: number) => {
|
||||
@@ -499,8 +541,8 @@ class AliyunApi {
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
}
|
||||
}
|
||||
).then((res: any) => {
|
||||
})
|
||||
.then((res: any) => {
|
||||
const id = `${bucketName}-${region}-${key}-${filePath}`
|
||||
if (res?.res?.statusCode === 200) {
|
||||
instance.updateUploadTask({
|
||||
@@ -519,8 +561,14 @@ class AliyunApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((err: any) => {
|
||||
this.logger.error(formatError(err, { class: 'AliyunApi', method: 'uploadBucketFile' }))
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
class: 'AliyunApi',
|
||||
method: 'uploadBucketFile'
|
||||
})
|
||||
)
|
||||
const id = `${bucketName}-${region}-${key}-${filePath}`
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -541,7 +589,7 @@ class AliyunApi {
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const res = await client.put(key, Buffer.from('')) as any
|
||||
const res = (await client.put(key, Buffer.from(''))) as any
|
||||
return res?.res?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -571,20 +619,27 @@ class AliyunApi {
|
||||
const preSignedUrl = client.signatureUrl(key, {
|
||||
expires: 60 * 60 * 48
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error: any) => {
|
||||
this.logger.error(formatError(error, { class: 'AliyunApi', method: 'downloadBucketFile' }))
|
||||
this.logger.error(
|
||||
formatError(error, {
|
||||
class: 'AliyunApi',
|
||||
method: 'downloadBucketFile'
|
||||
})
|
||||
)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
+131
-49
@@ -6,7 +6,14 @@ import path from 'path'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { gotUpload, NewDownloader, getAgent, getOptions, ConcurrencyPromisePool, formatError } from '~/manage/utils/common'
|
||||
import {
|
||||
gotUpload,
|
||||
NewDownloader,
|
||||
getAgent,
|
||||
getOptions,
|
||||
ConcurrencyPromisePool,
|
||||
formatError
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
@@ -41,7 +48,16 @@ class GithubApi {
|
||||
rawUrl = cdnUrl
|
||||
? placeholders.some(item => cdnUrl.includes(item))
|
||||
? placeholders.reduce((url, ph) => {
|
||||
const value = ph === '{username}' ? this.username : ph === '{repo}' ? repo : ph === '{branch}' ? branch : ph === '{path}' ? key : ''
|
||||
const value =
|
||||
ph === '{username}'
|
||||
? this.username
|
||||
: ph === '{repo}'
|
||||
? repo
|
||||
: ph === '{branch}'
|
||||
? branch
|
||||
: ph === '{path}'
|
||||
? key
|
||||
: ''
|
||||
return url.replaceAll(ph, value)
|
||||
}, cdnUrl)
|
||||
: `${cdnUrl}/${key}`
|
||||
@@ -69,7 +85,16 @@ class GithubApi {
|
||||
rawUrl = cdnUrl
|
||||
? placeholders.some(item => cdnUrl.includes(item))
|
||||
? placeholders.reduce((url, ph) => {
|
||||
const value = ph === '{username}' ? this.username : ph === '{repo}' ? repo : ph === '{branch}' ? branch : ph === '{path}' ? `${slicedPrefix}/${item.path}` : ''
|
||||
const value =
|
||||
ph === '{username}'
|
||||
? this.username
|
||||
: ph === '{repo}'
|
||||
? repo
|
||||
: ph === '{branch}'
|
||||
? branch
|
||||
: ph === '{path}'
|
||||
? `${slicedPrefix}/${item.path}`
|
||||
: ''
|
||||
return url.replaceAll(ph, value)
|
||||
}, cdnUrl)
|
||||
: `${cdnUrl}/${key}`
|
||||
@@ -99,10 +124,18 @@ class GithubApi {
|
||||
let res
|
||||
const result = [] as any[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/user/repos`,
|
||||
getOptions('GET', this.commonHeaders, { page: initPage, per_page: 100 }, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{ page: initPage, per_page: 100 },
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (res.statusCode === 200) {
|
||||
res.body.forEach((item: any) => {
|
||||
result.push({
|
||||
@@ -129,10 +162,18 @@ class GithubApi {
|
||||
let res
|
||||
const result = [] as string[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/branches`,
|
||||
getOptions('GET', this.commonHeaders, { page: initPage, per_page: 100 }, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{ page: initPage, per_page: 100 },
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (res.statusCode === 200) {
|
||||
res.body.forEach((item: any) => result.push(item.name))
|
||||
} else {
|
||||
@@ -167,10 +208,10 @@ class GithubApi {
|
||||
return result
|
||||
}
|
||||
const currentPrefix = treeQueue[0]
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees/${branch}:${treeQueue.shift()}`,
|
||||
getOptions('GET', this.commonHeaders, {}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res && res.statusCode === 200) {
|
||||
const { tree } = res.body
|
||||
tree.forEach((item: any) => {
|
||||
@@ -265,65 +306,98 @@ class GithubApi {
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName: repo, githubBranch: branch, key } = configMap
|
||||
// get sha of the branch
|
||||
const refRes = await got(
|
||||
const refRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`,
|
||||
getOptions('GET', this.commonHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (refRes.statusCode !== 200) return false
|
||||
const refSha = refRes.body.object.sha
|
||||
// get sha of the root tree
|
||||
const rootRes = await got(
|
||||
const rootRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/branches/${branch}`,
|
||||
getOptions('GET', undefined, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (rootRes.statusCode !== 200) return false
|
||||
const rootSha = rootRes.body.commit.commit.tree.sha
|
||||
// TODO: if there are more than 10000 files in the folder, it will be truncated
|
||||
// Rare cases, not considered for now
|
||||
// get sha of the folder tree
|
||||
const treeRes = await got(
|
||||
const treeRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees/${branch}:${key.replace(/(^\/+|\/+$)/g, '')}`,
|
||||
getOptions('GET', this.commonHeaders, {
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{
|
||||
recursive: true
|
||||
}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
},
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (treeRes.statusCode !== 200) return false
|
||||
const oldTree = treeRes.body.tree
|
||||
// create a new tree
|
||||
const newTree = oldTree.filter((item: any) => item.type === 'blob')
|
||||
const newTree = oldTree
|
||||
.filter((item: any) => item.type === 'blob')
|
||||
.map((item: any) => ({
|
||||
path: `${key.replace(/(^\/+|\/+$)/g, '')}/${item.path}`,
|
||||
mode: item.mode,
|
||||
type: item.type,
|
||||
sha: null
|
||||
}))
|
||||
const newTreeShaRes = await got(
|
||||
const newTreeShaRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees`,
|
||||
getOptions('POST', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'POST',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
base_tree: rootSha,
|
||||
tree: newTree
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (newTreeShaRes.statusCode !== 201) return false
|
||||
const newTreeSha = newTreeShaRes.body.sha
|
||||
// create a new commit
|
||||
const commitRes = await got(
|
||||
const commitRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/commits`,
|
||||
getOptions('POST', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'POST',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
message: 'deleted by PicList',
|
||||
tree: newTreeSha,
|
||||
parents: [refSha]
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (commitRes.statusCode !== 201) return false
|
||||
const commitSha = commitRes.body.sha
|
||||
// update the branch
|
||||
const updateRefRes = await got(
|
||||
const updateRefRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`,
|
||||
getOptions('PATCH', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'PATCH',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
sha: commitSha
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
return updateRefRes.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -341,12 +415,20 @@ class GithubApi {
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName: repo, customUrl: branch, key, rawUrl, githubPrivate: isPrivate } = configMap
|
||||
if (!isPrivate) return rawUrl
|
||||
const res = await got(
|
||||
const res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/contents/${key}`,
|
||||
getOptions('GET', this.commonHeaders, {
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{
|
||||
ref: branch
|
||||
}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
},
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
return res.statusCode === 200 ? res.body.download_url : ''
|
||||
}
|
||||
|
||||
@@ -454,27 +536,27 @@ class GithubApi {
|
||||
} else {
|
||||
downloadUrl = githubUrl
|
||||
}
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(
|
||||
instance,
|
||||
downloadUrl,
|
||||
id,
|
||||
savedFilePath,
|
||||
this.logger,
|
||||
this.proxyStr
|
||||
)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, downloadUrl, id, savedFilePath, this.logger, this.proxyStr).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
this.logger.error(formatError(error, { class: 'GithubApi', method: 'downloadBucketFile' }))
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(
|
||||
formatError(error, {
|
||||
class: 'GithubApi',
|
||||
method: 'downloadBucketFile'
|
||||
})
|
||||
)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ class ImgurApi {
|
||||
let res
|
||||
const result = [] as any[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/albums/${initPage}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (!(res.statusCode === 200 && res.body.success)) {
|
||||
return []
|
||||
}
|
||||
@@ -95,7 +95,10 @@ class ImgurApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketConfig: { Location: albumHash }, cancelToken } = configMap
|
||||
const {
|
||||
bucketConfig: { Location: albumHash },
|
||||
cancelToken
|
||||
} = configMap
|
||||
const cancelTask = [false]
|
||||
ipcMain.on('cancelLoadingFileList', (_: IpcMainEvent, token: string) => {
|
||||
if (token === cancelToken) {
|
||||
@@ -110,10 +113,10 @@ class ImgurApi {
|
||||
finished: false
|
||||
}
|
||||
if (albumHash !== 'unclassified') {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/album/${albumHash}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res.statusCode === 200 && res.body.success) {
|
||||
res.body.data.images.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFile(item))
|
||||
@@ -127,10 +130,10 @@ class ImgurApi {
|
||||
} else {
|
||||
let initPage = 0
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/images/${initPage}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res.statusCode === 200 && res.body.success) {
|
||||
res.body.data.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFile(item))
|
||||
@@ -152,10 +155,10 @@ class ImgurApi {
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { DeleteHash: deleteHash } = configMap
|
||||
const res = await got(
|
||||
const res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/image/${deleteHash}`,
|
||||
getOptions('DELETE', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
return res.statusCode === 200 && res.body.success
|
||||
}
|
||||
|
||||
@@ -241,26 +244,21 @@ class ImgurApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(
|
||||
instance,
|
||||
url,
|
||||
id,
|
||||
savedFilePath,
|
||||
this.logger,
|
||||
this.proxyStr
|
||||
)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, url, id, savedFilePath, this.logger, this.proxyStr).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'ImgurApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -21,8 +21,7 @@ class LocalApi {
|
||||
this.isWindows = process.platform === 'win32'
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'LocalApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'LocalApi', method }))
|
||||
|
||||
// windows 系统下将路径转换为 unix 风格
|
||||
transPathToUnix(filePath: string | undefined) {
|
||||
@@ -33,7 +32,10 @@ class LocalApi {
|
||||
transBack(filePath: string | undefined) {
|
||||
if (!filePath) return ''
|
||||
return this.isWindows
|
||||
? filePath.split(path.posix.sep).join(path.sep).replace(/^\\+|\\+$/g, '')
|
||||
? filePath
|
||||
.split(path.posix.sep)
|
||||
.join(path.sep)
|
||||
.replace(/^\\+|\\+$/g, '')
|
||||
: `/${filePath.replace(/^\/+|\/+$/g, '')}`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import path from 'path'
|
||||
@@ -7,7 +6,13 @@ import qiniu from 'qiniu/index'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { hmacSha1Base64, getFileMimeType, NewDownloader, formatError, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import {
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
formatError,
|
||||
ConcurrencyPromisePool
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
@@ -79,7 +84,7 @@ class QiniuApi {
|
||||
if (xQiniuHeaders) {
|
||||
const xQiniuHeaderStr = Object.keys(xQiniuHeaders)
|
||||
.sort()
|
||||
.map((key) => `\n${key}:${xQiniuHeaders[key]}`)
|
||||
.map(key => `\n${key}:${xQiniuHeaders[key]}`)
|
||||
.join('')
|
||||
signStr += xQiniuHeaderStr
|
||||
}
|
||||
@@ -252,11 +257,14 @@ class QiniuApi {
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
do {
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err: any, respBody: any, respInfo: any) => {
|
||||
},
|
||||
(err: any, respBody: any, respInfo: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -265,10 +273,13 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res && res.respInfo.statusCode === 200) {
|
||||
res.respBody && res.respBody.items && res.respBody.items.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.items &&
|
||||
res.respBody.items.forEach((item: any) => {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
@@ -308,12 +319,15 @@ class QiniuApi {
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
do {
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
delimiter: '/',
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err: any, respBody: any, respInfo: any) => {
|
||||
},
|
||||
(err: any, respBody: any, respInfo: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -322,13 +336,18 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res && res.respInfo.statusCode === 200) {
|
||||
res.respBody && res.respBody.commonPrefixes && res.respBody.commonPrefixes.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.commonPrefixes &&
|
||||
res.respBody.commonPrefixes.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
res.respBody && res.respBody.items && res.respBody.items.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.items &&
|
||||
res.respBody.items.forEach((item: any) => {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
@@ -374,12 +393,15 @@ class QiniuApi {
|
||||
success: false
|
||||
}
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
limit: itemsPerPage,
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
marker,
|
||||
delimiter: '/'
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -388,7 +410,8 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
if (res.respBody?.commonPrefixes) {
|
||||
@@ -401,7 +424,7 @@ class QiniuApi {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
}
|
||||
result.isTruncated = !!(res.respBody?.marker)
|
||||
result.isTruncated = !!res.respBody?.marker
|
||||
result.nextMarker = res.respBody?.marker ? res.respBody.marker : ''
|
||||
result.success = true
|
||||
}
|
||||
@@ -421,7 +444,7 @@ class QiniuApi {
|
||||
const { bucketName, key } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.delete(bucketName, key, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -432,7 +455,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -450,12 +473,15 @@ class QiniuApi {
|
||||
Contents: [] as any[]
|
||||
}
|
||||
do {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucketName, {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(
|
||||
bucketName,
|
||||
{
|
||||
prefix: key,
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -464,13 +490,14 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
}
|
||||
)
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
if (res.respBody?.items) {
|
||||
allFileList.Contents = allFileList.Contents.concat(res.respBody.items)
|
||||
}
|
||||
isTruncated = !!(res.respBody?.marker)
|
||||
isTruncated = !!res.respBody?.marker
|
||||
marker = res.respBody?.marker ? res.respBody.marker : ''
|
||||
} else {
|
||||
return false
|
||||
@@ -481,7 +508,7 @@ class QiniuApi {
|
||||
const deleteOps = allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => {
|
||||
return qiniu.rs.deleteOp(bucketName, item.key)
|
||||
})
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.batch(deleteOps, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -492,7 +519,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode !== 200) return false
|
||||
}
|
||||
return true
|
||||
@@ -512,10 +539,16 @@ class QiniuApi {
|
||||
const { bucketName, oldKey, newKey } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
bucketManager.move(bucketName, oldKey, bucketName, newKey, {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.move(
|
||||
bucketName,
|
||||
oldKey,
|
||||
bucketName,
|
||||
newKey,
|
||||
{
|
||||
force: true
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -524,8 +557,9 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
}
|
||||
)
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -585,7 +619,7 @@ class QiniuApi {
|
||||
putExtra.version = 'v2'
|
||||
putExtra.partSize = 4 * 1024 * 1024
|
||||
putExtra.progressCallback = (uploadBytes, totalBytes) => {
|
||||
const progress = Math.floor(uploadBytes / totalBytes * 100)
|
||||
const progress = Math.floor((uploadBytes / totalBytes) * 100)
|
||||
instance.updateUploadTask({
|
||||
id: `${bucketName}-${region}-${key}-${filePath}`,
|
||||
progress,
|
||||
@@ -594,7 +628,12 @@ class QiniuApi {
|
||||
}
|
||||
resumeUploader.putFile(uploadToken, key, filePath, putExtra, (respErr, respBody, respInfo) => {
|
||||
if (respErr) {
|
||||
this.logger.error(formatError(respErr, { class: 'Qiniu', method: 'uploadBucketFile' }))
|
||||
this.logger.error(
|
||||
formatError(respErr, {
|
||||
class: 'Qiniu',
|
||||
method: 'uploadBucketFile'
|
||||
})
|
||||
)
|
||||
instance.updateUploadTask({
|
||||
id: `${bucketName}-${region}-${key}-${filePath}`,
|
||||
progress: 0,
|
||||
@@ -636,7 +675,7 @@ class QiniuApi {
|
||||
const uploadToken = putPolicy.uploadToken(this.mac)
|
||||
const FormUploader = new qiniu.form_up.FormUploader()
|
||||
const putExtra = new qiniu.form_up.PutExtra()
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
FormUploader.put(uploadToken, key, '', putExtra, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -647,7 +686,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -673,20 +712,26 @@ class QiniuApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
const preSignedUrl = await this.getPreSignedUrl({ key, expires: 36000, customUrl })
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
const preSignedUrl = await this.getPreSignedUrl({
|
||||
key,
|
||||
expires: 36000,
|
||||
customUrl
|
||||
})
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'QiniuApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -77,7 +77,7 @@ class S3plistApi {
|
||||
|
||||
async getDogeCloudToken() {
|
||||
if (!this.dogeCloudSupport) return
|
||||
const token = await getTempToken(this.accessKeyId, this.secretAccessKey) as DogecloudToken
|
||||
const token = (await getTempToken(this.accessKeyId, this.secretAccessKey)) as DogecloudToken
|
||||
if (Object.keys(token).length === 0) {
|
||||
throw new Error('manage.setting.dogeCloudTokenError')
|
||||
}
|
||||
@@ -115,8 +115,7 @@ class S3plistApi {
|
||||
})
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'S3plistApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'S3plistApi', method }))
|
||||
|
||||
formatFolder(item: CommonPrefix, slicedPrefix: string, urlPrefix: string): any {
|
||||
return {
|
||||
@@ -186,7 +185,7 @@ class S3plistApi {
|
||||
const command = new ListBucketsCommand({})
|
||||
const data = await client.send(command)
|
||||
if (data.$metadata.httpStatusCode === 200) {
|
||||
const bucketList = data.Buckets?.map((item) => item.Name)
|
||||
const bucketList = data.Buckets?.map(item => item.Name)
|
||||
if (bucketList?.includes(BucketName)) {
|
||||
return true
|
||||
}
|
||||
@@ -258,7 +257,7 @@ class S3plistApi {
|
||||
}
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const result: IStringKeyMap[] = []
|
||||
const endpoint = options.endpoint as string || ''
|
||||
const endpoint = (options.endpoint as string) || ''
|
||||
options.region = endpoint.includes('cloudflarestorage') ? 'auto' : 'us-east-1'
|
||||
try {
|
||||
const client = new S3Client(options)
|
||||
@@ -271,21 +270,26 @@ class S3plistApi {
|
||||
|
||||
if (data.Buckets) {
|
||||
if (endpoint.includes('cloudflarestorage')) {
|
||||
result.push(...data.Buckets.map(bucket => ({
|
||||
result.push(
|
||||
...data.Buckets.map(bucket => ({
|
||||
Name: bucket.Name,
|
||||
CreationDate: bucket.CreationDate,
|
||||
Location: 'auto'
|
||||
})))
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
for (const bucket of data.Buckets) {
|
||||
const bucketName = bucket.Name
|
||||
const bucketConfig = await client.send(new GetBucketLocationCommand({
|
||||
const bucketConfig = await client.send(
|
||||
new GetBucketLocationCommand({
|
||||
Bucket: bucketName
|
||||
}))
|
||||
})
|
||||
)
|
||||
result.push({
|
||||
Name: bucketName,
|
||||
CreationDate: bucket.CreationDate,
|
||||
Location: bucketConfig.$metadata.httpStatusCode === 200
|
||||
Location:
|
||||
bucketConfig.$metadata.httpStatusCode === 200
|
||||
? bucketConfig.LocationConstraint?.toLowerCase() || 'us-east-1'
|
||||
: 'us-east-1'
|
||||
})
|
||||
@@ -303,7 +307,12 @@ class S3plistApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
let marker
|
||||
@@ -333,7 +342,8 @@ class S3plistApi {
|
||||
})
|
||||
res = await client.send(command)
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.Contents && res.Contents.forEach((item: _Object) => {
|
||||
res.Contents &&
|
||||
res.Contents.forEach((item: _Object) => {
|
||||
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
@@ -361,7 +371,12 @@ class S3plistApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
let marker
|
||||
@@ -393,10 +408,12 @@ class S3plistApi {
|
||||
})
|
||||
res = await client.send(command)
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.CommonPrefixes && res.CommonPrefixes.forEach((item: CommonPrefix) => {
|
||||
res.CommonPrefixes &&
|
||||
res.CommonPrefixes.forEach((item: CommonPrefix) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
res.Contents && res.Contents.forEach((item: _Object) => {
|
||||
res.Contents &&
|
||||
res.Contents.forEach((item: _Object) => {
|
||||
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
@@ -423,7 +440,13 @@ class S3plistApi {
|
||||
}
|
||||
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
const result = {
|
||||
@@ -434,7 +457,10 @@ class S3plistApi {
|
||||
}
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
Bucket: bucket,
|
||||
@@ -474,7 +500,10 @@ class S3plistApi {
|
||||
let result = false
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new CopyObjectCommand({
|
||||
Bucket: bucketName,
|
||||
@@ -562,7 +591,7 @@ class S3plistApi {
|
||||
Delimiter: '/',
|
||||
MaxKeys: 1000
|
||||
})
|
||||
res = await client.send(command) as ListObjectsV2CommandOutput
|
||||
res = (await client.send(command)) as ListObjectsV2CommandOutput
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.CommonPrefixes && allFileList.CommonPrefixes.push(...res.CommonPrefixes)
|
||||
res.Contents && allFileList.Contents.push(...res.Contents)
|
||||
@@ -595,7 +624,7 @@ class S3plistApi {
|
||||
const deleteCommand = new DeleteObjectsCommand({
|
||||
Bucket: bucketName,
|
||||
Delete: {
|
||||
Objects: deleteList.map((item) => {
|
||||
Objects: deleteList.map(item => {
|
||||
return {
|
||||
Key: item.Key
|
||||
}
|
||||
@@ -635,12 +664,16 @@ class S3plistApi {
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const signedUrl = await getSignedUrl(client, new GetObjectCommand({
|
||||
const signedUrl = await getSignedUrl(
|
||||
client,
|
||||
new GetObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: key
|
||||
}), {
|
||||
}),
|
||||
{
|
||||
expiresIn: expires || 3600
|
||||
})
|
||||
}
|
||||
)
|
||||
return signedUrl
|
||||
} catch (error) {
|
||||
this.logParam(error, 'getPreSignedUrl')
|
||||
@@ -693,7 +726,15 @@ class S3plistApi {
|
||||
fileArray.forEach((item: any) => {
|
||||
item.key.startsWith('/') && (item.key = item.key.slice(1))
|
||||
})
|
||||
const allowedAcl = ['private', 'public-read', 'public-read-write', 'aws-exec-read', 'authenticated-read', 'bucket-owner-read', 'bucket-owner-full-control']
|
||||
const allowedAcl = [
|
||||
'private',
|
||||
'public-read',
|
||||
'public-read-write',
|
||||
'aws-exec-read',
|
||||
'authenticated-read',
|
||||
'bucket-owner-read',
|
||||
'bucket-owner-full-control'
|
||||
]
|
||||
for (const item of fileArray) {
|
||||
const { bucketName, region, key, filePath, fileName, aclForUpload } = item
|
||||
const id = `${bucketName}-${String(region)}-${key}-${filePath}`
|
||||
@@ -743,11 +784,13 @@ class S3plistApi {
|
||||
parallelUploads3.on('httpUploadProgress', (progress: Progress) => {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: progress.loaded && progress.total ? Math.floor(progress.loaded / progress.total * 100) : 0,
|
||||
progress: progress.loaded && progress.total ? Math.floor((progress.loaded / progress.total) * 100) : 0,
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
})
|
||||
parallelUploads3.done().then((data) => {
|
||||
parallelUploads3
|
||||
.done()
|
||||
.then(data => {
|
||||
if (data.$metadata.httpStatusCode === 200) {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -763,7 +806,8 @@ class S3plistApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch(error => {
|
||||
this.logParam(error, 'uploadBucketFile')
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -806,19 +850,21 @@ class S3plistApi {
|
||||
expires: 36000,
|
||||
customUrl
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxy)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxy).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logParam(error, 'downloadBucketFile')
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -76,8 +76,7 @@ class SftpApi {
|
||||
}
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'SftpApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'SftpApi', method }))
|
||||
|
||||
transFormPermission = (permissionsStr: string) => {
|
||||
const permissions = permissionsStr.length === 10 ? permissionsStr.slice(1) : permissionsStr
|
||||
@@ -250,7 +249,8 @@ class SftpApi {
|
||||
if (formatedLSRes.length) {
|
||||
formatedLSRes.forEach((item: listDirResult) => {
|
||||
const relativePath = path.relative(baseDir, item.key.startsWith('/') ? item.key : `/${item.key}`)
|
||||
const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
const relative =
|
||||
webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
if (item.isDir) {
|
||||
result.fullList.push(this.formatFolder(item, webPath ? relative : urlPrefix, !!webPath))
|
||||
} else {
|
||||
|
||||
@@ -71,9 +71,7 @@ class SmmsApi {
|
||||
finished: false
|
||||
}
|
||||
do {
|
||||
res = await this.axiosInstance(
|
||||
'/upload_history',
|
||||
{
|
||||
res = await this.axiosInstance('/upload_history', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
@@ -131,9 +129,7 @@ class SmmsApi {
|
||||
nextMarker: '',
|
||||
success: false
|
||||
}
|
||||
const res = await this.axiosInstance(
|
||||
'/upload_history',
|
||||
{
|
||||
const res = await this.axiosInstance('/upload_history', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
@@ -141,8 +137,7 @@ class SmmsApi {
|
||||
params: {
|
||||
page: currentPage
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res?.status !== 200 || !res?.data?.success) return result
|
||||
|
||||
if (res.data.Count === 0) return { ...result, success: true }
|
||||
@@ -167,16 +162,13 @@ class SmmsApi {
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile({ DeleteHash }: IStringKeyMap): Promise<boolean> {
|
||||
const res = await this.axiosInstance(
|
||||
`/delete/${DeleteHash}`,
|
||||
{
|
||||
const res = await this.axiosInstance(`/delete/${DeleteHash}`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
hash: DeleteHash,
|
||||
format: 'json'
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
return res?.status === 200 && res?.data?.success
|
||||
}
|
||||
|
||||
@@ -239,19 +231,21 @@ class SmmsApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'SmmsApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import COS from 'cos-nodejs-sdk-v5'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
@@ -99,7 +98,13 @@ class TcyunApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1, prefix.length)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const cancelTask = [false]
|
||||
@@ -125,8 +130,11 @@ class TcyunApi {
|
||||
Marker: marker
|
||||
})
|
||||
if (res?.statusCode === 200) {
|
||||
result.fullList.push(...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix)))
|
||||
result.fullList.push(
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
)
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
} else {
|
||||
result.finished = true
|
||||
@@ -144,7 +152,13 @@ class TcyunApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1, prefix.length)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const cancelTask = [false]
|
||||
@@ -173,8 +187,9 @@ class TcyunApi {
|
||||
if (res?.statusCode === 200) {
|
||||
result.fullList.push(
|
||||
...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)),
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
)
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
} else {
|
||||
@@ -207,17 +222,24 @@ class TcyunApi {
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const res = await this.ctx.getBucket({
|
||||
const res = (await this.ctx.getBucket({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
Delimiter: '/',
|
||||
Marker: marker,
|
||||
MaxKeys: itemsPerPage
|
||||
}) as COS.GetBucketResult
|
||||
})) as COS.GetBucketResult
|
||||
if (res?.statusCode !== 200) {
|
||||
return {
|
||||
fullList: [],
|
||||
@@ -229,8 +251,9 @@ class TcyunApi {
|
||||
const result = {
|
||||
fullList: [
|
||||
...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)),
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
],
|
||||
isTruncated: res.IsTruncated === 'true',
|
||||
nextMarker: res.NextMarker || '',
|
||||
@@ -317,7 +340,14 @@ class TcyunApi {
|
||||
marker = res.NextMarker
|
||||
} while (res.IsTruncated === 'true')
|
||||
for (const item of allFileList.CommonPrefixes) {
|
||||
if (!(await this.deleteBucketFolder({ bucketName, region, key: item.Prefix }))) return false
|
||||
if (
|
||||
!(await this.deleteBucketFolder({
|
||||
bucketName,
|
||||
region,
|
||||
key: item.Prefix
|
||||
}))
|
||||
)
|
||||
return false
|
||||
}
|
||||
const cycles = Math.ceil(allFileList.Contents.length / 1000)
|
||||
for (let i = 0; i < cycles; i++) {
|
||||
@@ -344,14 +374,16 @@ class TcyunApi {
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName, region, key, expires, customUrl } = configMap
|
||||
const res = this.ctx.getObjectUrl({
|
||||
const res = this.ctx.getObjectUrl(
|
||||
{
|
||||
Bucket: bucketName,
|
||||
Region: region,
|
||||
Key: key,
|
||||
Expires: expires,
|
||||
Sign: true
|
||||
}, () => {
|
||||
})
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
return customUrl ? `${customUrl.replace(/\/+$/, '')}/${key}${res.slice(res.indexOf('?'))}` : res
|
||||
}
|
||||
|
||||
@@ -412,7 +444,12 @@ class TcyunApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
} else {
|
||||
this.logger.error(formatError(err, { method: 'uploadBucketFile', class: 'TcyunApi' }))
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
method: 'uploadBucketFile',
|
||||
class: 'TcyunApi'
|
||||
})
|
||||
)
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: 0,
|
||||
@@ -472,7 +509,8 @@ class TcyunApi {
|
||||
targetFilePath: path.join(downloadPath, fileName)
|
||||
})
|
||||
fs.ensureDirSync(path.dirname(path.join(downloadPath, fileName)))
|
||||
this.ctx.downloadFile({
|
||||
this.ctx
|
||||
.downloadFile({
|
||||
Bucket: bucketName,
|
||||
Region: region,
|
||||
Key: key,
|
||||
@@ -486,7 +524,8 @@ class TcyunApi {
|
||||
status: downloadTaskSpecialStatus.downloading
|
||||
})
|
||||
}
|
||||
}).then((res: any) => {
|
||||
})
|
||||
.then((res: any) => {
|
||||
instance.updateDownloadTask({
|
||||
id,
|
||||
progress: res && res.statusCode === 200 ? 100 : 0,
|
||||
@@ -494,8 +533,14 @@ class TcyunApi {
|
||||
response: typeof res === 'object' ? JSON.stringify(res) : String(res),
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}).catch((err: any) => {
|
||||
this.logger.error(formatError(err, { method: 'downloadBucketFile', class: 'TcyunApi' }))
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
method: 'downloadBucketFile',
|
||||
class: 'TcyunApi'
|
||||
})
|
||||
)
|
||||
instance.updateDownloadTask({
|
||||
id,
|
||||
progress: 0,
|
||||
|
||||
@@ -7,7 +7,15 @@ import Upyun from 'upyun'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { md5, hmacSha1Base64, getFileMimeType, NewDownloader, gotUpload, ConcurrencyPromisePool, formatError } from '~/manage/utils/common'
|
||||
import {
|
||||
md5,
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
gotUpload,
|
||||
ConcurrencyPromisePool,
|
||||
formatError
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
|
||||
@@ -26,7 +34,14 @@ class UpyunApi {
|
||||
stopMarker = 'g2gCZAAEbmV4dGQAA2VvZg'
|
||||
logger: ManageLogger
|
||||
|
||||
constructor (bucket: string, operator: string, password: string, logger: ManageLogger, antiLeechToken?: string, expireTime?: number) {
|
||||
constructor(
|
||||
bucket: string,
|
||||
operator: string,
|
||||
password: string,
|
||||
logger: ManageLogger,
|
||||
antiLeechToken?: string,
|
||||
expireTime?: number
|
||||
) {
|
||||
this.ser = new Upyun.Service(bucket, operator, password)
|
||||
this.cli = new Upyun.Client(this.ser)
|
||||
this.bucket = bucket
|
||||
@@ -87,13 +102,7 @@ class UpyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
authorization (
|
||||
method: string,
|
||||
uri: string,
|
||||
contentMd5: string,
|
||||
operator: string,
|
||||
password: string
|
||||
) {
|
||||
authorization(method: string, uri: string, contentMd5: string, operator: string, password: string) {
|
||||
return `UPYUN ${operator}:${hmacSha1Base64(
|
||||
md5(password, 'hex'),
|
||||
`${method.toUpperCase()}&${encodeURI(uri)}&${new Date().toUTCString()}${contentMd5 ? `&${contentMd5}` : ''}`
|
||||
@@ -313,11 +322,13 @@ class UpyunApi {
|
||||
})
|
||||
if (res) {
|
||||
res.files.forEach((item: any) => {
|
||||
item.type === 'N' && allFileList.Contents.push({
|
||||
item.type === 'N' &&
|
||||
allFileList.Contents.push({
|
||||
...item,
|
||||
key: `${key}${item.name}`
|
||||
})
|
||||
item.type === 'F' && allFileList.CommonPrefixes.push({
|
||||
item.type === 'F' &&
|
||||
allFileList.CommonPrefixes.push({
|
||||
...item,
|
||||
key: `${key}${item.name}/`
|
||||
})
|
||||
@@ -445,19 +456,21 @@ class UpyunApi {
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
const preSignedUrl = `${customUrl}/${key}`
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'UpyunApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -29,7 +29,15 @@ class WebdavplistApi {
|
||||
agent: https.Agent | http.Agent
|
||||
ctx: WebDAVClient
|
||||
|
||||
constructor (endpoint: string, username: string, password: string, sslEnabled: boolean, proxy: string | undefined, authType: 'basic' | 'digest' | undefined, logger: ManageLogger) {
|
||||
constructor(
|
||||
endpoint: string,
|
||||
username: string,
|
||||
password: string,
|
||||
sslEnabled: boolean,
|
||||
proxy: string | undefined,
|
||||
authType: 'basic' | 'digest' | undefined,
|
||||
logger: ManageLogger
|
||||
) {
|
||||
this.endpoint = formatEndpoint(endpoint, sslEnabled)
|
||||
this.username = username
|
||||
this.password = password
|
||||
@@ -50,14 +58,10 @@ class WebdavplistApi {
|
||||
if (this.authType === 'digest') {
|
||||
options.authType = AuthType.Digest
|
||||
}
|
||||
this.ctx = createClient(
|
||||
this.endpoint,
|
||||
options
|
||||
)
|
||||
this.ctx = createClient(this.endpoint, options)
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
|
||||
|
||||
formatFolder(item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.filename.replace(/^\/+/, '')
|
||||
@@ -166,7 +170,8 @@ class WebdavplistApi {
|
||||
if (res.data?.length) {
|
||||
res.data.forEach((item: FileStat) => {
|
||||
const relativePath = path.relative(baseDir, item.filename)
|
||||
const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
const relative =
|
||||
webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
if (item.type === 'directory') {
|
||||
result.fullList.push(this.formatFolder(item, webPath ? relative : urlPrefix, !!webPath))
|
||||
} else {
|
||||
@@ -261,10 +266,8 @@ class WebdavplistApi {
|
||||
targetFileRegion: region,
|
||||
noProgress: true
|
||||
})
|
||||
this.ctx.putFileContents(
|
||||
key,
|
||||
this.authType === 'digest' ? fs.readFileSync(filePath) : fs.createReadStream(filePath),
|
||||
{
|
||||
this.ctx
|
||||
.putFileContents(key, this.authType === 'digest' ? fs.readFileSync(filePath) : fs.createReadStream(filePath), {
|
||||
overwrite: true,
|
||||
onUploadProgress: (progressEvent: ProgressEvent) => {
|
||||
instance.updateUploadTask({
|
||||
@@ -273,8 +276,8 @@ class WebdavplistApi {
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
}
|
||||
}
|
||||
).then((res: boolean) => {
|
||||
})
|
||||
.then((res: boolean) => {
|
||||
if (res) {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -290,7 +293,8 @@ class WebdavplistApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.logParam(error, 'uploadBucketFile')
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -345,25 +349,35 @@ class WebdavplistApi {
|
||||
Authorization: `Basic ${base64Str}`
|
||||
}
|
||||
} else if (this.authType === 'digest') {
|
||||
const authHeader = await getAuthHeader('GET', this.endpoint, `/${key.replace(/^\/+/, '')}`, this.username, this.password)
|
||||
const authHeader = await getAuthHeader(
|
||||
'GET',
|
||||
this.endpoint,
|
||||
`/${key.replace(/^\/+/, '')}`,
|
||||
this.username,
|
||||
this.password
|
||||
)
|
||||
headers = {
|
||||
Authorization: authHeader
|
||||
}
|
||||
preSignedUrl = `${this.endpoint}/${key.replace(/^\/+/, '')}`
|
||||
}
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxyStr, headers)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxyStr, headers).then(
|
||||
(res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}))
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logParam(error, 'downloadBucketFile')
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -42,9 +42,13 @@ function manageDbChecker () {
|
||||
fs.unlinkSync(manageConfigFilePath)
|
||||
if (fs.existsSync(manageConfigFileBackupPath)) {
|
||||
try {
|
||||
configFile = fs.readFileSync(manageConfigFileBackupPath, { encoding: 'utf-8' })
|
||||
configFile = fs.readFileSync(manageConfigFileBackupPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
JSON.parse(configFile)
|
||||
writeFile.sync(manageConfigFilePath, configFile, { encoding: 'utf-8' })
|
||||
writeFile.sync(manageConfigFilePath, configFile, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const stats = fs.statSync(manageConfigFileBackupPath)
|
||||
optionsTpl.body = `${errorMsg.brokenButBackup}\n${T('TIPS_PICGO_BACKUP_FILE_VERSION', {
|
||||
v: dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')
|
||||
@@ -61,7 +65,9 @@ function manageDbChecker () {
|
||||
global.notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
writeFile.sync(manageConfigFileBackupPath, configFile, { encoding: 'utf-8' })
|
||||
writeFile.sync(manageConfigFileBackupPath, configFile, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +86,9 @@ function managePathChecker (): string {
|
||||
return _configFilePath
|
||||
}
|
||||
try {
|
||||
const configString = fs.readFileSync(defaultManageConfigPath, { encoding: 'utf-8' })
|
||||
const configString = fs.readFileSync(defaultManageConfigPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const config = JSON.parse(configString)
|
||||
const userConfigPath: string = config.configPath || ''
|
||||
if (userConfigPath) {
|
||||
@@ -111,8 +119,4 @@ function managePathDir () {
|
||||
return path.dirname(managePathChecker())
|
||||
}
|
||||
|
||||
export {
|
||||
managePathChecker,
|
||||
managePathDir,
|
||||
manageDbChecker
|
||||
}
|
||||
export { managePathChecker, managePathDir, manageDbChecker }
|
||||
|
||||
@@ -104,11 +104,21 @@ class UpDownTaskQueue {
|
||||
}
|
||||
|
||||
removeUploadedTask() {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = UpDownTaskQueue.getInstance().uploadTaskQueue.filter(item => item.status !== uploadTaskSpecialStatus.uploaded && item.status !== commonTaskStatus.canceled && item.status !== commonTaskStatus.failed)
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = UpDownTaskQueue.getInstance().uploadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== uploadTaskSpecialStatus.uploaded &&
|
||||
item.status !== commonTaskStatus.canceled &&
|
||||
item.status !== commonTaskStatus.failed
|
||||
)
|
||||
}
|
||||
|
||||
removeDownloadedTask() {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = UpDownTaskQueue.getInstance().downloadTaskQueue.filter(item => item.status !== downloadTaskSpecialStatus.downloaded && item.status !== commonTaskStatus.canceled && item.status !== commonTaskStatus.failed)
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = UpDownTaskQueue.getInstance().downloadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== downloadTaskSpecialStatus.downloaded &&
|
||||
item.status !== commonTaskStatus.canceled &&
|
||||
item.status !== commonTaskStatus.failed
|
||||
)
|
||||
}
|
||||
|
||||
clearDownloadTaskQueue() {
|
||||
@@ -123,10 +133,13 @@ class UpDownTaskQueue {
|
||||
persist() {
|
||||
try {
|
||||
this.checkPersistPath()
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
@@ -146,18 +159,24 @@ class UpDownTaskQueue {
|
||||
|
||||
private checkPersistPath() {
|
||||
if (!fs.existsSync(this.persistPath)) {
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
try {
|
||||
JSON.parse(fs.readFileSync(this.persistPath, { encoding: 'utf-8' }))
|
||||
} catch (e) {
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +59,13 @@ export const manageIpcList = {
|
||||
return manage.getBucketListBackstage(param)
|
||||
})
|
||||
|
||||
ipcMain.on('getBucketListRecursively', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
ipcMain.on(
|
||||
'getBucketListRecursively',
|
||||
async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getBucketListRecursively(param)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
ipcMain.handle('convertPathToBase64', async (_: IpcMainInvokeEvent, filePath: string) => {
|
||||
const res = fs.readFileSync(filePath, 'base64')
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
IpcMainEvent,
|
||||
IpcMainInvokeEvent,
|
||||
ipcMain
|
||||
} from 'electron'
|
||||
import { IpcMainEvent, IpcMainInvokeEvent, ipcMain } from 'electron'
|
||||
|
||||
import getManageApi from '~/manage/Main'
|
||||
import { PICLIST_MANAGE_GET_CONFIG, PICLIST_MANAGE_SAVE_CONFIG, PICLIST_MANAGE_REMOVE_CONFIG } from '~/manage/events/constants'
|
||||
import {
|
||||
PICLIST_MANAGE_GET_CONFIG,
|
||||
PICLIST_MANAGE_SAVE_CONFIG,
|
||||
PICLIST_MANAGE_REMOVE_CONFIG
|
||||
} from '~/manage/events/constants'
|
||||
|
||||
const manageApi = getManageApi()
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { ipcMain } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import fs from 'fs-extra'
|
||||
@@ -15,12 +14,7 @@ import { isInputConfigValid, formatError } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import {
|
||||
IManageApiType,
|
||||
IManageConfigType,
|
||||
IManageError,
|
||||
IPicBedMangeConfig
|
||||
} from '#/types/manage'
|
||||
import { IManageApiType, IManageConfigType, IManageError, IPicBedMangeConfig } from '#/types/manage'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
|
||||
export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
@@ -58,11 +52,25 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
const name = this.currentPicBedConfig.picBedName
|
||||
switch (name) {
|
||||
case 'aliyun':
|
||||
return new API.AliyunApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.accessKeySecret, this.logger)
|
||||
return new API.AliyunApi(
|
||||
this.currentPicBedConfig.accessKeyId,
|
||||
this.currentPicBedConfig.accessKeySecret,
|
||||
this.logger
|
||||
)
|
||||
case 'github':
|
||||
return new API.GithubApi(this.currentPicBedConfig.token, this.currentPicBedConfig.githubUsername, this.currentPicBedConfig.proxy, this.logger)
|
||||
return new API.GithubApi(
|
||||
this.currentPicBedConfig.token,
|
||||
this.currentPicBedConfig.githubUsername,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger
|
||||
)
|
||||
case 'imgur':
|
||||
return new API.ImgurApi(this.currentPicBedConfig.imgurUserName, this.currentPicBedConfig.accessToken, this.currentPicBedConfig.proxy, this.logger)
|
||||
return new API.ImgurApi(
|
||||
this.currentPicBedConfig.imgurUserName,
|
||||
this.currentPicBedConfig.accessToken,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger
|
||||
)
|
||||
case 'local':
|
||||
return new API.LocalApi(this.logger)
|
||||
case 'qiniu':
|
||||
@@ -70,15 +78,50 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
case 'smms':
|
||||
return new API.SmmsApi(this.currentPicBedConfig.token, this.logger)
|
||||
case 's3plist':
|
||||
return new API.S3plistApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.secretAccessKey, this.currentPicBedConfig.endpoint, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.s3ForcePathStyle, this.currentPicBedConfig.proxy, this.logger, this.currentPicBedConfig.dogeCloudSupport || false, this.currentPicBedConfig.bucketName || '')
|
||||
return new API.S3plistApi(
|
||||
this.currentPicBedConfig.accessKeyId,
|
||||
this.currentPicBedConfig.secretAccessKey,
|
||||
this.currentPicBedConfig.endpoint,
|
||||
this.currentPicBedConfig.sslEnabled,
|
||||
this.currentPicBedConfig.s3ForcePathStyle,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger,
|
||||
this.currentPicBedConfig.dogeCloudSupport || false,
|
||||
this.currentPicBedConfig.bucketName || ''
|
||||
)
|
||||
case 'sftp':
|
||||
return new API.SftpApi(this.currentPicBedConfig.host, this.currentPicBedConfig.port, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.privateKey, this.currentPicBedConfig.passphrase, this.currentPicBedConfig.fileMode, this.currentPicBedConfig.dirMode, this.logger)
|
||||
return new API.SftpApi(
|
||||
this.currentPicBedConfig.host,
|
||||
this.currentPicBedConfig.port,
|
||||
this.currentPicBedConfig.username,
|
||||
this.currentPicBedConfig.password,
|
||||
this.currentPicBedConfig.privateKey,
|
||||
this.currentPicBedConfig.passphrase,
|
||||
this.currentPicBedConfig.fileMode,
|
||||
this.currentPicBedConfig.dirMode,
|
||||
this.logger
|
||||
)
|
||||
case 'tcyun':
|
||||
return new API.TcyunApi(this.currentPicBedConfig.secretId, this.currentPicBedConfig.secretKey, this.logger)
|
||||
case 'upyun':
|
||||
return new API.UpyunApi(this.currentPicBedConfig.bucketName, this.currentPicBedConfig.operator, this.currentPicBedConfig.password, this.logger, this.currentPicBedConfig.antiLeechToken, this.currentPicBedConfig.expireTime)
|
||||
return new API.UpyunApi(
|
||||
this.currentPicBedConfig.bucketName,
|
||||
this.currentPicBedConfig.operator,
|
||||
this.currentPicBedConfig.password,
|
||||
this.logger,
|
||||
this.currentPicBedConfig.antiLeechToken,
|
||||
this.currentPicBedConfig.expireTime
|
||||
)
|
||||
case 'webdavplist':
|
||||
return new API.WebdavplistApi(this.currentPicBedConfig.endpoint, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.proxy, this.currentPicBedConfig.authType, this.logger)
|
||||
return new API.WebdavplistApi(
|
||||
this.currentPicBedConfig.endpoint,
|
||||
this.currentPicBedConfig.username,
|
||||
this.currentPicBedConfig.password,
|
||||
this.currentPicBedConfig.sslEnabled,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.currentPicBedConfig.authType,
|
||||
this.logger
|
||||
)
|
||||
default:
|
||||
return {} as any
|
||||
}
|
||||
@@ -117,9 +160,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
|
||||
saveConfig(config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn(
|
||||
'the format of config is invalid, please provide object'
|
||||
)
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
}
|
||||
this.setConfig(config)
|
||||
@@ -136,9 +177,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
|
||||
setConfig(config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn(
|
||||
'the format of config is invalid, please provide object'
|
||||
)
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
}
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
@@ -151,9 +190,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
unset(this.getConfig(key), propName)
|
||||
}
|
||||
|
||||
async getBucketList (
|
||||
param?: IStringKeyMap | undefined
|
||||
): Promise<any> {
|
||||
async getBucketList(param?: IStringKeyMap | undefined): Promise<any> {
|
||||
let client
|
||||
const name = this.currentPicBedConfig.picBedName.replace('plist', '')
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
@@ -171,36 +208,36 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
return []
|
||||
}
|
||||
case 'upyun':
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
Name: this.currentPicBedConfig.bucketName,
|
||||
Location: 'upyun',
|
||||
CreationDate: new Date().toISOString()
|
||||
}]
|
||||
}
|
||||
]
|
||||
case 'smms':
|
||||
case 'webdavplist':
|
||||
case 'local':
|
||||
case 'sftp':
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
Name: name,
|
||||
Location: name,
|
||||
CreationDate: new Date().toISOString()
|
||||
}]
|
||||
}
|
||||
]
|
||||
default:
|
||||
console.log(param)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketInfo (
|
||||
param?: IStringKeyMap | undefined
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketInfo(param?: IStringKeyMap | undefined): Promise<IStringKeyMap | IManageError> {
|
||||
console.log(param)
|
||||
return {}
|
||||
}
|
||||
|
||||
async getBucketDomain (
|
||||
param: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -225,9 +262,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async createBucket (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async createBucket(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -246,44 +281,32 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucket (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucket(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async getOperatorList (
|
||||
param?: IStringKeyMap
|
||||
): Promise<string[] | IManageError> {
|
||||
async getOperatorList(param?: IStringKeyMap): Promise<string[] | IManageError> {
|
||||
console.log(param)
|
||||
return []
|
||||
}
|
||||
|
||||
async addOperator (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async addOperator(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async deleteOperator (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteOperator(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async getBucketAclPolicy (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketAclPolicy(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
console.log(param)
|
||||
return {}
|
||||
}
|
||||
|
||||
async setBucketAclPolicy (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async setBucketAclPolicy(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'qiniu':
|
||||
@@ -299,9 +322,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketListRecursively (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketListRecursively(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
let window
|
||||
const defaultResult = {
|
||||
@@ -344,9 +365,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
* @param param
|
||||
* @returns
|
||||
*/
|
||||
async getBucketListBackstage (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketListBackstage(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
let window
|
||||
const defaultResult = {
|
||||
@@ -393,9 +412,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
* isDir: 是否是文件夹
|
||||
* fileSize: 文件大小
|
||||
**/
|
||||
async getBucketFileList (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketFileList(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
const defaultResponse = {
|
||||
fullList: <any>[],
|
||||
isTruncated: false,
|
||||
@@ -422,9 +439,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -451,9 +466,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucketFolder (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucketFolder(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -477,9 +490,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async renameBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async renameBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -502,9 +513,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async downloadBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async downloadBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -531,16 +540,12 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async copyMoveBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async copyMoveBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async createBucketFolder (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async createBucketFolder(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -564,9 +569,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async uploadBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -592,9 +595,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async getPreSignedUrl (
|
||||
param?: IStringKeyMap
|
||||
): Promise<string> {
|
||||
async getPreSignedUrl(param?: IStringKeyMap): Promise<string> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
|
||||
@@ -18,17 +18,12 @@ import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { formatHttpProxy } from '#/utils/common'
|
||||
|
||||
export const getFSFile = async (
|
||||
filePath: string,
|
||||
stream: boolean = false
|
||||
): Promise<IStringKeyMap> => {
|
||||
export const getFSFile = async (filePath: string, stream: boolean = false): Promise<IStringKeyMap> => {
|
||||
try {
|
||||
return {
|
||||
extension: path.extname(filePath),
|
||||
fileName: path.basename(filePath),
|
||||
buffer: stream
|
||||
? fs.createReadStream(filePath)
|
||||
: await fs.readFile(filePath),
|
||||
buffer: stream ? fs.createReadStream(filePath) : await fs.readFile(filePath),
|
||||
success: true
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -39,9 +34,7 @@ export const getFSFile = async (
|
||||
}
|
||||
|
||||
export function isInputConfigValid(config: any): boolean {
|
||||
return typeof config === 'object' &&
|
||||
!Array.isArray(config) &&
|
||||
Object.keys(config).length > 0
|
||||
return typeof config === 'object' && !Array.isArray(config) && Object.keys(config).length > 0
|
||||
}
|
||||
|
||||
export const getFileMimeType = (filePath: string): string => mime.lookup(filePath) || 'application/octet-stream'
|
||||
@@ -83,7 +76,8 @@ export const clearTempFolder = () => fs.emptyDirSync(getTempDirPath())
|
||||
|
||||
export const md5 = (str: string, code: 'hex' | 'base64'): string => crypto.createHash('md5').update(str).digest(code)
|
||||
|
||||
export const hmacSha1Base64 = (secretKey: string, stringToSign: string) : string => crypto.createHmac('sha1', secretKey).update(Buffer.from(stringToSign, 'utf8')).digest('base64')
|
||||
export const hmacSha1Base64 = (secretKey: string, stringToSign: string): string =>
|
||||
crypto.createHmac('sha1', secretKey).update(Buffer.from(stringToSign, 'utf8')).digest('base64')
|
||||
|
||||
export const NewDownloader = async (
|
||||
instance: UpDownTaskQueue,
|
||||
@@ -150,9 +144,7 @@ export const gotUpload = async (
|
||||
throwHttpErrors: boolean = false,
|
||||
agent: any = {}
|
||||
) => {
|
||||
got(
|
||||
url,
|
||||
{
|
||||
got(url, {
|
||||
headers,
|
||||
method,
|
||||
body,
|
||||
@@ -161,8 +153,7 @@ export const gotUpload = async (
|
||||
},
|
||||
throwHttpErrors,
|
||||
agent
|
||||
}
|
||||
)
|
||||
})
|
||||
.on('uploadProgress', (progress: any) => {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -174,7 +165,10 @@ export const gotUpload = async (
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: res?.statusCode === 200 || res?.statusCode === 201 ? 100 : 0,
|
||||
status: res?.statusCode === 200 || res?.statusCode === 201 ? uploadTaskSpecialStatus.uploaded : commonTaskStatus.failed,
|
||||
status:
|
||||
res?.statusCode === 200 || res?.statusCode === 201
|
||||
? uploadTaskSpecialStatus.uploaded
|
||||
: commonTaskStatus.failed,
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
})
|
||||
@@ -220,7 +214,10 @@ const commonOptions = {
|
||||
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
|
||||
} as any
|
||||
|
||||
export const getAgent = (proxy:any, https: boolean = true): {
|
||||
export const getAgent = (
|
||||
proxy: any,
|
||||
https: boolean = true
|
||||
): {
|
||||
https?: HttpsProxyAgent
|
||||
http?: HttpProxyAgent
|
||||
} => {
|
||||
@@ -298,7 +295,9 @@ export function getOptions (
|
||||
...(body && { body }),
|
||||
...(responseType && { responseType }),
|
||||
...(timeout !== undefined ? { timeout: { request: timeout } } : { timeout: { request: 30000 } }),
|
||||
...(proxy && { agent: Object.fromEntries(Object.entries(getAgent(proxy)).filter(([, v]) => v !== undefined)) }),
|
||||
...(proxy && {
|
||||
agent: Object.fromEntries(Object.entries(getAgent(proxy)).filter(([, v]) => v !== undefined))
|
||||
}),
|
||||
throwHttpErrors: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ export async function dogecloudApi (
|
||||
secretKey: string
|
||||
) {
|
||||
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||
const sign = crypto.createHmac('sha1', secretKey).update(Buffer.from(apiPath + '\n' + body, 'utf8')).digest('hex')
|
||||
const sign = crypto
|
||||
.createHmac('sha1', secretKey)
|
||||
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
||||
.digest('hex')
|
||||
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||
try {
|
||||
const res = await axios.request({
|
||||
@@ -40,16 +43,22 @@ export async function dogecloudApi (
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTempToken (accessKey: string, secretKey: string): Promise<{} | DogecloudToken> {
|
||||
const dogeTempToken = await picgo.getConfig('Credentials.doge-token') || {} as any
|
||||
export async function getTempToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudToken> {
|
||||
const dogeTempToken = (await picgo.getConfig('Credentials.doge-token')) || ({} as any)
|
||||
if (dogeTempToken.token && dogeTempToken.expires > Date.now() + 7200000) {
|
||||
return dogeTempToken.token
|
||||
}
|
||||
try {
|
||||
const data = await dogecloudApi('/auth/tmp_token.json', {
|
||||
const data = await dogecloudApi(
|
||||
'/auth/tmp_token.json',
|
||||
{
|
||||
channel: 'OSS_FULL',
|
||||
scopes: ['*']
|
||||
}, true, accessKey, secretKey)
|
||||
},
|
||||
true,
|
||||
accessKey,
|
||||
secretKey
|
||||
)
|
||||
const token = data.Credentials
|
||||
picgo.saveConfig({
|
||||
Credentials: {
|
||||
|
||||
@@ -27,9 +27,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
|
||||
#handleLog(type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](
|
||||
`[PicList ${type.toUpperCase()}]`
|
||||
)
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](`[PicList ${type.toUpperCase()}]`)
|
||||
console.log(logHeader, ...msg)
|
||||
this.#logLevel = this.#ctx.getConfig(configPaths.settings.logLevel)
|
||||
this.#logPath =
|
||||
@@ -61,11 +59,7 @@ export class ManageLogger implements ILogger {
|
||||
if (fs.existsSync(logPath)) {
|
||||
const logFileSize = fs.statSync(logPath).size
|
||||
const logFileSizeLimit =
|
||||
enforceNumber(
|
||||
this.#ctx.getConfig<Undefinable<number>>(
|
||||
configPaths.settings.logFileSizeLimit
|
||||
) || 10
|
||||
) *
|
||||
enforceNumber(this.#ctx.getConfig<Undefinable<number>>(configPaths.settings.logFileSizeLimit) || 10) *
|
||||
1024 *
|
||||
1024
|
||||
return {
|
||||
@@ -87,11 +81,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
#handleWriteLog (
|
||||
logPath: string,
|
||||
type: string,
|
||||
...msg: ILogArgvTypeWithError[]
|
||||
): void {
|
||||
#handleWriteLog(logPath: string, type: string, ...msg: ILogArgvTypeWithError[]): void {
|
||||
try {
|
||||
if (this.#checkLogLevel(type, this.#logLevel)) {
|
||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicList ${type.toUpperCase()}] `
|
||||
@@ -122,10 +112,7 @@ export class ManageLogger implements ILogger {
|
||||
return result
|
||||
}
|
||||
|
||||
#checkLogLevel (
|
||||
type: string,
|
||||
level: undefined | string | string[]
|
||||
): boolean {
|
||||
#checkLogLevel(type: string, level: undefined | string | string[]): boolean {
|
||||
if (level === undefined || level === 'all') {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import routers from '~/server/routerManager'
|
||||
import {
|
||||
handleResponse,
|
||||
ensureHTTPLink
|
||||
} from '~/server/utils'
|
||||
import { handleResponse, ensureHTTPLink } from '~/server/utils'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
@@ -31,9 +28,7 @@ const multerStorage = multer.diskStorage({
|
||||
filename: function (_req: any, file: { originalname: any }, cb: (arg0: null, arg1: any) => void) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
if (!/[^\u0000-\u00ff]/.test(file.originalname)) {
|
||||
file.originalname = Buffer.from(file.originalname, 'latin1').toString(
|
||||
'utf8'
|
||||
)
|
||||
file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
|
||||
}
|
||||
cb(null, file.originalname)
|
||||
}
|
||||
@@ -63,7 +58,7 @@ class Server {
|
||||
}
|
||||
|
||||
#isValidConfig(config: IObj | undefined) {
|
||||
return config && config.port && config.host && (config.enable !== undefined)
|
||||
return config && config.port && config.host && config.enable !== undefined
|
||||
}
|
||||
|
||||
#handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
@@ -108,7 +103,7 @@ class Server {
|
||||
}
|
||||
}
|
||||
if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error since the multer type is not correct
|
||||
uploadMulter.any()(request, response, (err: any) => {
|
||||
if (err) {
|
||||
logger.info('[PicList Server]', err)
|
||||
@@ -120,7 +115,7 @@ class Server {
|
||||
}
|
||||
})
|
||||
}
|
||||
// @ts-ignore
|
||||
// @ts-expect-error since the multer type is not correct
|
||||
const list = request.files.map(file => file.path)
|
||||
logger.info('[PicList Server] get a formData request')
|
||||
const handler = routers.getHandler(url!, 'POST')?.handler
|
||||
@@ -140,7 +135,7 @@ class Server {
|
||||
})
|
||||
request.on('end', () => {
|
||||
try {
|
||||
postObj = (body === '') ? {} : JSON.parse(body)
|
||||
postObj = body === '' ? {} : JSON.parse(body)
|
||||
} catch (err: any) {
|
||||
logger.error('[PicList Server]', err)
|
||||
return handleResponse({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
type HttpMethod = 'GET' | 'POST'
|
||||
|
||||
class Router {
|
||||
#router = new Map<string, Map<HttpMethod, {handler: routeHandler, urlparams?: URLSearchParams}>>()
|
||||
#router = new Map<string, Map<HttpMethod, { handler: routeHandler; urlparams?: URLSearchParams }>>()
|
||||
|
||||
#addRoute(method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
if (!this.#router.has(url)) {
|
||||
|
||||
@@ -16,10 +16,7 @@ import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { markdownContent } from '~/server/apiDoc'
|
||||
import router from '~/server/router'
|
||||
import {
|
||||
deleteChoosedFiles,
|
||||
handleResponse
|
||||
} from '~/server/utils'
|
||||
import { deleteChoosedFiles, handleResponse } from '~/server/utils'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
@@ -42,13 +39,15 @@ async function responseForGet ({ response } : {response: http.ServerResponse}) {
|
||||
router.get('/', responseForGet)
|
||||
router.get('/upload', responseForGet)
|
||||
|
||||
router.post('/upload', async ({
|
||||
router.post(
|
||||
'/upload',
|
||||
async ({
|
||||
response,
|
||||
list = [],
|
||||
urlparams
|
||||
}: {
|
||||
response: IHttpResponse,
|
||||
list?: string[],
|
||||
response: IHttpResponse
|
||||
list?: string[]
|
||||
urlparams?: URLSearchParams
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
@@ -70,9 +69,9 @@ router.post('/upload', async ({
|
||||
let currentPicBedConfigId = ''
|
||||
let needRestore = false
|
||||
if (picbed) {
|
||||
const currentPicBed = picgo.getConfig<IStringKeyMap>('picBed') || {} as IStringKeyMap
|
||||
const currentPicBed = picgo.getConfig<IStringKeyMap>('picBed') || ({} as IStringKeyMap)
|
||||
currentPicBedType = currentPicBed.current || ''
|
||||
currentPicBedConfig = currentPicBed[currentPicBedType] || {} as IStringKeyMap
|
||||
currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap)
|
||||
currentPicBedConfigId = currentPicBedConfig._id
|
||||
const configName = urlparams?.get('configName') || currentPicBed[picbed]?._configName
|
||||
if (picbed === currentPicBedType && configName === currentPicBedConfig._configName) {
|
||||
@@ -176,15 +175,12 @@ router.post('/upload', async ({
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
router.post('/delete', async ({
|
||||
response,
|
||||
list = []
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
list?: IStringKeyMap[]
|
||||
}): Promise<void> => {
|
||||
router.post(
|
||||
'/delete',
|
||||
async ({ response, list = [] }: { response: IHttpResponse; list?: IStringKeyMap[] }): Promise<void> => {
|
||||
if (list.length === 0) {
|
||||
handleResponse({
|
||||
response,
|
||||
@@ -221,13 +217,10 @@ router.post('/delete', async ({
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
router.any('/heartbeat', async ({
|
||||
response
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
}) => {
|
||||
router.any('/heartbeat', async ({ response }: { response: IHttpResponse }) => {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
@@ -29,9 +27,9 @@ export const handleResponse = ({
|
||||
success: false
|
||||
}
|
||||
}: {
|
||||
response: IHttpResponse,
|
||||
statusCode?: number,
|
||||
header?: IObj,
|
||||
response: IHttpResponse
|
||||
statusCode?: number
|
||||
header?: IObj
|
||||
body?: any
|
||||
}) => {
|
||||
if (body?.success === false) {
|
||||
@@ -43,9 +41,7 @@ export const handleResponse = ({
|
||||
}
|
||||
|
||||
export const ensureHTTPLink = (url: string): string => {
|
||||
return url.startsWith('http')
|
||||
? url
|
||||
: `http://${url}`
|
||||
return url.startsWith('http') ? url : `http://${url}`
|
||||
}
|
||||
|
||||
export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => {
|
||||
@@ -61,10 +57,7 @@ export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> =>
|
||||
const noteFunc = (value: boolean) => {
|
||||
const notification = new Notification({
|
||||
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
|
||||
body: T(value
|
||||
? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED'
|
||||
: 'GALLERY_SYNC_DELETE_NOTICE_FAILED'
|
||||
)
|
||||
body: T(value ? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED' : 'GALLERY_SYNC_DELETE_NOTICE_FAILED')
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
@@ -83,10 +83,14 @@ class WebServer {
|
||||
this.#server
|
||||
.listen(
|
||||
this.#config.webServerPort === 36699 ? 37777 : this.#config.webServerPort,
|
||||
this.#config.webServerHost, () => {
|
||||
logger.info(`Web server is running at http://${this.#config.webServerHost}:${this.#config.webServerPort}, root path is ${this.#config.webServerPath}`)
|
||||
})
|
||||
.on('error', (err) => {
|
||||
this.#config.webServerHost,
|
||||
() => {
|
||||
logger.info(
|
||||
`Web server is running at http://${this.#config.webServerHost}:${this.#config.webServerPort}, root path is ${this.#config.webServerPath}`
|
||||
)
|
||||
}
|
||||
)
|
||||
.on('error', err => {
|
||||
logger.error(err)
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -18,10 +18,7 @@ function beforeOpen () {
|
||||
resolveOtherI18nFiles()
|
||||
}
|
||||
|
||||
function copyFileOutsideOfElectronAsar (
|
||||
sourceInAsarArchive: string,
|
||||
destOutsideAsarArchive: string
|
||||
) {
|
||||
function copyFileOutsideOfElectronAsar(sourceInAsarArchive: string, destOutsideAsarArchive: string) {
|
||||
if (fs.existsSync(sourceInAsarArchive)) {
|
||||
// file will be copied
|
||||
if (fs.statSync(sourceInAsarArchive).isFile()) {
|
||||
@@ -85,13 +82,7 @@ function resolveClipboardImageGenerator () {
|
||||
}
|
||||
|
||||
function getClipboardFiles() {
|
||||
const files = [
|
||||
'/linux.sh',
|
||||
'/mac.applescript',
|
||||
'/windows.ps1',
|
||||
'/windows10.ps1',
|
||||
'/wsl.sh'
|
||||
]
|
||||
const files = ['/linux.sh', '/mac.applescript', '/windows.ps1', '/windows10.ps1', '/wsl.sh']
|
||||
|
||||
return files.map(item => {
|
||||
return {
|
||||
|
||||
+21
-10
@@ -12,7 +12,9 @@ import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
export let tray: Tray
|
||||
|
||||
export const setTray = (t: Tray) => { tray = t }
|
||||
export const setTray = (t: Tray) => {
|
||||
tray = t
|
||||
}
|
||||
|
||||
export const getTray = () => tray
|
||||
|
||||
@@ -32,13 +34,15 @@ export const handleCopyUrl = (str: string): void => {
|
||||
* show notification
|
||||
* @param options
|
||||
*/
|
||||
export const showNotification = (options: IPrivateShowNotificationOption = {
|
||||
export const showNotification = (
|
||||
options: IPrivateShowNotificationOption = {
|
||||
title: '',
|
||||
body: '',
|
||||
clickToCopy: false,
|
||||
copyContent: '',
|
||||
clickFn: () => {}
|
||||
}) => {
|
||||
}
|
||||
) => {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body
|
||||
@@ -60,10 +64,8 @@ export const showNotification = (options: IPrivateShowNotificationOption = {
|
||||
}
|
||||
|
||||
export const showMessageBox = (options: any) => {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve) => {
|
||||
dialog.showMessageBox(
|
||||
options
|
||||
).then((res) => {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
dialog.showMessageBox(options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
@@ -118,14 +120,18 @@ export const getClipboardFilePath = (): string => {
|
||||
}
|
||||
|
||||
if (img.isEmpty() && platform === 'win32') {
|
||||
const imgPath = clipboard.readBuffer('FileNameW')?.toString('ucs2')?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
||||
const imgPath = clipboard
|
||||
.readBuffer('FileNameW')
|
||||
?.toString('ucs2')
|
||||
?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
||||
return imgPath || ''
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export const handleUrlEncodeWithSetting = (url: string) => db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url
|
||||
export const handleUrlEncodeWithSetting = (url: string) =>
|
||||
db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url
|
||||
|
||||
const c1nApi = 'https://c1n.cn/link/short'
|
||||
|
||||
@@ -163,7 +169,12 @@ const generateYOURLSShortUrl = async (url: string) => {
|
||||
if (!/^https?:\/\//.test(domain)) {
|
||||
domain = `http://${domain}`
|
||||
}
|
||||
const params = new URLSearchParams({ signature, action: 'shorturl', format: 'json', url })
|
||||
const params = new URLSearchParams({
|
||||
signature,
|
||||
action: 'shorturl',
|
||||
format: 'json',
|
||||
url
|
||||
})
|
||||
try {
|
||||
const res = await axios.get(`${domain}/yourls-api.php?${params.toString()}`)
|
||||
if (res.data?.shorturl) {
|
||||
|
||||
@@ -40,7 +40,10 @@ async function dogecloudApi (
|
||||
secretKey: string
|
||||
) {
|
||||
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||
const sign = crypto.createHmac('sha1', secretKey).update(Buffer.from(apiPath + '\n' + body, 'utf8')).digest('hex')
|
||||
const sign = crypto
|
||||
.createHmac('sha1', secretKey)
|
||||
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
||||
.digest('hex')
|
||||
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||
try {
|
||||
const res = await axios.request({
|
||||
@@ -62,12 +65,18 @@ async function dogecloudApi (
|
||||
}
|
||||
}
|
||||
|
||||
async function getDogeToken (accessKey: string, secretKey: string): Promise<{} | DogecloudTokenFull> {
|
||||
async function getDogeToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
||||
try {
|
||||
const data = await dogecloudApi('/auth/tmp_token.json', {
|
||||
const data = await dogecloudApi(
|
||||
'/auth/tmp_token.json',
|
||||
{
|
||||
channel: 'OSS_FULL',
|
||||
scopes: ['*']
|
||||
}, true, accessKey, secretKey)
|
||||
},
|
||||
true,
|
||||
accessKey,
|
||||
secretKey
|
||||
)
|
||||
return data
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
@@ -77,8 +86,15 @@ async function getDogeToken (accessKey: string, secretKey: string): Promise<{} |
|
||||
|
||||
export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||
try {
|
||||
const { url: rawUrl, type, config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap
|
||||
let { imgUrl, config: { region } } = configMap
|
||||
const {
|
||||
url: rawUrl,
|
||||
type,
|
||||
config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy }
|
||||
} = configMap
|
||||
let {
|
||||
imgUrl,
|
||||
config: { region }
|
||||
} = configMap
|
||||
if (type === 'aws-s3' || type === 'aws-s3-plist') {
|
||||
imgUrl = rawUrl || imgUrl || ''
|
||||
}
|
||||
@@ -164,8 +180,10 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode
|
||||
|
||||
export async function removeFileFromDogeInMain(configMap: IStringKeyMap) {
|
||||
try {
|
||||
const { config: { bucketName, AccessKey, SecretKey } } = configMap
|
||||
const token = await getDogeToken(AccessKey, SecretKey) as DogecloudTokenFull
|
||||
const {
|
||||
config: { bucketName, AccessKey, SecretKey }
|
||||
} = configMap
|
||||
const token = (await getDogeToken(AccessKey, SecretKey)) as DogecloudTokenFull
|
||||
const bucket = token.Buckets?.find(item => item.name === bucketName || item.s3Bucket === bucketName)
|
||||
const newConfigMap = Object.assign({}, configMap)
|
||||
newConfigMap.config = {
|
||||
@@ -227,7 +245,7 @@ export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileNa
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
|
||||
const uploadPath = `/${config.uploadPath || ''}/`.replace(/\/+/g, '/')
|
||||
const remote = path.join(uploadPath, fileName)
|
||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||
client.close()
|
||||
|
||||
@@ -9,11 +9,8 @@ export const isMacOS = process.platform === 'darwin'
|
||||
|
||||
let version: string | undefined
|
||||
|
||||
const clean = (version: string) => version.split('.').length === 1
|
||||
? `${version}.0.0`
|
||||
: version.split('.').length === 2
|
||||
? `${version}.0`
|
||||
: version
|
||||
const clean = (version: string) =>
|
||||
version.split('.').length === 1 ? `${version}.0.0` : version.split('.').length === 2 ? `${version}.0` : version
|
||||
|
||||
const parseVersion = (plist: string) => {
|
||||
const matches = /<key>ProductVersion<\/key>\s*<string>([\d.]+)<\/string>/.exec(plist)
|
||||
|
||||
@@ -4,14 +4,16 @@ import { configPaths } from '#/utils/configPaths'
|
||||
const getPicBeds = () => {
|
||||
const picBedTypes = picgo.helper.uploader.getIdList()
|
||||
const picBedFromDB = picgo.getConfig<IPicBedType[]>(configPaths.picBed.list) || []
|
||||
const picBeds = picBedTypes.map((item: string) => {
|
||||
const picBeds = picBedTypes
|
||||
.map((item: string) => {
|
||||
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
|
||||
return {
|
||||
type: item,
|
||||
name: picgo.helper.uploader.get(item)!.name || item,
|
||||
visible: visible ? visible.visible : true
|
||||
}
|
||||
}).sort((a) => {
|
||||
})
|
||||
.sort(a => {
|
||||
if (a.type === 'tcyun') {
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
|
||||
if (fileList?.length === 0) {
|
||||
return null // for uploading images in clipboard
|
||||
} else if ((fileList?.length || 0) > 0) {
|
||||
const result = fileList!.map(item => {
|
||||
const result = fileList!
|
||||
.map(item => {
|
||||
if (isUrl(item)) {
|
||||
return {
|
||||
path: item
|
||||
@@ -57,13 +58,12 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
|
||||
return null
|
||||
}
|
||||
}
|
||||
}).filter(item => item !== null) as Result
|
||||
})
|
||||
.filter(item => item !== null) as Result
|
||||
return result
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export {
|
||||
getUploadFiles
|
||||
}
|
||||
export { getUploadFiles }
|
||||
|
||||
@@ -13,6 +13,7 @@ export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IP
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
config[i].choices = (config[i].choices as Function)()
|
||||
}
|
||||
}
|
||||
@@ -20,13 +21,17 @@ export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IP
|
||||
}
|
||||
|
||||
export const completeUploaderMetaConfig = (originData: IStringKeyMap): IUploaderConfigListItem => {
|
||||
return Object.assign({
|
||||
return Object.assign(
|
||||
{
|
||||
_configName: 'Default'
|
||||
}, trimValues(originData), {
|
||||
},
|
||||
trimValues(originData),
|
||||
{
|
||||
_id: uuid(),
|
||||
_createdAt: Date.now(),
|
||||
_updatedAt: Date.now()
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +135,9 @@ export const deleteUploaderConfig = (type: string, id: string): IUploaderConfigI
|
||||
/**
|
||||
* upgrade old uploader config to new format
|
||||
*/
|
||||
export const upgradeUploaderConfig = (type: string): {
|
||||
export const upgradeUploaderConfig = (
|
||||
type: string
|
||||
): {
|
||||
configList: IStringKeyMap[]
|
||||
defaultId: string
|
||||
} => {
|
||||
|
||||
+36
-14
@@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import fs from 'fs-extra'
|
||||
import { NodeSSH, Config, SSHExecCommandResponse } from 'node-ssh-no-cpu-features'
|
||||
import path from 'path'
|
||||
@@ -26,7 +25,11 @@ class SSHClient {
|
||||
async connect(config: ISftpPlistConfig): Promise<boolean> {
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
const loginInfo: Config = privateKey
|
||||
? { username, privateKeyPath: privateKey, passphrase: passphrase || undefined }
|
||||
? {
|
||||
username,
|
||||
privateKeyPath: privateKey,
|
||||
passphrase: passphrase || undefined
|
||||
}
|
||||
: { username, password }
|
||||
try {
|
||||
await SSHClient.client.connect({
|
||||
@@ -46,30 +49,42 @@ class SSHClient {
|
||||
const client = new Client()
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
const loginInfo: Config = privateKey
|
||||
? { username, privateKey: fs.readFileSync(privateKey), passphrase: passphrase || undefined }
|
||||
? {
|
||||
username,
|
||||
privateKey: fs.readFileSync(privateKey),
|
||||
passphrase: passphrase || undefined
|
||||
}
|
||||
: { username, password }
|
||||
remote = this.changeWinStylePathToUnix(remote)
|
||||
if (remote === '/' || remote.includes('*')) return false
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
client.on('ready', () => {
|
||||
client.sftp((err, sftp) => {
|
||||
client
|
||||
.on('ready', () => {
|
||||
client.sftp(
|
||||
(
|
||||
err: any,
|
||||
sftp: {
|
||||
unlink: (arg0: string, arg1: (err: any) => void) => void
|
||||
}
|
||||
) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (err) reject(false)
|
||||
sftp.unlink(remote, (err) => {
|
||||
sftp.unlink(remote, (err: any) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (err) reject(false)
|
||||
client.end()
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}).connect({
|
||||
.connect({
|
||||
host: config.host,
|
||||
port: Number(config.port) || 22,
|
||||
...loginInfo
|
||||
})
|
||||
}
|
||||
)
|
||||
return await promise
|
||||
})
|
||||
return (await promise) as boolean
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
return false
|
||||
@@ -103,10 +118,14 @@ class SSHClient {
|
||||
}
|
||||
}
|
||||
|
||||
async putFile (local: string, remote: string, config: {
|
||||
async putFile(
|
||||
local: string,
|
||||
remote: string,
|
||||
config: {
|
||||
fileMode?: string
|
||||
dirMode?: string
|
||||
} = {}): Promise<boolean> {
|
||||
} = {}
|
||||
): Promise<boolean> {
|
||||
if (!this._isConnected) {
|
||||
throw new Error('SSH 未连接')
|
||||
}
|
||||
@@ -126,9 +145,12 @@ class SSHClient {
|
||||
}
|
||||
}
|
||||
|
||||
async mkdir (dirPath: string, config: {
|
||||
async mkdir(
|
||||
dirPath: string,
|
||||
config: {
|
||||
dirMode?: string
|
||||
} = {}): Promise<boolean> {
|
||||
} = {}
|
||||
): Promise<boolean> {
|
||||
if (!this._isConnected) {
|
||||
throw new Error('SSH 未连接')
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ const STORE_PATH = app.getPath('userData')
|
||||
const readFileAsBase64 = (filePath: string) => fs.readFileSync(filePath, { encoding: 'base64' })
|
||||
|
||||
const isHttpResSuccess = (res: any) => res.status >= 200 && res.status < 300
|
||||
const uploadOrUpdateMsg = (fileName: string, isUpdate: boolean = true) => isUpdate ? `update ${fileName} from PicList` : `upload ${fileName} from PicList`
|
||||
const uploadOrUpdateMsg = (fileName: string, isUpdate: boolean = true) =>
|
||||
isUpdate ? `update ${fileName} from PicList` : `upload ${fileName} from PicList`
|
||||
|
||||
const getSyncConfig = () => {
|
||||
return db.get(configPaths.settings.sync) || {
|
||||
return (
|
||||
db.get(configPaths.settings.sync) || {
|
||||
type: 'github',
|
||||
username: '',
|
||||
repo: '',
|
||||
@@ -26,6 +28,7 @@ const getSyncConfig = () => {
|
||||
token: '',
|
||||
proxy: ''
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const getProxyagent = (proxy: string | undefined) => {
|
||||
@@ -50,13 +53,7 @@ function getOctokit (syncConfig: ISyncConfig) {
|
||||
})
|
||||
}
|
||||
|
||||
const isSyncConfigValidate = ({
|
||||
type,
|
||||
username,
|
||||
repo,
|
||||
branch,
|
||||
token
|
||||
}: ISyncConfig) => {
|
||||
const isSyncConfigValidate = ({ type, username, repo, branch, token }: ISyncConfig) => {
|
||||
return type && username && repo && branch && token
|
||||
}
|
||||
|
||||
@@ -179,12 +176,16 @@ async function updateLocalToRemote (syncConfig: ISyncConfig, fileName: string) {
|
||||
}
|
||||
const data = shaRes.data as any
|
||||
const sha = data.sha
|
||||
const res = await axios.put(apiUrl, {
|
||||
const res = await axios.put(
|
||||
apiUrl,
|
||||
{
|
||||
...defaultConfig,
|
||||
sha
|
||||
}, {
|
||||
},
|
||||
{
|
||||
headers
|
||||
})
|
||||
}
|
||||
)
|
||||
return isHttpResSuccess(res)
|
||||
}
|
||||
default:
|
||||
@@ -220,7 +221,10 @@ async function uploadFile (fileName: string[]): Promise<number> {
|
||||
async function downloadAndWriteFile(url: string, localFilePath: string, config: any, isWriteJson = false) {
|
||||
const res = await axios.get(url, config)
|
||||
if (isHttpResSuccess(res)) {
|
||||
await fs.writeFile(localFilePath, isWriteJson ? JSON.stringify(res.data, null, 2) : Buffer.from(res.data.content, 'base64'))
|
||||
await fs.writeFile(
|
||||
localFilePath,
|
||||
isWriteJson ? JSON.stringify(res.data, null, 2) : Buffer.from(res.data.content, 'base64')
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -251,9 +255,14 @@ async function downloadRemoteToLocal (syncConfig: ISyncConfig, fileName: string)
|
||||
if (res.status === 200) {
|
||||
const data = res.data as any
|
||||
const downloadUrl = data.download_url
|
||||
return downloadAndWriteFile(downloadUrl, localFilePath, {
|
||||
return downloadAndWriteFile(
|
||||
downloadUrl,
|
||||
localFilePath,
|
||||
{
|
||||
httpsAgent: getProxyagent(proxy)
|
||||
}, true)
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -294,7 +303,4 @@ async function downloadFile (fileName: string[]): Promise<number> {
|
||||
return (await Promise.all(fileName.map(downloadFunc))).reduce((a, b) => a + b, 0)
|
||||
}
|
||||
|
||||
export {
|
||||
uploadFile,
|
||||
downloadFile
|
||||
}
|
||||
export { uploadFile, downloadFile }
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
id="app"
|
||||
:key="pageReloadCount"
|
||||
>
|
||||
<div id="app" :key="pageReloadCount">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,7 +22,6 @@ onBeforeMount(async () => {
|
||||
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -9,9 +9,7 @@ interface IConfigMap {
|
||||
|
||||
export default class AliyunApi {
|
||||
static #getKey(fileName: string, path?: string): string {
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
: fileName
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` : fileName
|
||||
}
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
|
||||
@@ -9,7 +9,7 @@ export default class AwsS3Api {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromS3InMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'AWS S3', error)
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class AwsS3Api {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromDogeInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'DogeCloud', error)
|
||||
|
||||
@@ -17,13 +17,15 @@ export default class GithubApi {
|
||||
|
||||
static #createKey(path: string | undefined, fileName: string): string {
|
||||
const formatedFileName = fileName.replace(/%2F/g, '/')
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}`
|
||||
: formatedFileName
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` : formatedFileName
|
||||
}
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, hash, config: { repo, token, branch, path } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
hash,
|
||||
config: { repo, token, branch, path }
|
||||
} = configMap
|
||||
const [owner, repoName] = repo.split('/')
|
||||
const octokit = GithubApi.#createOctokit(token)
|
||||
const key = GithubApi.#createKey(path, fileName)
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class HuaweicloudApi {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromHuaweiInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'HuaweiCloud', error)
|
||||
|
||||
@@ -11,10 +11,7 @@ export default class ImgurApi {
|
||||
static #baseUrl = 'https://api.imgur.com/3'
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
config: { clientId = '', username = '', accessToken = '' } = {},
|
||||
hash = ''
|
||||
} = configMap
|
||||
const { config: { clientId = '', username = '', accessToken = '' } = {}, hash = '' } = configMap
|
||||
let Authorization: string, apiUrl: string
|
||||
|
||||
if (username && accessToken) {
|
||||
|
||||
@@ -26,8 +26,7 @@ export default class LskyplistApi {
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
try {
|
||||
const response: AxiosResponse = await axios.delete(
|
||||
`${host}/api/v1/images/${hash}`, {
|
||||
const response: AxiosResponse = await axios.delete(`${host}/api/v1/images/${hash}`, {
|
||||
headers: v2Headers,
|
||||
timeout: 30000,
|
||||
httpsAgent: requestAgent
|
||||
|
||||
@@ -14,12 +14,9 @@ export default class PiclistApi {
|
||||
const url = `http://${host || '127.0.0.1'}:${port || 36677}/delete`
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await axios.post(
|
||||
url,
|
||||
{
|
||||
const response: AxiosResponse = await axios.post(url, {
|
||||
list: [fullResult]
|
||||
}
|
||||
)
|
||||
})
|
||||
if (response.status === 200 && response.data?.success) {
|
||||
deleteLog(fullResult, 'Piclist')
|
||||
return true
|
||||
|
||||
@@ -9,14 +9,17 @@ interface IConfigMap {
|
||||
|
||||
export default class QiniuApi {
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config: { accessKey, secretKey, bucket, path } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
config: { accessKey, secretKey, bucket, path }
|
||||
} = configMap
|
||||
const mac = new Qiniu.auth.digest.Mac(accessKey, secretKey)
|
||||
const qiniuConfig = new Qiniu.conf.Config()
|
||||
try {
|
||||
const bucketManager = new Qiniu.rs.BucketManager(mac, qiniuConfig)
|
||||
const formattedPath = path?.replace(/^\/+|\/+$/, '') || ''
|
||||
const key = path === '/' || !path ? fileName : `${formattedPath}/${fileName}`
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.delete(bucket, key, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -27,7 +30,7 @@ export default class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
deleteLog(fileName, 'Qiniu')
|
||||
return true
|
||||
|
||||
@@ -11,8 +11,7 @@ export default class SftpPlistApi {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_SFTP_FILE, getRawData(config),
|
||||
fileName) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_SFTP_FILE, getRawData(config), fileName)) || false
|
||||
: await removeFileFromSFTPInMain(getRawData(config), fileName)
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'SFTP', error)
|
||||
|
||||
@@ -20,8 +20,7 @@ export default class SmmsApi {
|
||||
const { token } = config
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await axios.get(
|
||||
`${SmmsApi.#baseUrl}/delete/${hash}`, {
|
||||
const response: AxiosResponse = await axios.get(`${SmmsApi.#baseUrl}/delete/${hash}`, {
|
||||
headers: {
|
||||
Authorization: token
|
||||
},
|
||||
|
||||
@@ -16,7 +16,10 @@ export default class TcyunApi {
|
||||
}
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config: { secretId, secretKey, bucket, area, path } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
config: { secretId, secretKey, bucket, area, path }
|
||||
} = configMap
|
||||
try {
|
||||
const cos = TcyunApi.#createCOS(secretId, secretKey)
|
||||
let key
|
||||
|
||||
@@ -9,7 +9,10 @@ interface IConfigMap {
|
||||
|
||||
export default class UpyunApi {
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config: { bucket, operator, password, path } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
config: { bucket, operator, password, path }
|
||||
} = configMap
|
||||
try {
|
||||
const service = new Upyun.Service(bucket, operator, password)
|
||||
const client = new Upyun.Client(service)
|
||||
|
||||
@@ -10,7 +10,10 @@ interface IConfigMap {
|
||||
|
||||
export default class WebdavApi {
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config: { host, username, password, path, sslEnabled, authType } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
config: { host, username, password, path, sslEnabled, authType }
|
||||
} = configMap
|
||||
const endpoint = formatEndpoint(host, sslEnabled)
|
||||
const options: WebDAVClientOptions = {
|
||||
username,
|
||||
@@ -19,10 +22,7 @@ export default class WebdavApi {
|
||||
if (authType === 'digest') {
|
||||
options.authType = AuthType.Digest
|
||||
}
|
||||
const ctx = createClient(
|
||||
endpoint,
|
||||
options
|
||||
)
|
||||
const ctx = createClient(endpoint, options)
|
||||
let key
|
||||
if (path === '/' || !path) {
|
||||
key = fileName
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
id="config-form"
|
||||
:class="props.colorMode === 'white' ? 'white' : ''"
|
||||
>
|
||||
<el-form
|
||||
ref="$form"
|
||||
label-position="left"
|
||||
label-width="50%"
|
||||
:model="ruleForm"
|
||||
size="small"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$T('UPLOADER_CONFIG_NAME')"
|
||||
required
|
||||
prop="_configName"
|
||||
>
|
||||
<el-input
|
||||
v-model="ruleForm._configName"
|
||||
type="input"
|
||||
:placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')"
|
||||
/>
|
||||
<div id="config-form" :class="props.colorMode === 'white' ? 'white' : ''">
|
||||
<el-form ref="$form" label-position="left" label-width="50%" :model="ruleForm" size="small">
|
||||
<el-form-item :label="$T('UPLOADER_CONFIG_NAME')" required prop="_configName">
|
||||
<el-input v-model="ruleForm._configName" type="input" :placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')" />
|
||||
</el-form-item>
|
||||
<!-- dynamic config -->
|
||||
<el-form-item
|
||||
@@ -32,18 +15,9 @@
|
||||
<el-row align="middle">
|
||||
{{ item.alias || item.name }}
|
||||
<template v-if="item.tips">
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
placement="right"
|
||||
:persistent="false"
|
||||
teleported
|
||||
>
|
||||
<el-tooltip class="item" effect="dark" placement="right" :persistent="false" teleported>
|
||||
<template #content>
|
||||
<span
|
||||
class="config-form-common-tips"
|
||||
v-html="transformMarkdownToHTML(item.tips)"
|
||||
/>
|
||||
<span class="config-form-common-tips" v-html="transformMarkdownToHTML(item.tips)" />
|
||||
</template>
|
||||
<el-icon class="ml-[4px] cursor-pointer hover:text-blue">
|
||||
<InfoFilled />
|
||||
@@ -124,19 +98,23 @@ const $form = ref<FormInstance>()
|
||||
const configList = ref<IPicGoPluginConfig[]>([])
|
||||
const ruleForm = reactive<IStringKeyMap>({})
|
||||
|
||||
watch(toRefs(props.config), (val: IPicGoPluginConfig[]) => {
|
||||
watch(
|
||||
toRefs(props.config),
|
||||
(val: IPicGoPluginConfig[]) => {
|
||||
handleConfigChange(val)
|
||||
}, {
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
function handleConfigChange(val: any) {
|
||||
handleConfig(val)
|
||||
}
|
||||
|
||||
async function validate(): Promise<IStringKeyMap | false> {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
$form.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
resolve(ruleForm)
|
||||
@@ -176,17 +154,16 @@ async function handleConfig (val: IPicGoPluginConfig[]) {
|
||||
const configId = $route.params.configId
|
||||
Object.assign(ruleForm, config)
|
||||
if (val.length > 0) {
|
||||
configList.value = cloneDeep(val).map((item) => {
|
||||
configList.value = cloneDeep(val).map(item => {
|
||||
if (!configId) return item
|
||||
let defaultValue = item.default !== undefined
|
||||
? item.default
|
||||
: item.type === 'checkbox'
|
||||
? []
|
||||
: null
|
||||
let defaultValue = item.default !== undefined ? item.default : item.type === 'checkbox' ? [] : null
|
||||
if (item.type === 'checkbox') {
|
||||
const defaults = item.choices?.filter((i: any) => {
|
||||
const defaults =
|
||||
item.choices
|
||||
?.filter((i: any) => {
|
||||
return i.checked
|
||||
}).map((i: any) => i.value) || []
|
||||
})
|
||||
.map((i: any) => i.value) || []
|
||||
defaultValue = union(defaultValue, defaults)
|
||||
}
|
||||
if (config && config[item.name] !== undefined) {
|
||||
@@ -200,7 +177,7 @@ async function handleConfig (val: IPicGoPluginConfig[]) {
|
||||
|
||||
async function getCurConfigFormData() {
|
||||
const configId = $route.params.configId
|
||||
const curTypeConfigList = await getConfig<IStringKeyMap[]>(`uploader.${props.id}.configList`) || []
|
||||
const curTypeConfigList = (await getConfig<IStringKeyMap[]>(`uploader.${props.id}.configList`)) || []
|
||||
return curTypeConfigList.find(i => i._id === configId) || {}
|
||||
}
|
||||
|
||||
@@ -218,7 +195,7 @@ defineExpose({
|
||||
getConfigType
|
||||
})
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
<style lang="stylus">
|
||||
.config-form-common-tips
|
||||
a
|
||||
color #409EFF
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
id="config-form"
|
||||
:class="props.colorMode === 'white' ? 'white' : ''"
|
||||
>
|
||||
<el-form
|
||||
ref="$form"
|
||||
label-position="left"
|
||||
label-width="50%"
|
||||
:model="ruleForm"
|
||||
size="small"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$T('UPLOADER_CONFIG_NAME')"
|
||||
required
|
||||
prop="_configName"
|
||||
>
|
||||
<el-input
|
||||
v-model="ruleForm._configName"
|
||||
type="input"
|
||||
:placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')"
|
||||
/>
|
||||
<div id="config-form" :class="props.colorMode === 'white' ? 'white' : ''">
|
||||
<el-form ref="$form" label-position="left" label-width="50%" :model="ruleForm" size="small">
|
||||
<el-form-item :label="$T('UPLOADER_CONFIG_NAME')" required prop="_configName">
|
||||
<el-input v-model="ruleForm._configName" type="input" :placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')" />
|
||||
</el-form-item>
|
||||
<!-- dynamic config -->
|
||||
<el-form-item
|
||||
@@ -98,19 +81,23 @@ const $form = ref<FormInstance>()
|
||||
const configList = ref<IPicGoPluginConfig[]>([])
|
||||
const ruleForm = reactive<IStringKeyMap>({})
|
||||
|
||||
watch(() => props.config, (val: IPicGoPluginConfig[]) => {
|
||||
watch(
|
||||
() => props.config,
|
||||
(val: IPicGoPluginConfig[]) => {
|
||||
handleConfigChange(val)
|
||||
}, {
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
function handleConfigChange(val: any) {
|
||||
handleConfig(val)
|
||||
}
|
||||
|
||||
async function validate(): Promise<IStringKeyMap | false> {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
$form.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
resolve(ruleForm)
|
||||
@@ -141,16 +128,15 @@ async function handleConfig (val: IPicGoPluginConfig[]) {
|
||||
const config = await getCurConfigFormData()
|
||||
Object.assign(ruleForm, config)
|
||||
if (val.length > 0) {
|
||||
configList.value = cloneDeep(val).map((item) => {
|
||||
let defaultValue = item.default !== undefined
|
||||
? item.default
|
||||
: item.type === 'checkbox'
|
||||
? []
|
||||
: null
|
||||
configList.value = cloneDeep(val).map(item => {
|
||||
let defaultValue = item.default !== undefined ? item.default : item.type === 'checkbox' ? [] : null
|
||||
if (item.type === 'checkbox') {
|
||||
const defaults = item.choices?.filter((i: any) => {
|
||||
const defaults =
|
||||
item.choices
|
||||
?.filter((i: any) => {
|
||||
return i.checked
|
||||
}).map((i: any) => i.value) || []
|
||||
})
|
||||
.map((i: any) => i.value) || []
|
||||
defaultValue = union(defaultValue, defaults)
|
||||
}
|
||||
if (config && config[item.name] !== undefined) {
|
||||
@@ -163,7 +149,7 @@ async function handleConfig (val: IPicGoPluginConfig[]) {
|
||||
}
|
||||
|
||||
async function getCurConfigFormData() {
|
||||
return await getConfig<IStringKeyMap>(`${props.id}`) || {}
|
||||
return (await getConfig<IStringKeyMap>(`${props.id}`)) || {}
|
||||
}
|
||||
|
||||
function updateRuleForm(key: string, value: any) {
|
||||
@@ -180,7 +166,7 @@ defineExpose({
|
||||
getConfigType
|
||||
})
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
<style lang="stylus">
|
||||
#config-form
|
||||
.el-form
|
||||
label
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<el-image
|
||||
:src="isShowThumbnail && item.isImage ?
|
||||
base64Image
|
||||
: require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
|
||||
:src="
|
||||
isShowThumbnail && item.isImage
|
||||
? base64Image
|
||||
: require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)
|
||||
"
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
style="height: 100px; width: 100%; margin: 0 auto"
|
||||
>
|
||||
<template #placeholder>
|
||||
<el-icon>
|
||||
@@ -15,7 +17,7 @@
|
||||
<el-image
|
||||
:src="require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
style="height: 100px; width: 100%; margin: 0 auto"
|
||||
/>
|
||||
</template>
|
||||
</el-image>
|
||||
|
||||
@@ -6,18 +6,10 @@
|
||||
size="default"
|
||||
:model="waterMarkForm"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISADDWM')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="waterMarkForm.isAddWatermark"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISADDWM')">
|
||||
<el-switch v-model="waterMarkForm.isAddWatermark" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')"
|
||||
>
|
||||
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')">
|
||||
<el-radio-group v-model="waterMarkForm.watermarkType">
|
||||
<el-radio value="text">
|
||||
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_TEXT') }}
|
||||
@@ -27,23 +19,11 @@
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="waterMarkForm.isFullScreenWatermark"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')">
|
||||
<el-switch v-model="waterMarkForm.isFullScreenWatermark" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="waterMarkForm.watermarkDegree"
|
||||
:step="1"
|
||||
/>
|
||||
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')">
|
||||
<el-input-number v-model="waterMarkForm.watermarkDegree" :step="1" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
|
||||
@@ -57,25 +37,14 @@
|
||||
>
|
||||
<el-input v-model="waterMarkForm.watermarkFontPath" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="waterMarkForm.watermarkScaleRatio"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.01"
|
||||
/>
|
||||
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')">
|
||||
<el-input-number v-model="waterMarkForm.watermarkScaleRatio" :min="0" :max="1" :step="0.01" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR')"
|
||||
>
|
||||
<el-color-picker
|
||||
v-model="waterMarkForm.watermarkColor"
|
||||
show-alpha
|
||||
/>
|
||||
<el-color-picker v-model="waterMarkForm.watermarkColor" show-alpha />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'image'"
|
||||
@@ -83,175 +52,76 @@
|
||||
>
|
||||
<el-input v-model="waterMarkForm.watermarkImagePath" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="waterMarkForm.isAddWatermark"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="waterMarkForm.watermarkPosition"
|
||||
>
|
||||
<el-radio
|
||||
v-for="item in waterMarkPositionMap"
|
||||
:key="item[0]"
|
||||
:value="item[0]"
|
||||
>
|
||||
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')">
|
||||
<el-radio-group v-model="waterMarkForm.watermarkPosition">
|
||||
<el-radio v-for="item in waterMarkPositionMap" :key="item[0]" :value="item[0]">
|
||||
{{ item[1] }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isRemoveExif"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF')">
|
||||
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="compressForm.quality"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="1"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
|
||||
<el-input-number v-model="compressForm.quality" :min="0" :max="100" :step="1" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isConvert"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')">
|
||||
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isConvert"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')"
|
||||
>
|
||||
<el-select
|
||||
v-model="compressForm.convertFormat"
|
||||
:persistent="false"
|
||||
teleported
|
||||
>
|
||||
<el-option
|
||||
v-for="item in availableFormat"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')">
|
||||
<el-select v-model="compressForm.convertFormat" :persistent="false" teleported>
|
||||
<el-option v-for="item in availableFormat" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isConvert"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')"
|
||||
>
|
||||
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')">
|
||||
<el-input
|
||||
v-model="formatConvertObj"
|
||||
placeholder="{"jpg": "png", "png": "jpg"}"
|
||||
placeholder='{"jpg": "png", "png": "jpg"}'
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLIP')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isFlip"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLIP')">
|
||||
<el-switch v-model="compressForm.isFlip" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLOP')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isFlop"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLOP')">
|
||||
<el-switch v-model="compressForm.isFlop" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZE')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isReSize"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZE')">
|
||||
<el-switch v-model="compressForm.isReSize" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isReSize"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="compressForm.reSizeWidth"
|
||||
:min="0"
|
||||
/>
|
||||
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')">
|
||||
<el-input-number v-model="compressForm.reSizeWidth" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isReSize"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="compressForm.reSizeHeight"
|
||||
:min="0"
|
||||
/>
|
||||
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')">
|
||||
<el-input-number v-model="compressForm.reSizeHeight" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isReSize && compressForm.reSizeHeight! > 0 && compressForm.reSizeWidth === 0"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_HEIGHT')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.skipReSizeOfSmallImg"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isReSize && compressForm.reSizeWidth! > 0 && compressForm.reSizeHeight === 0"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_WIDTH')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.skipReSizeOfSmallImg"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZEBYPERCENT')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isReSizeByPercent"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZEBYPERCENT')">
|
||||
<el-switch v-model="compressForm.isReSizeByPercent" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isReSizeByPercent"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="compressForm.reSizePercent"
|
||||
:min="0"
|
||||
/>
|
||||
<el-form-item v-show="compressForm.isReSizeByPercent" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')">
|
||||
<el-input-number v-model="compressForm.reSizePercent" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="compressForm.isRotate"
|
||||
:style="switchStyle"
|
||||
/>
|
||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')">
|
||||
<el-switch v-model="compressForm.isRotate" :style="switchStyle" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="compressForm.isRotate"
|
||||
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="compressForm.rotateDegree"
|
||||
:step="1"
|
||||
/>
|
||||
<el-form-item v-show="compressForm.isRotate" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')">
|
||||
<el-input-number v-model="compressForm.rotateDegree" :step="1" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSaveConfig"
|
||||
>
|
||||
<el-button type="primary" @click="handleSaveConfig">
|
||||
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_CONFIRM') }}
|
||||
</el-button>
|
||||
<el-button @click="closeDialog">
|
||||
@@ -283,7 +153,29 @@ const waterMarkPositionMap = new Map([
|
||||
['centre', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_CENTER')]
|
||||
])
|
||||
const imageExtList = ['jpg', 'jpeg', 'png', 'webp', 'bmp', 'tiff', 'tif', 'svg', 'ico', 'avif', 'heif', 'heic']
|
||||
const availableFormat = ['avif', 'dz', 'fits', 'gif', 'heif', 'input', 'jpeg', 'jpg', 'jp2', 'jxl', 'magick', 'openslide', 'pdf', 'png', 'ppm', 'raw', 'svg', 'tiff', 'tif', 'v', 'webp']
|
||||
const availableFormat = [
|
||||
'avif',
|
||||
'dz',
|
||||
'fits',
|
||||
'gif',
|
||||
'heif',
|
||||
'input',
|
||||
'jpeg',
|
||||
'jpg',
|
||||
'jp2',
|
||||
'jxl',
|
||||
'magick',
|
||||
'openslide',
|
||||
'pdf',
|
||||
'png',
|
||||
'ppm',
|
||||
'raw',
|
||||
'svg',
|
||||
'tiff',
|
||||
'tif',
|
||||
'v',
|
||||
'webp'
|
||||
]
|
||||
|
||||
const waterMarkForm = reactive<IBuildInWaterMarkOptions>({
|
||||
isAddWatermark: false,
|
||||
@@ -324,8 +216,7 @@ function handleSaveConfig () {
|
||||
let iformatConvertObj = {}
|
||||
try {
|
||||
iformatConvertObj = JSON.parse(formatConvertObj.value)
|
||||
} catch (error) {
|
||||
}
|
||||
} catch (error) {}
|
||||
const formatConvertObjEntries = Object.entries(iformatConvertObj)
|
||||
const formatConvertObjEntriesFilter = formatConvertObjEntries.filter((item: any) => {
|
||||
return imageExtList.includes(item[0]) && availableFormat.includes(item[1])
|
||||
@@ -342,7 +233,7 @@ async function initData () {
|
||||
const compress = await getConfig<any>(configPaths.buildIn.compress)
|
||||
const watermark = await getConfig<any>(configPaths.buildIn.watermark)
|
||||
if (compress) {
|
||||
compressFormKeys.forEach((key) => {
|
||||
compressFormKeys.forEach(key => {
|
||||
compressForm[key] = compress[key] ?? compressForm[key]
|
||||
})
|
||||
try {
|
||||
@@ -356,7 +247,7 @@ async function initData () {
|
||||
}
|
||||
}
|
||||
if (watermark) {
|
||||
waterMarkFormKeys.forEach((key) => {
|
||||
waterMarkFormKeys.forEach(key => {
|
||||
waterMarkForm[key] = watermark[key] ?? waterMarkForm[key]
|
||||
})
|
||||
waterMarkForm.watermarkColor = watermark.watermarkColor === '' ? '#CCCCCC73' : watermark.watermarkColor
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
<template>
|
||||
<el-image
|
||||
:src="imageSource"
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
>
|
||||
<el-image :src="imageSource" fit="contain" style="height: 100px; width: 100%; margin: 0 auto">
|
||||
<template #placeholder>
|
||||
<el-icon>
|
||||
<Loading />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #error>
|
||||
<el-image
|
||||
:src="iconPath"
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
/>
|
||||
<el-image :src="iconPath" fit="contain" style="height: 100px; width: 100%; margin: 0 auto" />
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
@@ -43,7 +35,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const imageSource = computed(() => {
|
||||
return (props.isShowThumbnail && props.item.isImage && success.value)
|
||||
return props.isShowThumbnail && props.item.isImage && success.value
|
||||
? base64Url.value
|
||||
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||
})
|
||||
@@ -91,5 +83,4 @@ const fetchImage = async () => {
|
||||
watch(() => [props.url, props.item], fetchImage, { deep: true })
|
||||
|
||||
onMounted(fetchImage)
|
||||
|
||||
</script>
|
||||
|
||||
@@ -32,11 +32,13 @@ export default defineComponent({
|
||||
const success = ref(false)
|
||||
|
||||
const imageSource = computed(() => {
|
||||
return (props.isShowThumbnail && props.item.isImage && success.value)
|
||||
return props.isShowThumbnail && props.item.isImage && success.value
|
||||
? base64Url.value
|
||||
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||
})
|
||||
const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`))
|
||||
const iconPath = computed(() =>
|
||||
require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||
)
|
||||
|
||||
async function getWebdavHeader(key: string) {
|
||||
let headers = {} as any
|
||||
@@ -79,24 +81,14 @@ export default defineComponent({
|
||||
onMounted(fetchImage)
|
||||
|
||||
return () => (
|
||||
<ElImage
|
||||
src={imageSource.value}
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
>
|
||||
<ElImage src={imageSource.value} fit='contain' style='height: 100px;width: 100%;margin: 0 auto;'>
|
||||
{{
|
||||
placeholder: () => (
|
||||
<ElIcon>
|
||||
<Loading />
|
||||
</ElIcon>
|
||||
),
|
||||
error: () => (
|
||||
<ElImage
|
||||
src={iconPath.value}
|
||||
fit="contain"
|
||||
style="height: 100px;width: 100%;margin: 0 auto;"
|
||||
/>
|
||||
)
|
||||
error: () => <ElImage src={iconPath.value} fit='contain' style='height: 100px;width: 100%;margin: 0 auto;' />
|
||||
}}
|
||||
</ElImage>
|
||||
)
|
||||
|
||||
@@ -5,22 +5,12 @@
|
||||
:modal-append-to-body="false"
|
||||
append-to-body
|
||||
>
|
||||
<el-input
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"
|
||||
/>
|
||||
<el-input v-model="inputBoxValue" :placeholder="inputBoxOptions.placeholder" />
|
||||
<template #footer>
|
||||
<el-button
|
||||
round
|
||||
@click="handleInputBoxCancel"
|
||||
>
|
||||
<el-button round @click="handleInputBoxCancel">
|
||||
{{ $T('CANCEL') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
round
|
||||
@click="handleInputBoxConfirm"
|
||||
>
|
||||
<el-button type="primary" round @click="handleInputBoxConfirm">
|
||||
{{ $T('CONFIRM') }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -35,10 +25,7 @@ import { T as $T } from '@/i18n/index'
|
||||
import $bus from '@/utils/bus'
|
||||
import { sendToMain } from '@/utils/common'
|
||||
|
||||
import {
|
||||
SHOW_INPUT_BOX,
|
||||
SHOW_INPUT_BOX_RESPONSE
|
||||
} from '#/events/constants'
|
||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '#/events/constants'
|
||||
|
||||
const inputBoxValue = ref('')
|
||||
const showInputBoxVisible = ref(false)
|
||||
@@ -80,12 +67,10 @@ onBeforeUnmount(() => {
|
||||
ipcRenderer.removeListener(SHOW_INPUT_BOX, ipcEventHandler)
|
||||
$bus.off(SHOW_INPUT_BOX)
|
||||
})
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'InputBoxDialog'
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
</style>
|
||||
<style lang="stylus"></style>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<div class="toolbox-handler">
|
||||
<ElButton
|
||||
type="primary"
|
||||
:link="true"
|
||||
@click="() => props.handler(value)"
|
||||
>
|
||||
<ElButton type="primary" :link="true" @click="() => props.handler(value)">
|
||||
{{ props.handlerText }}
|
||||
</ElButton>
|
||||
</div>
|
||||
@@ -21,12 +17,10 @@ interface IProps {
|
||||
}
|
||||
|
||||
const props = defineProps<IProps>()
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ToolboxHandler'
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
</style>
|
||||
<style lang="stylus"></style>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<el-icon
|
||||
:color="color"
|
||||
class="toolbox-status-icon"
|
||||
>
|
||||
<el-icon :color="color" class="toolbox-status-icon">
|
||||
<template v-if="props.status === IToolboxItemCheckStatus.SUCCESS">
|
||||
<SuccessFilled />
|
||||
</template>
|
||||
@@ -37,14 +34,13 @@ const color = computed(() => {
|
||||
return '#909399'
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'ToolboxStatusIcon'
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
<style lang="stylus">
|
||||
.toolbox-status-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,4 @@ const T = (key: ILocalesKey, args: IStringKeyMap = {}): string => {
|
||||
return i18nManager.T(key, args)
|
||||
}
|
||||
|
||||
export {
|
||||
i18nManager,
|
||||
T
|
||||
}
|
||||
export { i18nManager, T }
|
||||
|
||||
+47
-120
@@ -1,48 +1,24 @@
|
||||
<template>
|
||||
<div id="main-page">
|
||||
<div
|
||||
class="fake-title-bar"
|
||||
>
|
||||
<div class="fake-title-bar__title">
|
||||
PicList - {{ version }}
|
||||
</div>
|
||||
<div
|
||||
v-if="osGlobal !== 'darwin'"
|
||||
class="handle-bar"
|
||||
>
|
||||
<div class="fake-title-bar">
|
||||
<div class="fake-title-bar__title">PicList - {{ version }}</div>
|
||||
<div v-if="osGlobal !== 'darwin'" class="handle-bar">
|
||||
<el-icon
|
||||
class="minus"
|
||||
:color="isAlwaysOnTop ? '#409EFF' : '#fff'"
|
||||
size="20"
|
||||
style="margin-right: 10px;"
|
||||
style="margin-right: 10px"
|
||||
@click="setAlwaysOnTop"
|
||||
>
|
||||
<ArrowUpBold />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="minus"
|
||||
color="#fff"
|
||||
size="20"
|
||||
style="margin-right: 10px;"
|
||||
@click="minimizeWindow"
|
||||
>
|
||||
<el-icon class="minus" color="#fff" size="20" style="margin-right: 10px" @click="minimizeWindow">
|
||||
<SemiSelect />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="plus"
|
||||
color="orange"
|
||||
size="20"
|
||||
style="margin-right: 10px;"
|
||||
@click="openMiniWindow"
|
||||
>
|
||||
<el-icon class="plus" color="orange" size="20" style="margin-right: 10px" @click="openMiniWindow">
|
||||
<ArrowDownBold />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="close"
|
||||
color="#fff"
|
||||
size="20"
|
||||
@click="closeWindow"
|
||||
>
|
||||
<el-icon class="close" color="#fff" size="20" @click="closeWindow">
|
||||
<CloseBold />
|
||||
</el-icon>
|
||||
</div>
|
||||
@@ -56,19 +32,9 @@
|
||||
status="success"
|
||||
class="progress-bar"
|
||||
/>
|
||||
<el-row
|
||||
style="padding-top: 22px;"
|
||||
class="main-content"
|
||||
>
|
||||
<el-col
|
||||
class="side-bar-menu"
|
||||
>
|
||||
<el-menu
|
||||
class="picgo-sidebar"
|
||||
:default-active="defaultActive"
|
||||
:unique-opened="true"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-row style="padding-top: 22px" class="main-content">
|
||||
<el-col class="side-bar-menu">
|
||||
<el-menu class="picgo-sidebar" :default-active="defaultActive" :unique-opened="true" @select="handleSelect">
|
||||
<el-menu-item :index="routerConfig.UPLOAD_PAGE">
|
||||
<el-icon>
|
||||
<UploadFilled />
|
||||
@@ -87,21 +53,14 @@
|
||||
</el-icon>
|
||||
<span>{{ $T('GALLERY') }}</span>
|
||||
</el-menu-item>
|
||||
<el-sub-menu
|
||||
index="sub-menu"
|
||||
:show-timeout="0"
|
||||
:hide-timeout="0"
|
||||
:popper-offset="0"
|
||||
>
|
||||
<el-sub-menu index="sub-menu" :show-timeout="0" :hide-timeout="0" :popper-offset="0">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Menu />
|
||||
</el-icon>
|
||||
<span>{{ $T('PICBEDS_SETTINGS') }}</span>
|
||||
</template>
|
||||
<template
|
||||
v-for="item in picBedGlobal"
|
||||
>
|
||||
<template v-for="item in picBedGlobal">
|
||||
<el-menu-item
|
||||
v-if="item.visible"
|
||||
:key="item.type"
|
||||
@@ -123,39 +82,22 @@
|
||||
</el-icon>
|
||||
<span>{{ $T('PLUGIN_SETTINGS') }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item
|
||||
:index="routerConfig.DocumentPage"
|
||||
>
|
||||
<el-menu-item :index="routerConfig.DocumentPage">
|
||||
<el-icon>
|
||||
<Link />
|
||||
</el-icon>
|
||||
<span>{{ $T('MANUAL') }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<el-icon
|
||||
class="info-window"
|
||||
@click="openMenu"
|
||||
>
|
||||
<el-icon class="info-window" @click="openMenu">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="21"
|
||||
:offset="3"
|
||||
style="height: 100%"
|
||||
class="main-wrapper"
|
||||
>
|
||||
<router-view
|
||||
v-slot="{ Component }"
|
||||
>
|
||||
<transition
|
||||
name="picgo-fade"
|
||||
mode="out-in"
|
||||
>
|
||||
<el-col :span="21" :offset="3" style="height: 100%" class="main-wrapper">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="picgo-fade" mode="out-in">
|
||||
<keep-alive :include="keepAlivePages">
|
||||
<component
|
||||
:is="Component"
|
||||
/>
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
@@ -171,27 +113,10 @@
|
||||
lock-scroll
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
label-position="left"
|
||||
label-width="70px"
|
||||
size="small"
|
||||
>
|
||||
<el-form-item
|
||||
:label="$T('CHOOSE_PICBED')"
|
||||
>
|
||||
<el-select
|
||||
v-model="choosedPicBedForQRCode"
|
||||
multiple
|
||||
collapse-tags
|
||||
:persistent="false"
|
||||
teleported
|
||||
>
|
||||
<el-option
|
||||
v-for="item in picBedGlobal"
|
||||
:key="item.type"
|
||||
:label="item.name"
|
||||
:value="item.type"
|
||||
/>
|
||||
<el-form label-position="left" label-width="70px" size="small">
|
||||
<el-form-item :label="$T('CHOOSE_PICBED')">
|
||||
<el-select v-model="choosedPicBedForQRCode" multiple collapse-tags :persistent="false" teleported>
|
||||
<el-option v-for="item in picBedGlobal" :key="item.type" :label="item.name" :value="item.type" />
|
||||
</el-select>
|
||||
<el-button
|
||||
v-show="choosedPicBedForQRCode.length > 0"
|
||||
@@ -205,11 +130,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="qrcode-container">
|
||||
<qrcode-vue
|
||||
v-show="choosedPicBedForQRCode.length > 0"
|
||||
:size="280"
|
||||
:value="picBedConfigString"
|
||||
/>
|
||||
<qrcode-vue v-show="choosedPicBedForQRCode.length > 0" :size="280" :value="picBedConfigString" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
<input-box-dialog />
|
||||
@@ -231,11 +152,7 @@ import {
|
||||
Link,
|
||||
ArrowUpBold
|
||||
} from '@element-plus/icons-vue'
|
||||
import {
|
||||
ipcRenderer,
|
||||
IpcRendererEvent,
|
||||
clipboard
|
||||
} from 'electron'
|
||||
import { ipcRenderer, IpcRendererEvent, clipboard } from 'electron'
|
||||
import { ElMessage as $message, ElMessageBox } from 'element-plus'
|
||||
import pick from 'lodash/pick'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
@@ -249,9 +166,7 @@ import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||
import { sendRPC } from '@/utils/common'
|
||||
import { osGlobal, picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
||||
|
||||
import {
|
||||
SHOW_MAIN_PAGE_QRCODE
|
||||
} from '#/events/constants'
|
||||
import { SHOW_MAIN_PAGE_QRCODE } from '#/events/constants'
|
||||
import { configPaths, manualPageOpenType } from '#/utils/configPaths'
|
||||
import { II18nLanguage, IRPCActionType } from '#/types/enum'
|
||||
|
||||
@@ -265,7 +180,10 @@ const qrcodeVisible = ref(false)
|
||||
const picBedConfigString = ref('')
|
||||
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
||||
const isAlwaysOnTop = ref(false)
|
||||
const keepAlivePages = $router.getRoutes().filter(item => item.meta.keepAlive).map(item => item.name as string)
|
||||
const keepAlivePages = $router
|
||||
.getRoutes()
|
||||
.filter(item => item.meta.keepAlive)
|
||||
.map(item => item.name as string)
|
||||
|
||||
const isShowprogress = ref(false)
|
||||
const progress = ref(0)
|
||||
@@ -281,7 +199,9 @@ onBeforeMount(() => {
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => choosedPicBedForQRCode, (val) => {
|
||||
watch(
|
||||
() => choosedPicBedForQRCode,
|
||||
val => {
|
||||
if (val.value.length > 0) {
|
||||
nextTick(async () => {
|
||||
const picBedConfig = await getConfig('picBed')
|
||||
@@ -289,15 +209,21 @@ watch(() => choosedPicBedForQRCode, (val) => {
|
||||
picBedConfigString.value = JSON.stringify(config)
|
||||
})
|
||||
}
|
||||
}, { deep: true })
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
const handleSelect = async (index: string) => {
|
||||
defaultActive.value = index
|
||||
if (index === routerConfig.DocumentPage) {
|
||||
const manualPageOpenSetting = await getConfig<manualPageOpenType>(configPaths.settings.manualPageOpen)
|
||||
const lang = await getConfig(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
const lang = (await getConfig(configPaths.settings.language)) || II18nLanguage.ZH_CN
|
||||
const openManual = () => sendRPC(IRPCActionType.OPEN_MANUAL_WINDOW)
|
||||
const openExternal = () => sendRPC(IRPCActionType.OPEN_URL, lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html')
|
||||
const openExternal = () =>
|
||||
sendRPC(
|
||||
IRPCActionType.OPEN_URL,
|
||||
lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html'
|
||||
)
|
||||
|
||||
if (!manualPageOpenSetting) {
|
||||
ElMessageBox.confirm($T('MANUAL_PAGE_OPEN_TIP'), $T('MANUAL_PAGE_OPEN_TIP_TITLE'), {
|
||||
@@ -305,10 +231,12 @@ const handleSelect = async (index: string) => {
|
||||
cancelButtonText: $T('MANUAL_PAGE_OPEN_BY_BUILD_IN'),
|
||||
type: 'info',
|
||||
center: true
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
saveConfig(configPaths.settings.manualPageOpen, 'browser')
|
||||
openExternal()
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
saveConfig(configPaths.settings.manualPageOpen, 'window')
|
||||
openManual()
|
||||
})
|
||||
@@ -359,7 +287,7 @@ function setAlwaysOnTop () {
|
||||
sendRPC(IRPCActionType.MAIN_WINDOW_ON_TOP)
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async (to) => {
|
||||
onBeforeRouteUpdate(async to => {
|
||||
if (to.params.type) {
|
||||
defaultActive.value = `${routerConfig.UPLOADER_CONFIG_PAGE}-${to.params.type}`
|
||||
} else {
|
||||
@@ -371,14 +299,13 @@ onBeforeUnmount(() => {
|
||||
ipcRenderer.removeAllListeners(SHOW_MAIN_PAGE_QRCODE)
|
||||
ipcRenderer.removeAllListeners('updateProgress')
|
||||
})
|
||||
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MainPage'
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
<style lang="stylus">
|
||||
$darwinBg = transparentify(#172426, #000, 0.7)
|
||||
.setting-list-scroll
|
||||
height 800px
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
<template>
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span style="position:absolute;left: 0;">
|
||||
<span
|
||||
v-for="(segment, index) in segments"
|
||||
:key="index"
|
||||
:style="segment.style"
|
||||
>
|
||||
<span style="position: absolute; left: 0">
|
||||
<span v-for="(segment, index) in segments" :key="index" :style="segment.style">
|
||||
{{ segment.text }}
|
||||
</span>
|
||||
<el-tooltip
|
||||
:content="tooltip"
|
||||
effect="dark"
|
||||
placement="right"
|
||||
:persistent="false"
|
||||
teleported
|
||||
>
|
||||
<el-tooltip :content="tooltip" effect="dark" placement="right" :persistent="false" teleported>
|
||||
<el-icon>
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -26,7 +16,7 @@
|
||||
v-model="value"
|
||||
:active-text="activeText"
|
||||
:inactive-text="inactiveText"
|
||||
style="--el-switch-on-color: #13ce66;--el-switch-off-color: #ff4949; position:absolute; right: 0;"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949; position: absolute; right: 0"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
@@ -35,12 +25,11 @@
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
|
||||
defineProps<{
|
||||
tooltip?: string,
|
||||
activeText?: string,
|
||||
inactiveText?: string,
|
||||
segments?: { text: string, style: string }[],
|
||||
tooltip?: string
|
||||
activeText?: string
|
||||
inactiveText?: string
|
||||
segments?: { text: string; style: string }[]
|
||||
}>()
|
||||
|
||||
const value = defineModel<boolean>()
|
||||
|
||||
</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user