mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-12 19:40:12 +08:00
🚧 WIP(custom): v3.0.0 migrate to vite and esm
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
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 { IRemoteNotice, IRemoteNoticeAction, IRemoteNoticeLocalCountStorage } from '#/types/types'
|
||||
import { showNotification } from '~/utils/common'
|
||||
|
||||
// for test
|
||||
const REMOTE_NOTICE_URL = 'https://release.piclist.cn/remote-notice.json'
|
||||
@@ -21,12 +23,12 @@ class RemoteNoticeHandler {
|
||||
private remoteNotice: IRemoteNotice | null = null
|
||||
private remoteNoticeLocalCountStorage: IRemoteNoticeLocalCountStorage | null = null
|
||||
|
||||
async init() {
|
||||
async init () {
|
||||
this.remoteNotice = await this.getRemoteNoticeInfo()
|
||||
this.initLocalCountStorage()
|
||||
}
|
||||
|
||||
private initLocalCountStorage() {
|
||||
private initLocalCountStorage () {
|
||||
const localCountStorage = {}
|
||||
if (!fs.existsSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH)) {
|
||||
fs.writeFileSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, JSON.stringify({}))
|
||||
@@ -42,14 +44,14 @@ class RemoteNoticeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private saveLocalCountStorage(newData?: IRemoteNoticeLocalCountStorage) {
|
||||
private saveLocalCountStorage (newData?: IRemoteNoticeLocalCountStorage) {
|
||||
if (newData) {
|
||||
this.remoteNoticeLocalCountStorage = newData
|
||||
}
|
||||
fs.writeFileSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, JSON.stringify(this.remoteNoticeLocalCountStorage))
|
||||
}
|
||||
|
||||
private async getRemoteNoticeInfo(): Promise<IRemoteNotice | null> {
|
||||
private async getRemoteNoticeInfo (): Promise<IRemoteNotice | null> {
|
||||
try {
|
||||
const noticeInfo = (await axios({
|
||||
method: 'get',
|
||||
@@ -66,7 +68,7 @@ class RemoteNoticeHandler {
|
||||
* if the notice is not shown or is always shown, then show the notice
|
||||
* @param action
|
||||
*/
|
||||
private checkActionCount(action: IRemoteNoticeAction) {
|
||||
private checkActionCount (action: IRemoteNoticeAction) {
|
||||
try {
|
||||
if (!this.remoteNoticeLocalCountStorage) {
|
||||
return true
|
||||
@@ -100,7 +102,7 @@ class RemoteNoticeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private async doActions(actions: IRemoteNoticeAction[]) {
|
||||
private async doActions (actions: IRemoteNoticeAction[]) {
|
||||
for (const action of actions) {
|
||||
if (this.checkActionCount(action)) {
|
||||
switch (action.type) {
|
||||
@@ -115,7 +117,7 @@ class RemoteNoticeHandler {
|
||||
body: action.data?.content || '',
|
||||
clickToCopy: !!action.data?.copyToClipboard,
|
||||
copyContent: action.data?.copyToClipboard || '',
|
||||
clickFn() {
|
||||
clickFn () {
|
||||
if (action.data?.url) {
|
||||
shell.openExternal(action.data.url)
|
||||
}
|
||||
@@ -161,7 +163,7 @@ class RemoteNoticeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
triggerHook(hook: IRemoteNoticeTriggerHook) {
|
||||
triggerHook (hook: IRemoteNoticeTriggerHook) {
|
||||
if (!this.remoteNotice || !this.remoteNotice.list) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import { globalShortcut } from 'electron'
|
||||
|
||||
import shortKeyService from 'apis/app/shortKey/shortKeyService'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
import logger from '@core/picgo/logger'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import shortKeyService from 'apis/app/shortKey/shortKeyService'
|
||||
import GuiApi from 'apis/gui'
|
||||
import { globalShortcut } from 'electron'
|
||||
|
||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||
import { IKeyCommandType, IPluginShortKeyConfig, IShortKeyConfig, IShortKeyConfigs, IShortKeyHandler } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
class ShortKeyHandler {
|
||||
private isInModifiedMode: boolean = false
|
||||
constructor() {
|
||||
constructor () {
|
||||
bus.on(TOGGLE_SHORTKEY_MODIFIED_MODE, flag => {
|
||||
this.isInModifiedMode = flag
|
||||
})
|
||||
}
|
||||
|
||||
init() {
|
||||
async init () {
|
||||
this.initBuiltInShortKey()
|
||||
this.initPluginsShortKey()
|
||||
await this.initPluginsShortKey()
|
||||
}
|
||||
|
||||
private initBuiltInShortKey() {
|
||||
private initBuiltInShortKey () {
|
||||
const commands = db.get(configPaths.settings.shortKey._path) as IShortKeyConfigs
|
||||
Object.keys(commands)
|
||||
.filter(item => item.includes('picgo:'))
|
||||
@@ -39,11 +38,11 @@ class ShortKeyHandler {
|
||||
})
|
||||
}
|
||||
|
||||
private initPluginsShortKey() {
|
||||
private async initPluginsShortKey () {
|
||||
// get enabled plugin
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
for (const item of pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(item)
|
||||
const plugin = await picgo.pluginLoader.getPlugin(item)
|
||||
// if a plugin has commands
|
||||
if (plugin && plugin.commands) {
|
||||
if (typeof plugin.commands !== 'function') {
|
||||
@@ -69,7 +68,7 @@ class ShortKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private registerShortKey(
|
||||
private registerShortKey (
|
||||
config: IShortKeyConfig | IPluginShortKeyConfig,
|
||||
command: string,
|
||||
handler: IShortKeyHandler,
|
||||
@@ -98,7 +97,7 @@ class ShortKeyHandler {
|
||||
}
|
||||
|
||||
// enable or disable shortKey
|
||||
bindOrUnbindShortKey(item: IShortKeyConfig, from: string): boolean {
|
||||
bindOrUnbindShortKey (item: IShortKeyConfig, from: string): boolean {
|
||||
const command = `${from}:${item.name}`
|
||||
if (item.enable === false) {
|
||||
globalShortcut.unregister(item.key)
|
||||
@@ -122,7 +121,7 @@ class ShortKeyHandler {
|
||||
}
|
||||
|
||||
// update shortKey bindings
|
||||
updateShortKey(item: IShortKeyConfig, oldKey: string, from: string): boolean {
|
||||
updateShortKey (item: IShortKeyConfig, oldKey: string, from: string): boolean {
|
||||
const command = `${from}:${item.name}`
|
||||
if (globalShortcut.isRegistered(item.key)) return false
|
||||
globalShortcut.unregister(oldKey)
|
||||
@@ -135,7 +134,7 @@ class ShortKeyHandler {
|
||||
return true
|
||||
}
|
||||
|
||||
private async handler(command: string) {
|
||||
private async handler (command: string) {
|
||||
if (this.isInModifiedMode) {
|
||||
return
|
||||
}
|
||||
@@ -151,8 +150,8 @@ class ShortKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
registerPluginShortKey(pluginName: string) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginName)
|
||||
async registerPluginShortKey (pluginName: string) {
|
||||
const plugin = await picgo.pluginLoader.getPlugin(pluginName)
|
||||
if (plugin && plugin.commands) {
|
||||
if (typeof plugin.commands !== 'function') {
|
||||
logger.warn(`${pluginName}'s commands is not a function`)
|
||||
@@ -171,7 +170,7 @@ class ShortKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
unregisterPluginShortKey(pluginName: string) {
|
||||
unregisterPluginShortKey (pluginName: string) {
|
||||
const commands = db.get(configPaths.settings.shortKey._path) as IShortKeyConfigs
|
||||
const keyList = Object.keys(commands)
|
||||
.filter(command => command.includes(pluginName))
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import { IShortKeyHandler } from '#/types/types'
|
||||
|
||||
class ShortKeyService {
|
||||
private commandList: Map<string, IShortKeyHandler> = new Map()
|
||||
registerCommand(command: string, handler: IShortKeyHandler) {
|
||||
registerCommand (command: string, handler: IShortKeyHandler) {
|
||||
this.commandList.set(command, handler)
|
||||
}
|
||||
|
||||
unregisterCommand(command: string) {
|
||||
unregisterCommand (command: string) {
|
||||
this.commandList.delete(command)
|
||||
}
|
||||
|
||||
getShortKeyHandler(command: string): IShortKeyHandler | null {
|
||||
getShortKeyHandler (command: string): IShortKeyHandler | null {
|
||||
const handler = this.commandList.get(command)
|
||||
if (handler) return handler
|
||||
logger.warn(`cannot find command: ${command}`)
|
||||
return null
|
||||
}
|
||||
|
||||
getCommandList() {
|
||||
getCommandList () {
|
||||
return [...this.commandList.keys()]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import uploader from 'apis/app/uploader'
|
||||
import { handleSecondaryUpload, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import {
|
||||
app,
|
||||
clipboard,
|
||||
@@ -10,31 +15,23 @@ import {
|
||||
Tray
|
||||
} from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import uploader from 'apis/app/uploader'
|
||||
import { handleSecondaryUpload, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import pkg from 'root/package.json'
|
||||
|
||||
import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
import { IBounds, ImgInfo } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { buildPicBedListMenu } from '~/events/remotes/menu'
|
||||
import { T } from '~/i18n'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { ensureFilePath, handleCopyUrl, setTray, tray } from '~/utils/common'
|
||||
import { isMacOSVersionGreaterThanOrEqualTo } from '~/utils/getMacOSVersion'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
|
||||
import pkg from 'root/package.json'
|
||||
import { hideMiniWindow, openMainWindow, openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
let contextMenu: Menu | null
|
||||
|
||||
export function setDockMenu() {
|
||||
export function setDockMenu () {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const dockMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
@@ -43,7 +40,7 @@ export function setDockMenu() {
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click() {
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
clipboardPoll.startListening()
|
||||
clipboardPoll.on('change', () => {
|
||||
@@ -56,7 +53,7 @@ export function setDockMenu() {
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click() {
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
clipboardPoll.stopListening()
|
||||
clipboardPoll.removeAllListeners()
|
||||
@@ -65,10 +62,10 @@ export function setDockMenu() {
|
||||
visible: isListeningClipboard
|
||||
}
|
||||
])
|
||||
app.dock.setMenu(dockMenu)
|
||||
app.dock?.setMenu(dockMenu)
|
||||
}
|
||||
|
||||
export function createMenu() {
|
||||
export function createMenu () {
|
||||
const submenu = buildPicBedListMenu()
|
||||
const appMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
@@ -77,7 +74,7 @@ export function createMenu() {
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
@@ -105,7 +102,7 @@ export function createMenu() {
|
||||
Menu.setApplicationMenu(appMenu)
|
||||
}
|
||||
|
||||
export function createContextMenu() {
|
||||
export function createContextMenu () {
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const isMiniWindowVisible =
|
||||
@@ -130,7 +127,7 @@ export function createContextMenu() {
|
||||
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
const template: (MenuItemConstructorOptions | MenuItem)[] = [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
{
|
||||
@@ -145,7 +142,7 @@ export function createContextMenu() {
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
@@ -158,7 +155,7 @@ export function createContextMenu() {
|
||||
0,
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
click () {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
@@ -183,7 +180,7 @@ export function createContextMenu() {
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
click () {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
@@ -205,7 +202,7 @@ export function createContextMenu() {
|
||||
},
|
||||
{
|
||||
label: T('ABOUT'),
|
||||
click() {
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicList',
|
||||
message: 'PicList',
|
||||
@@ -222,13 +219,13 @@ export function createContextMenu() {
|
||||
const getTrayIcon = () => {
|
||||
if (process.platform === 'darwin') {
|
||||
const isMacOSGreaterThan11 = isMacOSVersionGreaterThanOrEqualTo('11')
|
||||
return isMacOSGreaterThan11 ? `${__static}/menubar-newdarwinTemplate.png` : `${__static}/menubar.png`
|
||||
return isMacOSGreaterThan11 ? './resources/menubar-newdarwinTemplate.png' : './resources/menubar.png'
|
||||
} else {
|
||||
return `${__static}/menubar-nodarwin.png`
|
||||
return './resources/menubar-nodarwin.png'
|
||||
}
|
||||
}
|
||||
|
||||
export function createTray(tooltip: string) {
|
||||
export function createTray (tooltip: string) {
|
||||
const menubarPic = getTrayIcon()
|
||||
setTray(new Tray(menubarPic))
|
||||
tray.setToolTip(tooltip)
|
||||
@@ -241,6 +238,7 @@ export function createTray(tooltip: string) {
|
||||
createContextMenu()
|
||||
tray!.popUpContextMenu(contextMenu!)
|
||||
})
|
||||
|
||||
tray.on('click', (_, bounds) => {
|
||||
if (process.platform === 'darwin') {
|
||||
toggleWindow(bounds)
|
||||
@@ -293,9 +291,9 @@ export function createTray(tooltip: string) {
|
||||
|
||||
tray.on('drag-enter', () => {
|
||||
if (nativeTheme.shouldUseDarkColors) {
|
||||
tray!.setImage(`${__static}/upload-dark.png`)
|
||||
tray!.setImage('./resources/upload-dark.png')
|
||||
} else {
|
||||
tray!.setImage(`${__static}/upload.png`)
|
||||
tray!.setImage('./resources/upload.png')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -305,54 +303,56 @@ export function createTray(tooltip: string) {
|
||||
|
||||
// drop-files only be supported in macOS
|
||||
// so the tray window must be available
|
||||
tray.on('drop-files', async (_: Event, files: string[]) => {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
const rawInput = cloneDeep(files)
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const { needRestore, ctx } = await handleSecondaryUpload(trayWindow.webContents, files, 'tray')
|
||||
let imgs: ImgInfo[] | false = false
|
||||
if (needRestore) {
|
||||
const res = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.uploadReturnCtx(ctx ? ctx.processedInput : files, true)
|
||||
imgs = res ? res.output : false
|
||||
} else {
|
||||
imgs = await uploader.setWebContents(trayWindow.webContents).upload(files)
|
||||
}
|
||||
const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
const [pasteTextItem, shortUrl] = await pasteTemplate(
|
||||
pasteStyle,
|
||||
imgs[i],
|
||||
db.get(configPaths.settings.customLink)
|
||||
)
|
||||
imgs[i].shortUrl = shortUrl
|
||||
pasteText.push(pasteTextItem)
|
||||
const isShowResultNotification =
|
||||
if (process.platform === 'darwin') {
|
||||
(tray as any).on('drop-files', async (_: Event, files: string[]) => {
|
||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||
const rawInput = cloneDeep(files)
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||
const { needRestore, ctx } = await handleSecondaryUpload(trayWindow.webContents, files, 'tray')
|
||||
let imgs: ImgInfo[] | false = false
|
||||
if (needRestore) {
|
||||
const res = await uploader
|
||||
.setWebContents(trayWindow.webContents)
|
||||
.uploadReturnCtx(ctx ? ctx.processedInput : files, true)
|
||||
imgs = res ? res.output : false
|
||||
} else {
|
||||
imgs = await uploader.setWebContents(trayWindow.webContents).upload(files)
|
||||
}
|
||||
const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
const [pasteTextItem, shortUrl] = await pasteTemplate(
|
||||
pasteStyle,
|
||||
imgs[i],
|
||||
db.get(configPaths.settings.customLink)
|
||||
)
|
||||
imgs[i].shortUrl = shortUrl
|
||||
pasteText.push(pasteTextItem)
|
||||
const isShowResultNotification =
|
||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||
? true
|
||||
: !!db.get(configPaths.settings.uploadResultNotification)
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
body: shortUrl || imgs[i].imgUrl!
|
||||
if (isShowResultNotification) {
|
||||
const notification = new Notification({
|
||||
title: T('UPLOAD_SUCCEED'),
|
||||
body: shortUrl || imgs[i].imgUrl!
|
||||
// icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
}
|
||||
await GalleryDB.getInstance().insert(imgs[i])
|
||||
}
|
||||
await GalleryDB.getInstance().insert(imgs[i])
|
||||
handleCopyUrl(pasteText.join('\n'))
|
||||
trayWindow.webContents.send('dragFiles', imgs)
|
||||
}
|
||||
handleCopyUrl(pasteText.join('\n'))
|
||||
trayWindow.webContents.send('dragFiles', imgs)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// toggleWindow()
|
||||
} else if (process.platform === 'linux') {
|
||||
// click事件在Ubuntu上无法触发,Unity不支持(在Mac和Windows上可以触发)
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import uploader from 'apis/app/uploader'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { T } from '~/i18n/index'
|
||||
import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
import { Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { IPicGo } from 'piclist'
|
||||
|
||||
import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
import { IFileWithPath, ImgInfo, IStringKeyMap, IUploadOption } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { T } from '~/i18n/index'
|
||||
import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common'
|
||||
import { changeCurrentUploader } from '~/utils/handleUploaderConfig'
|
||||
import { IPicGo } from 'piclist'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {
|
||||
const useBuiltinClipboard =
|
||||
@@ -213,8 +211,8 @@ export const handleSecondaryUpload = async (
|
||||
trayWindow?.webContents?.send('uploadFiles', secondImgs)
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < secondImgs.length; i++) {
|
||||
await GalleryDB.getInstance().insert(secondImgs[i])
|
||||
for (const secondImgsItem of secondImgs) {
|
||||
await GalleryDB.getInstance().insert(secondImgsItem)
|
||||
}
|
||||
if (uploadType === 'tray') {
|
||||
trayWindow?.webContents?.send('dragFiles', secondImgs)
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { BrowserWindow, clipboard, ipcMain, Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { IPicGo } from 'piclist'
|
||||
import util from 'util'
|
||||
import writeFile from 'write-file-atomic'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import path from 'node:path'
|
||||
import util from 'node:util'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
import { showNotification, getClipboardFilePath, calcDurationRange } from '~/utils/common'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import dayjs from 'dayjs'
|
||||
import { BrowserWindow, clipboard, ipcMain, IpcMainEvent, Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { IPicGo } from 'piclist'
|
||||
import writeFile from 'write-file-atomic'
|
||||
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME, TALKING_DATA_EVENT } from '#/events/constants'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
import { IAnalyticsData, ImgInfo, ITalkingDataOptions, IUploadOption } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
import { T } from '~/i18n'
|
||||
import { calcDurationRange, getClipboardFilePath, showNotification } from '~/utils/common'
|
||||
|
||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => {
|
||||
return new Promise(resolve => {
|
||||
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: Event, newName: string) => {
|
||||
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: IpcMainEvent, newName: string) => {
|
||||
resolve(newName)
|
||||
window.close()
|
||||
})
|
||||
@@ -52,12 +51,12 @@ const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) =>
|
||||
class Uploader {
|
||||
private webContents: WebContents | null = null
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.init()
|
||||
}
|
||||
|
||||
init() {
|
||||
picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: Electron.NotificationConstructorOptions | undefined) => {
|
||||
init () {
|
||||
picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: any) => {
|
||||
new Notification(message).show()
|
||||
})
|
||||
|
||||
@@ -88,7 +87,7 @@ class Uploader {
|
||||
: item.fileName
|
||||
if (rename) {
|
||||
const window = windowManager.create(IWindowList.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...')
|
||||
@@ -108,12 +107,12 @@ class Uploader {
|
||||
})
|
||||
}
|
||||
|
||||
setWebContents(webContents: WebContents) {
|
||||
setWebContents (webContents: WebContents) {
|
||||
this.webContents = webContents
|
||||
return this
|
||||
}
|
||||
|
||||
private async getClipboardImagePath(): Promise<string | false> {
|
||||
private async getClipboardImagePath (): Promise<string | false> {
|
||||
const imgPath = getClipboardFilePath()
|
||||
if (imgPath) return imgPath
|
||||
|
||||
@@ -131,7 +130,7 @@ class Uploader {
|
||||
/**
|
||||
* use electron's clipboard image to upload
|
||||
*/
|
||||
async uploadWithBuildInClipboard(): Promise<ImgInfo[] | false> {
|
||||
async uploadWithBuildInClipboard (): Promise<ImgInfo[] | false> {
|
||||
let imgPath: string | false = false
|
||||
try {
|
||||
imgPath = await this.getClipboardImagePath()
|
||||
@@ -147,7 +146,7 @@ class Uploader {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadWithBuildInClipboardReturnCtx(img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
||||
async uploadWithBuildInClipboardReturnCtx (img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
||||
let imgPath: string | false = false
|
||||
try {
|
||||
imgPath = await this.getClipboardImagePath()
|
||||
@@ -163,7 +162,7 @@ class Uploader {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadReturnCtx(img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
||||
async uploadReturnCtx (img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const ctx = await picgo.uploadReturnCtx(img, skipProcess)
|
||||
@@ -198,7 +197,7 @@ class Uploader {
|
||||
}
|
||||
}
|
||||
|
||||
async upload(img?: IUploadOption): Promise<ImgInfo[] | false> {
|
||||
async upload (img?: IUploadOption): Promise<ImgInfo[] | false> {
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const output = await picgo.upload(img)
|
||||
|
||||
@@ -2,25 +2,27 @@ 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`
|
||||
: 'picgo://./index.html#documents'
|
||||
? 'http://localhost:3000#documents'
|
||||
: 'index.html#documents'
|
||||
|
||||
export const MINI_WINDOW_URL = isDevelopment
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#mini-page`
|
||||
: 'picgo://./index.html#mini-page'
|
||||
? 'http://localhost:3000#mini-page'
|
||||
: 'index.html#mini-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'
|
||||
? 'http://localhost:3000#rename-page'
|
||||
: 'index.html#rename-page'
|
||||
|
||||
export const SETTING_WINDOW_URL = isDevelopment
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#main-page/upload`
|
||||
: 'picgo://./index.html#main-page/upload'
|
||||
? 'http://localhost:3000#main-page/upload'
|
||||
: '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 ? 'http://localhost:3000' : 'index.html'
|
||||
|
||||
console.log(TRAY_WINDOW_URL)
|
||||
|
||||
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'
|
||||
? 'http://localhost:3000#toolbox-page'
|
||||
: 'index.html#toolbox-page'
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import { CREATE_APP_MENU } from '@core/bus/constants'
|
||||
import db from '@core/datastore'
|
||||
import { app } from 'electron'
|
||||
|
||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||
import { IWindowListItem } from '#/types/electron'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { IBrowserWindowOptions } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import {
|
||||
MANUAL_WINDOW_URL,
|
||||
MINI_WINDOW_URL,
|
||||
RENAME_WINDOW_URL,
|
||||
SETTING_WINDOW_URL,
|
||||
TRAY_WINDOW_URL,
|
||||
TOOLBOX_WINDOW_URL
|
||||
TOOLBOX_WINDOW_URL,
|
||||
TRAY_WINDOW_URL
|
||||
} from './constants'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import { CREATE_APP_MENU } from '@core/bus/constants'
|
||||
import db from '@core/datastore'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
|
||||
const handleWindowParams = (windowURL: string) => windowURL
|
||||
@@ -33,6 +35,7 @@ const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||
height: mainWindowHeight || 800
|
||||
}
|
||||
}
|
||||
const preloadPath = fileURLToPath(new URL('../preload/index.mjs', import.meta.url))
|
||||
|
||||
const { width: defaultWindowWidth, height: defaultWindowHeight } = getDefaultWindowSizes()
|
||||
|
||||
@@ -46,8 +49,10 @@ const trayWindowOptions = {
|
||||
transparent: true,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
sandbox: false,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
backgroundThrottling: false,
|
||||
webSecurity: false
|
||||
@@ -66,10 +71,12 @@ const manualWindowOptions = {
|
||||
vibrancy: 'ultra-dark',
|
||||
transparent: false,
|
||||
webPreferences: {
|
||||
sandbox: false,
|
||||
webviewTag: true,
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false
|
||||
}
|
||||
@@ -88,10 +95,12 @@ const settingWindowOptions = {
|
||||
transparent: true,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
sandbox: false,
|
||||
webviewTag: true,
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false
|
||||
}
|
||||
@@ -102,7 +111,7 @@ if (process.platform !== 'darwin') {
|
||||
settingWindowOptions.frame = false
|
||||
settingWindowOptions.backgroundColor = '#3f3c37'
|
||||
settingWindowOptions.transparent = false
|
||||
settingWindowOptions.icon = `${__static}/logo.png`
|
||||
settingWindowOptions.icon = '.resources/logo.png'
|
||||
}
|
||||
|
||||
const miniWindowOptions = {
|
||||
@@ -114,11 +123,13 @@ const miniWindowOptions = {
|
||||
skipTaskbar: true,
|
||||
resizable: false,
|
||||
transparent: process.platform !== 'linux',
|
||||
icon: `${__static}/logo.png`,
|
||||
icon: './resources/logo.png',
|
||||
webPreferences: {
|
||||
sandbox: false,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
nodeIntegrationInWorker: true
|
||||
}
|
||||
} as IBrowserWindowOptions
|
||||
@@ -135,8 +146,10 @@ const renameWindowOptions = {
|
||||
resizable: false,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
sandbox: false,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
backgroundThrottling: false
|
||||
}
|
||||
@@ -159,11 +172,13 @@ const toolboxWindowOptions = {
|
||||
resizable: false,
|
||||
title: `PicList ${T('TOOLBOX')}`,
|
||||
vibrancy: 'ultra-dark',
|
||||
icon: `${__static}/logo.png`,
|
||||
icon: './resources/logo.png',
|
||||
webPreferences: {
|
||||
sandbox: false,
|
||||
backgroundThrottling: false,
|
||||
nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
|
||||
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
|
||||
preload: preloadPath,
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
webSecurity: false
|
||||
}
|
||||
@@ -179,7 +194,7 @@ windowList.set(IWindowList.TRAY_WINDOW, {
|
||||
isValid: process.platform !== 'linux',
|
||||
multiple: false,
|
||||
options: () => trayWindowOptions,
|
||||
callback(window) {
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(TRAY_WINDOW_URL))
|
||||
window.on('blur', () => {
|
||||
window.hide()
|
||||
@@ -191,7 +206,7 @@ windowList.set(IWindowList.MANUAL_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: false,
|
||||
options: () => manualWindowOptions,
|
||||
callback(window) {
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(MANUAL_WINDOW_URL))
|
||||
window.focus()
|
||||
}
|
||||
@@ -201,8 +216,9 @@ windowList.set(IWindowList.SETTING_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: false,
|
||||
options: () => settingWindowOptions,
|
||||
callback(window, windowManager) {
|
||||
callback (window, windowManager) {
|
||||
window.loadURL(handleWindowParams(SETTING_WINDOW_URL))
|
||||
window.webContents.openDevTools({ mode: 'detach' })
|
||||
window.on('closed', () => {
|
||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||
if (process.platform === 'linux') {
|
||||
@@ -220,7 +236,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
|
||||
isValid: process.platform !== 'darwin',
|
||||
multiple: false,
|
||||
options: () => miniWindowOptions,
|
||||
callback(window) {
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(MINI_WINDOW_URL))
|
||||
}
|
||||
})
|
||||
@@ -229,7 +245,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: true,
|
||||
options: () => renameWindowOptions,
|
||||
async callback(window, windowManager) {
|
||||
async callback (window, windowManager) {
|
||||
window.loadURL(handleWindowParams(RENAME_WINDOW_URL))
|
||||
const currentWindow = windowManager.getAvailableWindow(true)
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
@@ -245,7 +261,7 @@ windowList.set(IWindowList.TOOLBOX_WINDOW, {
|
||||
isValid: true,
|
||||
multiple: false,
|
||||
options: () => toolboxWindowOptions,
|
||||
async callback(window, windowManager) {
|
||||
async callback (window, windowManager) {
|
||||
window.loadURL(TOOLBOX_WINDOW_URL)
|
||||
const currentWindow = windowManager.getAvailableWindow(true)
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import windowList from 'apis/app/window/windowList'
|
||||
import { BrowserWindow } from 'electron'
|
||||
|
||||
import windowList from 'apis/app/window/windowList'
|
||||
import { IWindowListItem, IWindowManager } from '#/types/electron'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
|
||||
class WindowManager implements IWindowManager {
|
||||
#windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||
#windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||
|
||||
create(name: IWindowList) {
|
||||
create (name: IWindowList) {
|
||||
const windowConfig: IWindowListItem = windowList.get(name)!
|
||||
if (!windowConfig.isValid) return null
|
||||
|
||||
@@ -29,14 +30,14 @@ class WindowManager implements IWindowManager {
|
||||
return window
|
||||
}
|
||||
|
||||
get(name: IWindowList) {
|
||||
get (name: IWindowList) {
|
||||
if (this.has(name)) {
|
||||
return this.#windowMap.get(name)!
|
||||
}
|
||||
return this.create(name)
|
||||
}
|
||||
|
||||
has(name: IWindowList) {
|
||||
has (name: IWindowList) {
|
||||
return this.#windowMap.has(name)
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ class WindowManager implements IWindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableWindow(isSkipMiniWindow = false) {
|
||||
getAvailableWindow (isSkipMiniWindow = false) {
|
||||
const miniWindow = this.#windowMap.get(IWindowList.MINI_WINDOW)
|
||||
if (miniWindow && miniWindow.isVisible() && !isSkipMiniWindow) {
|
||||
return miniWindow
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import bus from '@core/bus/index'
|
||||
import {
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE,
|
||||
@@ -9,6 +8,9 @@ import {
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE
|
||||
} from '@core/bus/constants'
|
||||
import bus from '@core/bus/index'
|
||||
|
||||
import { IFileWithPath } from '#/types/types'
|
||||
|
||||
export const uploadWithClipboardFiles = (): Promise<{
|
||||
success: boolean
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { EventEmitter } from 'events'
|
||||
import { EventEmitter } from 'node:events'
|
||||
|
||||
const bus = new EventEmitter()
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import dayjs from 'dayjs'
|
||||
import path from 'path'
|
||||
import writeFile from 'write-file-atomic'
|
||||
import path from 'node:path'
|
||||
|
||||
import { getLogger } from '@core/utils/localLogger'
|
||||
import dayjs from 'dayjs'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import writeFile from 'write-file-atomic'
|
||||
|
||||
import { notificationList } from '#/utils/notification'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
@@ -22,10 +23,7 @@ const errorMsg = {
|
||||
brokenButBackup: T('TIPS_PICGO_CONFIG_FILE_BROKEN_WITH_BACKUP')
|
||||
}
|
||||
|
||||
/** ensure notification list */
|
||||
if (!global.notificationList) global.notificationList = []
|
||||
|
||||
function dbChecker() {
|
||||
function dbChecker () {
|
||||
if (process.type !== 'renderer') {
|
||||
// db save bak
|
||||
try {
|
||||
@@ -63,16 +61,16 @@ function dbChecker() {
|
||||
optionsTpl.body = `${errorMsg.brokenButBackup}\n${T('TIPS_PICGO_BACKUP_FILE_VERSION', {
|
||||
v: dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')
|
||||
})}`
|
||||
global.notificationList?.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
} catch (e) {
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList?.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
}
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList?.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
writeFile.sync(configFileBackupPath, configFile, { encoding: 'utf-8' })
|
||||
@@ -82,7 +80,7 @@ function dbChecker() {
|
||||
/**
|
||||
* Get config path
|
||||
*/
|
||||
function dbPathChecker(): string {
|
||||
function dbPathChecker (): string {
|
||||
if (_configFilePath) {
|
||||
return _configFilePath
|
||||
}
|
||||
@@ -113,7 +111,7 @@ function dbPathChecker(): string {
|
||||
title: T('TIPS_NOTICE'),
|
||||
body: T('TIPS_CUSTOM_CONFIG_FILE_PATH_ERROR')
|
||||
}
|
||||
global.notificationList?.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
hasCheckPath = true
|
||||
}
|
||||
logger('error', e)
|
||||
@@ -122,11 +120,11 @@ function dbPathChecker(): string {
|
||||
}
|
||||
}
|
||||
|
||||
function dbPathDir() {
|
||||
function dbPathDir () {
|
||||
return path.dirname(dbPathChecker())
|
||||
}
|
||||
|
||||
function getGalleryDBPath(): {
|
||||
function getGalleryDBPath (): {
|
||||
dbPath: string
|
||||
dbBackupPath: string
|
||||
} {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import fs from 'fs-extra'
|
||||
import { DBStore, JSONStore } from '@picgo/store'
|
||||
|
||||
import { dbPathChecker, dbPathDir, getGalleryDBPath } from '@core/datastore/dbChecker'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { IJSON } from '@picgo/store/dist/types'
|
||||
import { DBStore, JSONStore } from '@piclist/store'
|
||||
import fs from 'fs-extra'
|
||||
import { IConfig } from 'piclist'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { T } from '~/i18n'
|
||||
interface IJSON {
|
||||
[propsName: string]: string | number | IJSON
|
||||
}
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
if (!fs.pathExistsSync(STORE_PATH)) {
|
||||
@@ -19,7 +19,7 @@ export const DB_PATH: string = getGalleryDBPath().dbPath
|
||||
class ConfigStore {
|
||||
#db: JSONStore
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.#db = new JSONStore(CONFIG_PATH)
|
||||
|
||||
if (!this.#db.has('picBed')) {
|
||||
@@ -43,11 +43,11 @@ class ConfigStore {
|
||||
this.read()
|
||||
}
|
||||
|
||||
read(flush?: boolean): IJSON {
|
||||
read (flush?: boolean): IJSON {
|
||||
return this.#db.read(flush)
|
||||
}
|
||||
|
||||
getSingle(key = ''): any {
|
||||
getSingle (key = ''): any {
|
||||
if (key === '') {
|
||||
return this.#db.read(true)
|
||||
}
|
||||
@@ -55,43 +55,43 @@ class ConfigStore {
|
||||
return this.#db.get(key)
|
||||
}
|
||||
|
||||
get(key: string): any
|
||||
get(key: string[]): any[]
|
||||
get(key: string | string[] = ''): any {
|
||||
get (key: string): any
|
||||
get (key: string[]): any[]
|
||||
get (key: string | string[] = ''): any {
|
||||
if (Array.isArray(key)) {
|
||||
return key.map(k => this.getSingle(k))
|
||||
}
|
||||
return this.getSingle(key)
|
||||
}
|
||||
|
||||
set(key: string, value: any): void {
|
||||
set (key: string, value: any): void {
|
||||
this.read(true)
|
||||
return this.#db.set(key, value)
|
||||
}
|
||||
|
||||
has(key: string) {
|
||||
has (key: string) {
|
||||
this.read(true)
|
||||
return this.#db.has(key)
|
||||
}
|
||||
|
||||
unset(key: string, value: any): boolean {
|
||||
unset (key: string, value: any): boolean {
|
||||
this.read(true)
|
||||
return this.#db.unset(key, value)
|
||||
}
|
||||
|
||||
saveConfig(config: Partial<IConfig>): void {
|
||||
saveConfig (config: Partial<IConfig>): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.set(name, config[name])
|
||||
})
|
||||
}
|
||||
|
||||
removeConfig(config: IConfig): void {
|
||||
removeConfig (config: IConfig): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.unset(name, config[name])
|
||||
})
|
||||
}
|
||||
|
||||
getConfigPath() {
|
||||
getConfigPath () {
|
||||
return CONFIG_PATH
|
||||
}
|
||||
}
|
||||
@@ -103,11 +103,11 @@ export default db
|
||||
// v2.3.0 add gallery db
|
||||
class GalleryDB {
|
||||
static #instance: DBStore
|
||||
private constructor() {
|
||||
private constructor () {
|
||||
console.log('init gallery db')
|
||||
}
|
||||
|
||||
static getInstance(): DBStore {
|
||||
static getInstance (): DBStore {
|
||||
if (!GalleryDB.#instance) {
|
||||
GalleryDB.#instance = new DBStore(DB_PATH, 'gallery')
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import debounce from 'lodash/debounce'
|
||||
import { PicGo } from 'piclist'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import { dbChecker, dbPathChecker } from '@core/datastore/dbChecker'
|
||||
|
||||
import { debounce } from 'lodash-es'
|
||||
import { PicGo } from 'piclist'
|
||||
import pkg from 'root/package.json'
|
||||
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
|
||||
const CONFIG_PATH = dbPathChecker()
|
||||
|
||||
dbChecker()
|
||||
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const picgo = await PicGo.create(CONFIG_PATH)
|
||||
|
||||
picgo.saveConfig({
|
||||
debug: true,
|
||||
PICGO_ENV: 'GUI'
|
||||
})
|
||||
|
||||
global.PICGO_GUI_VERSION = pkg.version
|
||||
picgo.GUI_VERSION = global.PICGO_GUI_VERSION
|
||||
picgo.GUI_VERSION = pkg.version
|
||||
|
||||
const originPicGoSaveConfig = picgo.saveConfig.bind(picgo)
|
||||
|
||||
function flushDB() {
|
||||
function flushDB () {
|
||||
db.read(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import fs from 'fs-extra'
|
||||
import util from 'node:util'
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
import util from 'util'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { ILogArgvTypeWithError } from '#/types/types'
|
||||
|
||||
const MB = 1024 * 1024
|
||||
const DEFAULT_LOG_FILE_SIZE_LIMIT = 10 * MB
|
||||
|
||||
49
src/main/apis/delete/alist.ts
Normal file
49
src/main/apis/delete/alist.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: {
|
||||
version: string
|
||||
url: string
|
||||
uploadPath: string
|
||||
token: string
|
||||
}
|
||||
}
|
||||
|
||||
export default class AlistApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
const { version, url, uploadPath, token } = config
|
||||
if (String(version) === '2') {
|
||||
deleteLog(fileName, 'Alist', false, 'Alist version 2 is not supported, deletion is skipped')
|
||||
return true
|
||||
}
|
||||
const result = await axios.request({
|
||||
method: 'post',
|
||||
url: `${url}/api/fs/remove`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token
|
||||
},
|
||||
data: {
|
||||
dir: path.join('/', uploadPath, path.dirname(fileName)),
|
||||
names: [path.basename(fileName)]
|
||||
}
|
||||
})
|
||||
if (result.data.code === 200) {
|
||||
deleteLog(fileName, 'Alist')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Alist', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Alist', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/main/apis/delete/alistplist.ts
Normal file
64
src/main/apis/delete/alistplist.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: {
|
||||
url: string
|
||||
username: string
|
||||
password: string
|
||||
uploadPath: string
|
||||
token: string
|
||||
}
|
||||
}
|
||||
|
||||
const getAListToken = async (url: string, username: string, password: string) => {
|
||||
const res = await axios.post(`${url}/api/auth/login`, {
|
||||
username,
|
||||
password
|
||||
})
|
||||
if (res.data.code === 200 && res.data.message === 'success') {
|
||||
return res.data.data.token
|
||||
}
|
||||
}
|
||||
|
||||
export default class AListplistApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
const { url, username, password, uploadPath } = config
|
||||
let token = config.token
|
||||
if (!token) {
|
||||
token = await getAListToken(url, username, password)
|
||||
}
|
||||
if (!url || !(token || (username && password))) {
|
||||
deleteFailedLog(fileName, 'Alist', 'No valid token or username/password provided')
|
||||
return false
|
||||
}
|
||||
const result = await axios.request({
|
||||
method: 'post',
|
||||
url: `${url}/api/fs/remove`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token
|
||||
},
|
||||
data: {
|
||||
dir: path.join('/', uploadPath, path.dirname(fileName)),
|
||||
names: [path.basename(fileName)]
|
||||
}
|
||||
})
|
||||
if (result.data.code === 200) {
|
||||
deleteLog(fileName, 'Alist')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Alist', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Alist', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/main/apis/delete/aliyun.ts
Normal file
33
src/main/apis/delete/aliyun.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import OSS from 'ali-oss'
|
||||
|
||||
import { IAliYunConfig, PartialKeys } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: PartialKeys<IAliYunConfig, 'path'>
|
||||
}
|
||||
|
||||
export default class AliyunApi {
|
||||
static #getKey (fileName: string, path?: string): string {
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` : fileName
|
||||
}
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
const client = new OSS({ ...config, region: config.area })
|
||||
const key = AliyunApi.#getKey(fileName, config.path)
|
||||
const result = await client.delete(key)
|
||||
if (result.res.status === 204) {
|
||||
deleteLog(fileName, 'Aliyun')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Aliyun', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Aliyun', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/main/apis/delete/allApi.ts
Normal file
46
src/main/apis/delete/allApi.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import AlistApi from '~/apis/delete/alist'
|
||||
import AlistplistApi from '~/apis/delete/alistplist'
|
||||
import AliyunApi from '~/apis/delete/aliyun'
|
||||
import AwsS3Api from '~/apis/delete/awss3'
|
||||
import DogeCloudApi from '~/apis/delete/dogecloud'
|
||||
import GithubApi from '~/apis/delete/github'
|
||||
import HuaweicloudApi from '~/apis/delete/huaweiyun'
|
||||
import ImgurApi from '~/apis/delete/imgur'
|
||||
import LocalApi from '~/apis/delete/local'
|
||||
import LskyplistApi from '~/apis/delete/lskyplist'
|
||||
import PiclistApi from '~/apis/delete/piclist'
|
||||
import QiniuApi from '~/apis/delete/qiniu'
|
||||
import SftpPlistApi from '~/apis/delete/sftpplist'
|
||||
import SmmsApi from '~/apis/delete/smms'
|
||||
import TcyunApi from '~/apis/delete/tcyun'
|
||||
import UpyunApi from '~/apis/delete/upyun'
|
||||
import WebdavApi from '~/apis/delete/webdav'
|
||||
|
||||
const apiMap: IStringKeyMap = {
|
||||
alist: AlistApi,
|
||||
alistplist: AlistplistApi,
|
||||
aliyun: AliyunApi,
|
||||
'aws-s3': AwsS3Api,
|
||||
'aws-s3-plist': AwsS3Api,
|
||||
dogecloud: DogeCloudApi,
|
||||
github: GithubApi,
|
||||
'huaweicloud-uploader': HuaweicloudApi,
|
||||
imgur: ImgurApi,
|
||||
local: LocalApi,
|
||||
lskyplist: LskyplistApi,
|
||||
piclist: PiclistApi,
|
||||
qiniu: QiniuApi,
|
||||
sftpplist: SftpPlistApi,
|
||||
smms: SmmsApi,
|
||||
tcyun: TcyunApi,
|
||||
upyun: UpyunApi,
|
||||
webdavplist: WebdavApi
|
||||
}
|
||||
|
||||
export default class ALLApi {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const api = apiMap[configMap.type]
|
||||
return api ? await api.delete(configMap) : false
|
||||
}
|
||||
}
|
||||
15
src/main/apis/delete/awss3.ts
Normal file
15
src/main/apis/delete/awss3.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { getRawData } from '@/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { removeFileFromS3InMain } from '~/utils/deleteFunc'
|
||||
import { deleteFailedLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class AwsS3Api {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return await removeFileFromS3InMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'AWS S3', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/main/apis/delete/dogecloud.ts
Normal file
15
src/main/apis/delete/dogecloud.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { getRawData } from '@/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { removeFileFromDogeInMain } from '~/utils/deleteFunc'
|
||||
import { deleteFailedLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class AwsS3Api {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return await removeFileFromDogeInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'DogeCloud', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/main/apis/delete/github.ts
Normal file
53
src/main/apis/delete/github.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Octokit } from '@octokit/rest'
|
||||
|
||||
import { IGitHubConfig, PartialKeys } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
hash: string
|
||||
config: PartialKeys<IGitHubConfig, 'path'>
|
||||
}
|
||||
|
||||
export default class GithubApi {
|
||||
static #createOctokit (token: string) {
|
||||
return new Octokit({
|
||||
auth: token
|
||||
})
|
||||
}
|
||||
|
||||
static #createKey (path: string | undefined, fileName: string): string {
|
||||
const formatedFileName = fileName.replace(/%2F/g, '/')
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` : formatedFileName
|
||||
}
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
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)
|
||||
try {
|
||||
const { status } = await octokit.rest.repos.deleteFile({
|
||||
owner,
|
||||
repo: repoName,
|
||||
path: key,
|
||||
message: `delete ${fileName} by PicList`,
|
||||
sha: hash,
|
||||
branch
|
||||
})
|
||||
if (status === 200) {
|
||||
deleteLog(fileName, 'GitHub')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'GitHub', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'GitHub', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/main/apis/delete/huaweiyun.ts
Normal file
15
src/main/apis/delete/huaweiyun.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { getRawData } from '@/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { removeFileFromHuaweiInMain } from '~/utils/deleteFunc'
|
||||
import { deleteFailedLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class HuaweicloudApi {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return await removeFileFromHuaweiInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'HuaweiCloud', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/apis/delete/imgur.ts
Normal file
44
src/main/apis/delete/imgur.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
|
||||
import { IImgurConfig } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
config?: Partial<IImgurConfig>
|
||||
hash?: string
|
||||
}
|
||||
|
||||
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
|
||||
let Authorization: string, apiUrl: string
|
||||
|
||||
if (username && accessToken) {
|
||||
Authorization = `Bearer ${accessToken}`
|
||||
apiUrl = `${ImgurApi.#baseUrl}/account/${username}/image/${hash}`
|
||||
} else if (clientId) {
|
||||
Authorization = `Client-ID ${clientId}`
|
||||
apiUrl = `${ImgurApi.#baseUrl}/image/${hash}`
|
||||
} else {
|
||||
deleteLog(hash, 'Imgur', false, 'No credentials found')
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const response: AxiosResponse = await axios.delete(apiUrl, {
|
||||
headers: { Authorization },
|
||||
timeout: 30000
|
||||
})
|
||||
if (response.status === 200) {
|
||||
deleteLog(hash, 'Imgur')
|
||||
return true
|
||||
}
|
||||
deleteLog(hash, 'Imgur', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(hash, 'Imgur', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/main/apis/delete/local.ts
Normal file
26
src/main/apis/delete/local.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
hash: string
|
||||
}
|
||||
|
||||
export default class LocalApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { hash } = configMap
|
||||
if (!hash) {
|
||||
deleteLog(hash, 'Local', false, 'Local.delete: invalid params')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.remove(hash)
|
||||
deleteLog(hash, 'Local')
|
||||
return true
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(hash, 'Local', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/main/apis/delete/lskyplist.ts
Normal file
47
src/main/apis/delete/lskyplist.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import https from 'node:https'
|
||||
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class LskyplistApi {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { hash, config } = configMap
|
||||
if (!hash || !config || !config.token) {
|
||||
deleteLog(hash, 'Lskyplist', false, 'LskyplistApi.delete: invalid params')
|
||||
return false
|
||||
}
|
||||
|
||||
const { host, token, version } = config
|
||||
if (version !== 'V2') {
|
||||
deleteLog(hash, 'Lskyplist', false, 'LskyplistApi.delete: invalid version')
|
||||
return false
|
||||
}
|
||||
|
||||
const v2Headers = {
|
||||
Accept: 'application/json',
|
||||
Authorization: token || undefined
|
||||
}
|
||||
|
||||
const requestAgent = new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
try {
|
||||
const response: AxiosResponse = await axios.delete(`${host}/api/v1/images/${hash}`, {
|
||||
headers: v2Headers,
|
||||
timeout: 30000,
|
||||
httpsAgent: requestAgent
|
||||
})
|
||||
if (response.status === 200 && response.data.status === true) {
|
||||
deleteLog(hash, 'Lskyplist')
|
||||
return true
|
||||
}
|
||||
deleteLog(hash, 'Lskyplist', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(hash, 'Lskyplist', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/main/apis/delete/piclist.ts
Normal file
34
src/main/apis/delete/piclist.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class PiclistApi {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { config, fullResult } = configMap
|
||||
const { host, port } = config
|
||||
if (!fullResult) return true
|
||||
|
||||
if (!host) {
|
||||
deleteLog(fullResult, 'Piclist', false, 'PiclistApi.delete: invalid params')
|
||||
return false
|
||||
}
|
||||
|
||||
const url = `http://${host || '127.0.0.1'}:${port || 36677}/delete`
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await axios.post(url, {
|
||||
list: [fullResult]
|
||||
})
|
||||
if (response.status === 200 && response.data?.success) {
|
||||
deleteLog(fullResult, 'Piclist')
|
||||
return true
|
||||
}
|
||||
deleteLog(fullResult, 'Piclist', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fullResult, 'Piclist', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/apis/delete/qiniu.ts
Normal file
44
src/main/apis/delete/qiniu.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { IQiniuConfig, PartialKeys } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: PartialKeys<IQiniuConfig, 'path'>
|
||||
}
|
||||
|
||||
export default class QiniuApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
fileName,
|
||||
config: { accessKey, secretKey, bucket, path }
|
||||
} = configMap
|
||||
const mac = new window.node.qiniu.auth.digest.Mac(accessKey, secretKey)
|
||||
const qiniuConfig = new window.node.qiniu.conf.Config()
|
||||
try {
|
||||
const bucketManager = new window.node.qiniu.rs.BucketManager(mac, qiniuConfig)
|
||||
const formattedPath = path?.replace(/^\/+|\/+$/, '') || ''
|
||||
const key = path === '/' || !path ? fileName : `${formattedPath}/${fileName}`
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.delete(bucket, key, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve({
|
||||
respBody,
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
deleteLog(fileName, 'Qiniu')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Qiniu', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Qiniu', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/apis/delete/sftpplist.ts
Normal file
16
src/main/apis/delete/sftpplist.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { getRawData } from '@/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { removeFileFromSFTPInMain } from '~/utils/deleteFunc'
|
||||
import { deleteFailedLog } from '~/utils/deleteLog'
|
||||
|
||||
export default class SftpPlistApi {
|
||||
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
return await removeFileFromSFTPInMain(getRawData(config), fileName)
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'SFTP', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/main/apis/delete/smms.ts
Normal file
45
src/main/apis/delete/smms.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import axios, { AxiosResponse } from 'axios'
|
||||
|
||||
import { ISMMSConfig } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
hash?: string
|
||||
config?: Partial<ISMMSConfig>
|
||||
}
|
||||
|
||||
export default class SmmsApi {
|
||||
static readonly #baseUrl = 'https://smms.app/api/v2'
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { hash, config } = configMap
|
||||
if (!hash || !config || !config.token) {
|
||||
deleteLog(hash, 'Smms', false, 'SmmsApi.delete: invalid params')
|
||||
return false
|
||||
}
|
||||
|
||||
const { token } = config
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await axios.get(`${SmmsApi.#baseUrl}/delete/${hash}`, {
|
||||
headers: {
|
||||
Authorization: token
|
||||
},
|
||||
params: {
|
||||
hash,
|
||||
format: 'json'
|
||||
},
|
||||
timeout: 30000
|
||||
})
|
||||
if (response.status === 200) {
|
||||
deleteLog(hash, 'Smms')
|
||||
return true
|
||||
}
|
||||
deleteLog(hash, 'Smms', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(hash, 'Smms', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/main/apis/delete/tcyun.ts
Normal file
46
src/main/apis/delete/tcyun.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import COS from 'cos-nodejs-sdk-v5'
|
||||
|
||||
import { ITcYunConfig, PartialKeys } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: PartialKeys<ITcYunConfig, 'path'>
|
||||
}
|
||||
export default class TcyunApi {
|
||||
static #createCOS (SecretId: string, SecretKey: string): COS {
|
||||
return new COS({
|
||||
SecretId,
|
||||
SecretKey
|
||||
})
|
||||
}
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
fileName,
|
||||
config: { secretId, secretKey, bucket, area, path }
|
||||
} = configMap
|
||||
try {
|
||||
const cos = TcyunApi.#createCOS(secretId, secretKey)
|
||||
let key
|
||||
if (path === '/' || !path) {
|
||||
key = `/${fileName}`
|
||||
} else {
|
||||
key = `/${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
}
|
||||
const result = await cos.deleteObject({
|
||||
Bucket: bucket,
|
||||
Region: area,
|
||||
Key: key
|
||||
})
|
||||
if (result.statusCode === 204) {
|
||||
deleteLog(fileName, 'Tcyun')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Tcyun', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Tcyun', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/main/apis/delete/upyun.ts
Normal file
38
src/main/apis/delete/upyun.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import Upyun from 'upyun'
|
||||
|
||||
import { IUpYunConfig, PartialKeys } from '#/types/types'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: PartialKeys<IUpYunConfig, 'path'>
|
||||
}
|
||||
|
||||
export default class UpyunApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
fileName,
|
||||
config: { bucket, operator, password, path }
|
||||
} = configMap
|
||||
try {
|
||||
const service = new Upyun.Service(bucket, operator, password)
|
||||
const client = new Upyun.Client(service)
|
||||
let key
|
||||
if (path === '/' || !path) {
|
||||
key = fileName
|
||||
} else {
|
||||
key = `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
}
|
||||
const result = await client.deleteFile(key)
|
||||
if (result) {
|
||||
deleteLog(fileName, 'Upyun')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Upyun', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Upyun', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/main/apis/delete/webdav.ts
Normal file
42
src/main/apis/delete/webdav.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { AuthType, createClient, WebDAVClientOptions } from 'webdav'
|
||||
|
||||
import { IWebdavPlistConfig, PartialKeys } from '#/types/types'
|
||||
import { formatEndpoint } from '#/utils/common'
|
||||
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: PartialKeys<IWebdavPlistConfig, 'path'>
|
||||
}
|
||||
|
||||
export default class WebdavApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
fileName,
|
||||
config: { host, username, password, path, sslEnabled, authType }
|
||||
} = configMap
|
||||
const endpoint = formatEndpoint(host, sslEnabled)
|
||||
const options: WebDAVClientOptions = {
|
||||
username,
|
||||
password
|
||||
}
|
||||
if (authType === 'digest') {
|
||||
options.authType = AuthType.Digest
|
||||
}
|
||||
const ctx = createClient(endpoint, options)
|
||||
let key
|
||||
if (path === '/' || !path) {
|
||||
key = fileName
|
||||
} else {
|
||||
key = `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
}
|
||||
try {
|
||||
await ctx.deleteFile(key)
|
||||
deleteLog(fileName, 'WebDAV')
|
||||
return true
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'WebDAV', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,38 @@
|
||||
import { BrowserWindow, dialog, ipcMain, Notification } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { DBStore } from '@picgo/store'
|
||||
|
||||
import { getWindowId, getSettingWindowId } from '@core/bus/apis'
|
||||
|
||||
import { getSettingWindowId, getWindowId } from '@core/bus/apis'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from '@core/datastore/dbChecker'
|
||||
|
||||
import { DBStore } from '@piclist/store'
|
||||
import uploader from 'apis/app/uploader'
|
||||
import { handleSecondaryUpload } from 'apis/app/uploader/apis'
|
||||
import { BrowserWindow, dialog, ipcMain, IpcMainEvent, MessageBoxOptions, Notification } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
import { SHOW_INPUT_BOX } from '#/events/constants'
|
||||
import { IPasteStyle } from '#/types/enum'
|
||||
import { IGuiApi, ImgInfo, IShowFileExplorerOption, IShowInputBoxOption, IShowMessageBoxOption, IShowMessageBoxResult, IShowNotificationOption, IUploadOption } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { T } from '~/i18n'
|
||||
import { handleCopyUrl } from '~/utils/common'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { SHOW_INPUT_BOX } from '#/events/constants'
|
||||
import { IPasteStyle } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { handleSecondaryUpload } from '../app/uploader/apis'
|
||||
|
||||
// Cross-process support may be required in the future
|
||||
class GuiApi implements IGuiApi {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
private static instance: GuiApi
|
||||
private windowId: number = -1
|
||||
private settingWindowId: number = -1
|
||||
private constructor() {
|
||||
private constructor () {
|
||||
console.log('init guiapi')
|
||||
}
|
||||
|
||||
static getInstance(): GuiApi {
|
||||
static getInstance (): GuiApi {
|
||||
if (!GuiApi.instance) {
|
||||
GuiApi.instance = new GuiApi()
|
||||
}
|
||||
return GuiApi.instance
|
||||
}
|
||||
|
||||
private async showSettingWindow() {
|
||||
private async showSettingWindow () {
|
||||
this.settingWindowId = await getSettingWindowId()
|
||||
const settingWindow = BrowserWindow.fromId(this.settingWindowId)
|
||||
if (settingWindow?.isVisible()) {
|
||||
@@ -50,11 +46,11 @@ class GuiApi implements IGuiApi {
|
||||
})
|
||||
}
|
||||
|
||||
private getWebcontentsByWindowId(id: number) {
|
||||
private getWebcontentsByWindowId (id: number) {
|
||||
return BrowserWindow.fromId(id)?.webContents
|
||||
}
|
||||
|
||||
async showInputBox(
|
||||
async showInputBox (
|
||||
options: IShowInputBoxOption = {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
@@ -63,19 +59,19 @@ class GuiApi implements IGuiApi {
|
||||
await this.showSettingWindow()
|
||||
this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
|
||||
return new Promise<string>(resolve => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (_: Event, value: string) => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (_: IpcMainEvent, value: string) => {
|
||||
resolve(value)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async showFileExplorer(options: IShowFileExplorerOption = {}) {
|
||||
async showFileExplorer (options: IShowFileExplorerOption = {}) {
|
||||
this.windowId = await getWindowId()
|
||||
const res = await dialog.showOpenDialog(BrowserWindow.fromId(this.windowId)!, options)
|
||||
return res.filePaths || []
|
||||
}
|
||||
|
||||
async upload(input: IUploadOption) {
|
||||
async upload (input: IUploadOption) {
|
||||
this.windowId = await getWindowId()
|
||||
const webContents = this.getWebcontentsByWindowId(this.windowId)
|
||||
const rawInput = cloneDeep(input)
|
||||
@@ -126,7 +122,7 @@ class GuiApi implements IGuiApi {
|
||||
return []
|
||||
}
|
||||
|
||||
showNotification(
|
||||
showNotification (
|
||||
options: IShowNotificationOption = {
|
||||
title: '',
|
||||
body: ''
|
||||
@@ -139,7 +135,7 @@ class GuiApi implements IGuiApi {
|
||||
notification.show()
|
||||
}
|
||||
|
||||
showMessageBox(
|
||||
showMessageBox (
|
||||
options: IShowMessageBoxOption = {
|
||||
title: '',
|
||||
message: '',
|
||||
@@ -147,12 +143,14 @@ class GuiApi implements IGuiApi {
|
||||
buttons: ['Yes', 'No']
|
||||
}
|
||||
) {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
this.windowId = await getWindowId()
|
||||
dialog.showMessageBox(BrowserWindow.fromId(this.windowId)!, options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
return new Promise<IShowMessageBoxResult>(resolve => {
|
||||
getWindowId().then(id => {
|
||||
this.windowId = id
|
||||
dialog.showMessageBox(BrowserWindow.fromId(id)!, options as MessageBoxOptions).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -161,7 +159,7 @@ class GuiApi implements IGuiApi {
|
||||
/**
|
||||
* get picgo config/data path
|
||||
*/
|
||||
async getConfigPath() {
|
||||
async getConfigPath () {
|
||||
const currentConfigPath = dbPathChecker()
|
||||
const galleryDBPath = getGalleryDBPath().dbPath
|
||||
return {
|
||||
@@ -171,12 +169,12 @@ class GuiApi implements IGuiApi {
|
||||
}
|
||||
}
|
||||
|
||||
get galleryDB(): DBStore {
|
||||
get galleryDB (): DBStore {
|
||||
return new Proxy<DBStore>(GalleryDB.getInstance(), {
|
||||
get(target, prop: keyof DBStore) {
|
||||
get (target, prop: keyof DBStore) {
|
||||
if (prop === 'overwrite') {
|
||||
return new Proxy(GalleryDB.getInstance().overwrite, {
|
||||
apply(target, ctx, args) {
|
||||
apply (target, ctx, args) {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi
|
||||
@@ -199,7 +197,7 @@ class GuiApi implements IGuiApi {
|
||||
}
|
||||
if (prop === 'removeById') {
|
||||
return new Proxy(GalleryDB.getInstance().removeById, {
|
||||
apply(target, ctx, args) {
|
||||
apply (target, ctx, args) {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import bus from '@core/bus'
|
||||
import {
|
||||
CREATE_APP_MENU,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
GET_SETTING_WINDOW_ID,
|
||||
GET_SETTING_WINDOW_ID_RESPONSE,
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE,
|
||||
GET_WINDOW_ID,
|
||||
GET_WINDOW_ID_REPONSE,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES,
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE,
|
||||
UPLOAD_WITH_FILES,
|
||||
UPLOAD_WITH_FILES_RESPONSE
|
||||
} from '@core/bus/constants'
|
||||
|
||||
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'
|
||||
import { IFileWithPath } from '#/types/types'
|
||||
|
||||
function initEventCenter() {
|
||||
function initEventCenter () {
|
||||
const eventList: any = {
|
||||
'picgo:upload': uploadClipboardFiles,
|
||||
[UPLOAD_WITH_CLIPBOARD_FILES]: busCallUploadClipboardFiles,
|
||||
@@ -31,31 +31,31 @@ function initEventCenter() {
|
||||
}
|
||||
}
|
||||
|
||||
async function busCallUploadClipboardFiles() {
|
||||
async function busCallUploadClipboardFiles () {
|
||||
const result = await uploadClipboardFiles()
|
||||
const imgUrl = result.url
|
||||
bus.emit(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, imgUrl)
|
||||
}
|
||||
|
||||
async function busCallUploadFiles(pathList: IFileWithPath[]) {
|
||||
async function busCallUploadFiles (pathList: IFileWithPath[]) {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
const result = await uploadChoosedFiles(win.webContents, pathList)
|
||||
const urls = result.map((item: any) => item.url)
|
||||
bus.emit(UPLOAD_WITH_FILES_RESPONSE, urls)
|
||||
}
|
||||
|
||||
function busCallGetWindowId() {
|
||||
function busCallGetWindowId () {
|
||||
const win = windowManager.getAvailableWindow()
|
||||
bus.emit(GET_WINDOW_ID_REPONSE, win.id)
|
||||
}
|
||||
|
||||
function busCallGetSettingWindowId() {
|
||||
function busCallGetSettingWindowId () {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
bus.emit(GET_SETTING_WINDOW_ID_RESPONSE, settingWindow.id)
|
||||
}
|
||||
|
||||
export default {
|
||||
listen() {
|
||||
listen () {
|
||||
initEventCenter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
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 windowManager from 'apis/app/window/windowManager'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import { handlePluginUninstall, handlePluginUpdate } from '~/events/rpc/routes/plugin/utils'
|
||||
import { T } from '~/i18n'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { setTrayToolTip } from '~/utils/common'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
import { changeCurrentUploader, changeSecondUploader } from '~/utils/handleUploaderConfig'
|
||||
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell } from 'electron'
|
||||
import { PicGo as PicGoCore } from 'piclist'
|
||||
import pkg from 'root/package.json'
|
||||
|
||||
import {
|
||||
PICGO_CONFIG_PLUGIN,
|
||||
@@ -23,9 +15,14 @@ import {
|
||||
SHOW_MAIN_PAGE_QRCODE
|
||||
} from '#/events/constants'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { IPicGoPlugin, IUploaderConfig } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
import pkg from 'root/package.json'
|
||||
import { handlePluginUninstall, handlePluginUpdate } from '~/events/rpc/routes/plugin/utils'
|
||||
import { T } from '~/i18n'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { setTrayToolTip } from '~/utils/common'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
import { changeCurrentUploader, changeSecondUploader } from '~/utils/handleUploaderConfig'
|
||||
import { openMainWindow } from '~/utils/windowHelper'
|
||||
|
||||
interface GuiMenuItem {
|
||||
@@ -37,7 +34,7 @@ const buildMiniPageMenu = () => {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
const template: (MenuItemConstructorOptions | MenuItem)[] = [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click: openMainWindow
|
||||
@@ -49,19 +46,19 @@ const buildMiniPageMenu = () => {
|
||||
},
|
||||
{
|
||||
label: T('UPLOAD_BY_CLIPBOARD'),
|
||||
click() {
|
||||
click () {
|
||||
uploadClipboardFiles()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click() {
|
||||
click () {
|
||||
BrowserWindow.getFocusedWindow()!.hide()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click() {
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
@@ -74,7 +71,7 @@ const buildMiniPageMenu = () => {
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click() {
|
||||
click () {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
ClipboardWatcher.stopListening()
|
||||
ClipboardWatcher.removeAllListeners()
|
||||
@@ -84,7 +81,7 @@ const buildMiniPageMenu = () => {
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
click () {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
@@ -101,7 +98,7 @@ const buildMainPageMenu = (win: BrowserWindow) => {
|
||||
const template = [
|
||||
{
|
||||
label: T('ABOUT'),
|
||||
click() {
|
||||
click () {
|
||||
dialog.showMessageBox({
|
||||
title: 'PicList',
|
||||
message: 'PicList',
|
||||
@@ -111,31 +108,31 @@ const buildMainPageMenu = (win: BrowserWindow) => {
|
||||
},
|
||||
{
|
||||
label: T('SHOW_PICBED_QRCODE'),
|
||||
click() {
|
||||
click () {
|
||||
win?.webContents?.send(SHOW_MAIN_PAGE_QRCODE)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('OPEN_TOOLBOX'),
|
||||
click() {
|
||||
click () {
|
||||
const window = windowManager.create(IWindowList.TOOLBOX_WINDOW)
|
||||
window?.show()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('SHOW_DEVTOOLS'),
|
||||
click() {
|
||||
click () {
|
||||
win?.webContents?.openDevTools({ mode: 'detach' })
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('FEEDBACK'),
|
||||
click() {
|
||||
click () {
|
||||
const url = 'https://github.com/Kuingsmile/PicList/issues'
|
||||
shell.openExternal(url)
|
||||
}
|
||||
}
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
] as (MenuItemConstructorOptions | MenuItem)[]
|
||||
return Menu.buildFromTemplate(template)
|
||||
}
|
||||
|
||||
@@ -179,10 +176,10 @@ const buildSecondPicBedMenu = () => {
|
||||
: undefined,
|
||||
click: !hasSubmenu
|
||||
? function () {
|
||||
picgo.saveConfig({
|
||||
[configPaths.picBed.secondUploader]: item.type
|
||||
})
|
||||
}
|
||||
picgo.saveConfig({
|
||||
[configPaths.picBed.secondUploader]: item.type
|
||||
})
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
@@ -236,15 +233,15 @@ const buildPicBedListMenu = () => {
|
||||
: undefined,
|
||||
click: !hasSubmenu
|
||||
? function () {
|
||||
picgo.saveConfig({
|
||||
[configPaths.picBed.current]: item.type,
|
||||
[configPaths.picBed.uploader]: item.type
|
||||
})
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
}
|
||||
setTrayToolTip(item.type)
|
||||
picgo.saveConfig({
|
||||
[configPaths.picBed.current]: item.type,
|
||||
[configPaths.picBed.uploader]: item.type
|
||||
})
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||
}
|
||||
setTrayToolTip(item.type)
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
@@ -281,7 +278,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
{
|
||||
label: T('ENABLE_PLUGIN'),
|
||||
enabled: !plugin.enabled,
|
||||
click() {
|
||||
click () {
|
||||
picgo.saveConfig({
|
||||
[`picgoPlugins.${plugin.fullName}`]: true
|
||||
})
|
||||
@@ -292,7 +289,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
{
|
||||
label: T('DISABLE_PLUGIN'),
|
||||
enabled: plugin.enabled,
|
||||
click() {
|
||||
click () {
|
||||
picgo.saveConfig({
|
||||
[`picgoPlugins.${plugin.fullName}`]: false
|
||||
})
|
||||
@@ -310,7 +307,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
},
|
||||
{
|
||||
label: T('UNINSTALL_PLUGIN'),
|
||||
click() {
|
||||
click () {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUninstall(plugin.fullName)
|
||||
@@ -318,20 +315,20 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
},
|
||||
{
|
||||
label: T('UPDATE_PLUGIN'),
|
||||
click() {
|
||||
click () {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUpdate(plugin.fullName)
|
||||
}
|
||||
}
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
] as (MenuItemConstructorOptions | MenuItem)[]
|
||||
for (const i in plugin.config) {
|
||||
if (plugin.config[i].config.length > 0) {
|
||||
const obj = {
|
||||
label: T('CONFIG_THING', {
|
||||
c: `${i} - ${plugin.config[i].fullName || plugin.config[i].name}`
|
||||
}),
|
||||
click() {
|
||||
click () {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const currentType = i
|
||||
const configName = plugin.config[i].fullName || plugin.config[i].name
|
||||
@@ -349,7 +346,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
const pluginTransformer = plugin.config.transformer.name
|
||||
const obj = {
|
||||
label: `${currentTransformer === pluginTransformer ? T('DISABLE') : T('ENABLE')}transformer - ${plugin.config.transformer.name}`,
|
||||
click() {
|
||||
click () {
|
||||
const transformer = plugin.config.transformer.name
|
||||
const currentTransformer = picgo.getConfig<string>(configPaths.picBed.transformer) || 'path'
|
||||
if (currentTransformer === transformer) {
|
||||
@@ -374,8 +371,8 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
for (const i of plugin.guiMenu) {
|
||||
menu.push({
|
||||
label: i.label,
|
||||
click() {
|
||||
const picgPlugin = picgo.pluginLoader.getPlugin(plugin.fullName)
|
||||
async click () {
|
||||
const picgPlugin = await picgo.pluginLoader.getPlugin(plugin.fullName)
|
||||
if (picgPlugin?.guiMenu?.(picgo)?.length) {
|
||||
const menu: GuiMenuItem[] = picgPlugin.guiMenu(picgo)
|
||||
menu.forEach(item => {
|
||||
@@ -392,4 +389,4 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
return Menu.buildFromTemplate(menu)
|
||||
}
|
||||
|
||||
export { buildMiniPageMenu, buildMainPageMenu, buildPicBedListMenu, buildPluginPageMenu, buildSecondPicBedMenu }
|
||||
export { buildMainPageMenu, buildMiniPageMenu, buildPicBedListMenu, buildPluginPageMenu, buildSecondPicBedMenu }
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import logger from '@core/picgo/logger'
|
||||
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from 'electron'
|
||||
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import { RPC_ACTIONS, RPC_ACTIONS_INVOKE } from '#/events/constants'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IRPCRoutes, IRPCServer } from '#/types/rpc'
|
||||
import { galleryRouter } from '~/events/rpc/routes/gallery'
|
||||
import { manageRouter } from '~/events/rpc/routes/manage'
|
||||
import { picbedRouter } from '~/events/rpc/routes/picbed'
|
||||
import { pluginRouter } from '~/events/rpc/routes/plugin'
|
||||
import { settingRouter } from '~/events/rpc/routes/setting'
|
||||
@@ -10,10 +13,6 @@ import { systemRouter } from '~/events/rpc/routes/system'
|
||||
import { toolboxRouter } from '~/events/rpc/routes/toolbox'
|
||||
import { trayRouter } from '~/events/rpc/routes/tray'
|
||||
import { uploadRouter } from '~/events/rpc/routes/upload'
|
||||
import { manageRouter } from '~/events/rpc/routes/manage'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { RPC_ACTIONS, RPC_ACTIONS_INVOKE } from '#/events/constants'
|
||||
|
||||
class RPCServer implements IRPCServer {
|
||||
private routes: IRPCRoutes = new Map()
|
||||
@@ -38,12 +37,12 @@ class RPCServer implements IRPCServer {
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
start () {
|
||||
ipcMain.on(RPC_ACTIONS, this.rpcEventHandler)
|
||||
ipcMain.handle(RPC_ACTIONS_INVOKE, this.rpcEventHandlerWithResponse)
|
||||
}
|
||||
|
||||
use(routes: IRPCRoutes) {
|
||||
use (routes: IRPCRoutes) {
|
||||
for (const [action, route] of routes) {
|
||||
if (route.type === IRPCType.SEND) {
|
||||
this.routes.set(action, route)
|
||||
@@ -53,7 +52,7 @@ class RPCServer implements IRPCServer {
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
stop () {
|
||||
ipcMain.off(RPC_ACTIONS, this.rpcEventHandler)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IRPCType, IRPCActionType } from '#/types/enum'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IRPCHandler, IRPCRouter, IRPCRoutes } from '#/types/rpc'
|
||||
|
||||
interface IBatchAddParams {
|
||||
action: IRPCActionType
|
||||
@@ -20,7 +21,7 @@ export class RPCRouter implements IRPCRouter {
|
||||
return this
|
||||
}
|
||||
|
||||
routes() {
|
||||
routes () {
|
||||
return this.routeMap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { clipboard } from 'electron'
|
||||
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { IFilter, IObject } from '@picgo/store/dist/types'
|
||||
import GuiApi from 'apis/gui'
|
||||
import { clipboard } from 'electron'
|
||||
|
||||
import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { ILogType, ImgInfo, ISftpPlistConfig, IStringKeyMap } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
removeFileFromDogeInMain,
|
||||
@@ -14,10 +16,16 @@ import {
|
||||
removeFileFromSFTPInMain
|
||||
} from '~/utils/deleteFunc'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
interface IFilter {
|
||||
orderBy?: 'asc' | 'desc'
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
interface IObject {
|
||||
id?: string
|
||||
[propName: string]: any
|
||||
}
|
||||
const galleryRouter = new RPCRouter()
|
||||
|
||||
const galleryRoutes = [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ManageApi } from '~/manage/manageApi'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { ManageApi } from '~/manage/manageApi'
|
||||
|
||||
export default [
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IObj } from '#/types/types'
|
||||
import getManageApi from '~/manage/Main'
|
||||
|
||||
const manageApi = getManageApi()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import configRoutes from '~/events/rpc/routes/manage/config'
|
||||
import bucketRoutes from '~/events/rpc/routes/manage/bucket'
|
||||
import configRoutes from '~/events/rpc/routes/manage/config'
|
||||
import upDownLoadRoutes from '~/events/rpc/routes/manage/upDownload'
|
||||
|
||||
const manageRouter = new RPCRouter()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { app, dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { downloadFileFromUrl } from '~/manage/utils/common'
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
deleteUploaderConfig,
|
||||
@@ -8,7 +11,6 @@ import {
|
||||
selectUploaderConfig,
|
||||
updateUploaderConfig
|
||||
} from '~/utils/handleUploaderConfig'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
const picbedRouter = new RPCRouter()
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
pluginGetListFunc,
|
||||
pluginImportLocalFunc,
|
||||
pluginInstallFunc,
|
||||
pluginGetListFunc,
|
||||
pluginUpdateAllFunc
|
||||
} from '~/events/rpc/routes/plugin/utils'
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
|
||||
const pluginRouter = new RPCRouter()
|
||||
|
||||
const pluginRoutes = [
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { dialog, shell } from 'electron'
|
||||
import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist'
|
||||
import path from 'path'
|
||||
import path from 'node:path'
|
||||
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist'
|
||||
|
||||
import { ICOREBuildInEvent, IPicGoHelperType, IWindowList } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IDispose, IPicGoPlugin } from '#/types/types'
|
||||
import { handleStreamlinePluginName, simpleClone } from '#/utils/common'
|
||||
import { T } from '~/i18n'
|
||||
import { showNotification } from '~/utils/common'
|
||||
|
||||
import { handleStreamlinePluginName, simpleClone } from '#/utils/common'
|
||||
import { ICOREBuildInEvent, IPicGoHelperType, IWindowList } from '#/types/enum'
|
||||
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
|
||||
// get uploader or transformer config
|
||||
const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => {
|
||||
let config: any[] = []
|
||||
@@ -47,13 +45,17 @@ const handleConfigWithFunction = (config: any[]) => {
|
||||
return config
|
||||
}
|
||||
|
||||
const getPluginList = (): IPicGoPlugin[] => {
|
||||
const getPluginList = async (): Promise<IPicGoPlugin[]> => {
|
||||
const pluginList = picgo.pluginLoader.getFullList()
|
||||
const list = []
|
||||
for (const i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])!
|
||||
const plugin = (await picgo.pluginLoader.getPlugin(pluginList[i]))!
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const pluginPKGPath = path.join(pluginPath, 'package.json')
|
||||
if (!fs.existsSync(pluginPKGPath)) {
|
||||
continue
|
||||
}
|
||||
const pluginPKG = fs.readJSONSync(pluginPKGPath, 'utf8')
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu: Omit<IGuiMenuItem, 'handle'>[] = []
|
||||
@@ -156,7 +158,7 @@ export const handlePluginUninstall = async (fullName: string) => {
|
||||
|
||||
export const pluginGetListFunc = async (event: IIPCEvent) => {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
const list = simpleClone(await getPluginList())
|
||||
// here can just send JS Object not function
|
||||
// or will cause [Failed to serialize arguments] error
|
||||
event.sender.send('pluginList', list)
|
||||
@@ -180,7 +182,7 @@ export const pluginInstallFunc = async (event: IIPCEvent, args: [fullName: strin
|
||||
errMsg: res.success ? '' : res.body
|
||||
})
|
||||
if (res.success) {
|
||||
shortKeyHandler.registerPluginShortKey(res.body[0])
|
||||
await shortKeyHandler.registerPluginShortKey(res.body[0])
|
||||
} else {
|
||||
showNotification({
|
||||
title: T('PLUGIN_INSTALL_FAILED'),
|
||||
@@ -201,7 +203,7 @@ export const pluginImportLocalFunc = async (event: IIPCEvent) => {
|
||||
const res = await picgo.pluginHandler.install(filePaths)
|
||||
if (res.success) {
|
||||
try {
|
||||
const list = simpleClone(getPluginList())
|
||||
const list = simpleClone(await getPluginList())
|
||||
event.sender.send('pluginList', list)
|
||||
} catch (e: any) {
|
||||
event.sender.send('pluginList', [])
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import path from 'node:path'
|
||||
|
||||
import logger from '@core/picgo/logger'
|
||||
import { downloadFile, uploadFile } from '~/utils/syncSettings'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { downloadFile, uploadFile } from '~/utils/syncSettings'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import advancedRoutes from '~/events/rpc/routes/setting/advanced'
|
||||
import configureRoutes from '~/events/rpc/routes/setting/configure'
|
||||
import mainAppRoutes from '~/events/rpc/routes/setting/mainApp'
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
import picgo from '@core/picgo'
|
||||
import { app, IpcMainEvent, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IObj } from '#/types/types'
|
||||
|
||||
const STORE_PATH = dbPathDir()
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import { T } from '~/i18n'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IShortKeyConfig } from '#/types/types'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
const notificationFunc = (result: boolean) => {
|
||||
const notification = new Notification({
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { app, shell } from 'electron'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { app, IpcMainEvent, shell } from 'electron'
|
||||
import { IIPCEvent } from 'root/src/universal/types/rpc'
|
||||
|
||||
import { SET_CURRENT_LANGUAGE } from '#/events/constants'
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
import { i18nManager } from '~/i18n'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
import { SET_CURRENT_LANGUAGE } from '#/events/constants'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.GET_PLATFORM,
|
||||
handler: async (event: IIPCEvent) => {
|
||||
(event as IpcMainEvent).returnValue = process.platform
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.RELOAD_APP,
|
||||
handler: async () => {
|
||||
@@ -31,14 +36,14 @@ export default [
|
||||
{
|
||||
action: IRPCActionType.GET_LANGUAGE_LIST,
|
||||
handler: async (event: IIPCEvent) => {
|
||||
event.returnValue = i18nManager.languageList
|
||||
(event as IpcMainEvent).returnValue = i18nManager.languageList
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.GET_CURRENT_LANGUAGE,
|
||||
handler: async (event: IIPCEvent) => {
|
||||
const { lang, locales } = i18nManager.getCurrentLocales()
|
||||
event.returnValue = [lang, locales]
|
||||
;(event as IpcMainEvent).returnValue = [lang, locales]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { app, BrowserWindow } from 'electron'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IMiniWindowPos, IPicGoPlugin } from '#/types/types'
|
||||
import {
|
||||
buildMainPageMenu,
|
||||
buildMiniPageMenu,
|
||||
@@ -11,13 +13,11 @@ import {
|
||||
} from '~/events/remotes/menu'
|
||||
import { openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.HIDE_DOCK,
|
||||
handler: async (_: IIPCEvent, args: [value: boolean]) => {
|
||||
args[0] ? app.dock.hide() : app.dock.show()
|
||||
args[0] ? app.dock?.hide() : app.dock?.show()
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import path from 'node:path'
|
||||
|
||||
import { dbPathChecker, defaultConfigPath } from '@core/datastore/dbChecker'
|
||||
|
||||
import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils'
|
||||
import { T } from '~/i18n'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
|
||||
|
||||
import { IToolboxCheckerMap, IToolboxFixMap } from '#/types/rpc'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD)
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { DB_PATH, GalleryDB } from '@core/datastore'
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
import { IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
import { GalleryDB, DB_PATH } from '@core/datastore'
|
||||
|
||||
import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
|
||||
import { IToolboxCheckerMap, IToolboxFixMap } from '#/types/rpc'
|
||||
import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
export const checkFileMap: IToolboxCheckerMap<
|
||||
IToolboxItemType.IS_CONFIG_FILE_BROKEN | IToolboxItemType.IS_GALLERY_FILE_BROKEN
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
import { IConfig } from 'piclist'
|
||||
import tunnel from 'tunnel'
|
||||
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
|
||||
import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
|
||||
import { IToolboxCheckerMap } from '#/types/rpc'
|
||||
import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
|
||||
|
||||
function getProxy(proxyStr: string): AxiosRequestConfig['proxy'] | null {
|
||||
function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null {
|
||||
if (proxyStr) {
|
||||
try {
|
||||
const proxyOptions = new URL(proxyStr)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { IpcMainEvent } from 'electron'
|
||||
|
||||
import { IRPCActionType, IRPCType, IToolboxItemType } from '#/types/enum'
|
||||
import { IToolboxCheckArgs, IToolboxCheckerMap, IToolboxFixMap } from '#/types/rpc'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { checkClipboardUploadMap, fixClipboardUploadMap } from '~/events/rpc/routes/toolbox/checkClipboardUpload'
|
||||
import { checkFileMap, fixFileMap } from '~/events/rpc/routes/toolbox/checkFile'
|
||||
import { checkProxyMap } from '~/events/rpc/routes/toolbox/checkProxy'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import { IRPCActionType, IRPCType, IToolboxItemType } from '#/types/enum'
|
||||
import { IpcMainEvent } from 'electron'
|
||||
|
||||
const toolboxRouter = new RPCRouter()
|
||||
|
||||
@@ -22,7 +23,7 @@ const toolboxFixMap: Partial<IToolboxFixMap<IToolboxItemType>> = {
|
||||
toolboxRouter
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK,
|
||||
async (event, args) => {
|
||||
async (event: any, args: IToolboxCheckArgs) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
if (type) {
|
||||
const handler = toolboxCheckMap[type]
|
||||
@@ -43,7 +44,7 @@ toolboxRouter
|
||||
)
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX,
|
||||
async (event, args) => {
|
||||
async (event: any, args: IToolboxCheckArgs) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
const handler = toolboxFixMap[type]
|
||||
if (handler) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { IpcMainEvent } from 'electron'
|
||||
import { IRPCActionType, IToolboxItemType } from '#/types/enum'
|
||||
|
||||
export function sendToolboxResWithType(type: IToolboxItemType) {
|
||||
import { IRPCActionType, IToolboxItemType } from '#/types/enum'
|
||||
import { IToolboxCheckRes } from '#/types/rpc'
|
||||
|
||||
export function sendToolboxResWithType (type: IToolboxItemType) {
|
||||
return (event: IpcMainEvent, res?: Omit<IToolboxCheckRes, 'type'>) => {
|
||||
return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, {
|
||||
...res,
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { generateShortUrl, setTrayToolTip, handleCopyUrl } from '~/utils/common'
|
||||
|
||||
import { IRPCActionType, IRPCType, IPasteStyle, IWindowList } from '#/types/enum'
|
||||
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import uploader from 'apis/app/uploader'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import { T } from '~/i18n'
|
||||
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { IPasteStyle, IRPCActionType, IRPCType, IWindowList } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { T } from '~/i18n'
|
||||
import { generateShortUrl, handleCopyUrl, setTrayToolTip } from '~/utils/common'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
const trayRouter = new RPCRouter()
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from '~/apis/app/uploader/apis'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { IIPCEvent } from '#/types/rpc'
|
||||
import { IFileWithPath } from '#/types/types'
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import getPicBeds from '~/utils/getPicBeds'
|
||||
|
||||
const uploadRouter = new RPCRouter()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import http from 'http'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import http from 'node:http'
|
||||
import path from 'node:path'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
export const imgFilePath = path.join(picgo.baseDir, 'imgTemp')
|
||||
fs.ensureDirSync(imgFilePath)
|
||||
@@ -12,7 +12,7 @@ const serverPort = 36699
|
||||
|
||||
let server: http.Server
|
||||
|
||||
export function startFileServer() {
|
||||
export function startFileServer () {
|
||||
server = http.createServer((req, res) => {
|
||||
const requestPath = req.url?.split('?')[0]
|
||||
const filePath = path.join(imgFilePath, decodeURIComponent(requestPath as string))
|
||||
@@ -36,7 +36,7 @@ export function startFileServer() {
|
||||
})
|
||||
}
|
||||
|
||||
export function stopFileServer() {
|
||||
export function stopFileServer () {
|
||||
server.close(() => {
|
||||
logger.info('File server is stopped')
|
||||
})
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { I18n, ObjectAdapter } from '@piclist/i18n'
|
||||
import fs from 'fs-extra'
|
||||
import yaml from 'js-yaml'
|
||||
import path from 'path'
|
||||
|
||||
import { ObjectAdapter, I18n } from '@picgo/i18n'
|
||||
|
||||
import { builtinI18nList } from '#/i18n'
|
||||
import { ILocales, ILocalesKey } from '#/types/i18n'
|
||||
import { II18nItem, IStringKeyMap } from '#/types/types'
|
||||
|
||||
class I18nManager {
|
||||
private i18n: I18n | null = null
|
||||
private builtinI18nFolder = path.join(__static, 'i18n')
|
||||
private builtinI18nFolder = path.join('./resources', 'i18n')
|
||||
private outterI18nFolder = ''
|
||||
private localesMap: Map<string, ILocales> = new Map()
|
||||
private currentLanguage: string = 'zh-CN'
|
||||
readonly defaultLanguage: string = 'zh-CN'
|
||||
private i18nFileList: II18nItem[] = builtinI18nList
|
||||
|
||||
setOutterI18nFolder(folder: string) {
|
||||
setOutterI18nFolder (folder: string) {
|
||||
this.outterI18nFolder = folder
|
||||
}
|
||||
|
||||
addI18nFile(file: string, label: string) {
|
||||
addI18nFile (file: string, label: string) {
|
||||
this.i18nFileList.push({
|
||||
label,
|
||||
value: file
|
||||
})
|
||||
}
|
||||
|
||||
private getLocales(lang: string): ILocales {
|
||||
private getLocales (lang: string): ILocales {
|
||||
if (this.localesMap.has(lang)) {
|
||||
return this.localesMap.get(lang)!
|
||||
}
|
||||
@@ -53,13 +55,13 @@ class I18nManager {
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentLanguage(lang: string) {
|
||||
setCurrentLanguage (lang: string) {
|
||||
const locales = this.getLocales(lang)
|
||||
this.currentLanguage = lang
|
||||
this.initI18n(lang, locales)
|
||||
}
|
||||
|
||||
private initI18n(lang: string = this.defaultLanguage, locales: ILocales) {
|
||||
private initI18n (lang: string = this.defaultLanguage, locales: ILocales) {
|
||||
const objectAdapter = new ObjectAdapter({
|
||||
[lang]: locales
|
||||
})
|
||||
@@ -69,15 +71,15 @@ class I18nManager {
|
||||
})
|
||||
}
|
||||
|
||||
T(key: ILocalesKey, args: IStringKeyMap = {}): string {
|
||||
T (key: ILocalesKey, args: IStringKeyMap = {}): string {
|
||||
return this.i18n?.translate(key, args) || key
|
||||
}
|
||||
|
||||
get languageList() {
|
||||
get languageList () {
|
||||
return this.i18nFileList
|
||||
}
|
||||
|
||||
getCurrentLocales() {
|
||||
getCurrentLocales () {
|
||||
return {
|
||||
lang: this.currentLanguage,
|
||||
locales: this.getLocales(this.currentLanguage)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path'
|
||||
import { app } from 'electron'
|
||||
import path from 'node:path'
|
||||
|
||||
import { getLogger } from '@core/utils/localLogger'
|
||||
import { app } from 'electron'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const LOG_PATH = path.join(STORE_PATH, 'piclist-gui-local.log')
|
||||
@@ -24,9 +24,9 @@ process.on('unhandledRejection', (error: any) => {
|
||||
})
|
||||
|
||||
// acconrding to https://github.com/Molunerfinn/PicGo/commit/7363be798cfef11e980934e542817ff1d6c04389#diff-896d0db4fbd446798fbffec14d456b4cd98d4c72c46856c770a585fa7ab0926f
|
||||
function bootstrapEPIPESuppression() {
|
||||
function bootstrapEPIPESuppression () {
|
||||
let suppressing = false
|
||||
function logEPIPEErrorOnce() {
|
||||
function logEPIPEErrorOnce () {
|
||||
if (suppressing) {
|
||||
return
|
||||
}
|
||||
@@ -35,8 +35,27 @@ function bootstrapEPIPESuppression() {
|
||||
handleProcessError('Detected EPIPE error; suppressing further EPIPE errors')
|
||||
}
|
||||
|
||||
require('epipebomb')(process.stdout, logEPIPEErrorOnce)
|
||||
require('epipebomb')(process.stderr, logEPIPEErrorOnce)
|
||||
epipeBomb(process.stdout, logEPIPEErrorOnce)
|
||||
epipeBomb(process.stderr, logEPIPEErrorOnce)
|
||||
}
|
||||
|
||||
bootstrapEPIPESuppression()
|
||||
|
||||
function epipeBomb (stream: any, callback: any) {
|
||||
if (stream == null) stream = process.stdout
|
||||
if (callback == null) callback = process.exit
|
||||
|
||||
function epipeFilter (err: any) {
|
||||
if (err.code === 'EPIPE') return callback()
|
||||
|
||||
// If there's more than one error handler (ie, us),
|
||||
// then the error won't be bubbled up anyway
|
||||
if (stream.listeners('error').length <= 1) {
|
||||
stream.removeAllListeners() // Pretend we were never here
|
||||
stream.emit('error', err) // Then emit as if we were never here
|
||||
stream.on('error', epipeFilter) // Then reattach, ready for the next error!
|
||||
}
|
||||
}
|
||||
|
||||
stream.on('error', epipeFilter)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
// TODO: so how to import pure esm module in electron main process????? help wanted
|
||||
import { shellPath } from 'shell-path'
|
||||
|
||||
// just copy the fix-path because I can't import pure ESM module in electron main process
|
||||
// @ts-nocheck since the module is not pure ESM
|
||||
// shell-path 3.0.0 not work
|
||||
|
||||
const shellPath = require('shell-path')
|
||||
|
||||
export default function fixPath() {
|
||||
export default function fixPath () {
|
||||
if (process.platform === 'win32') {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
import axios from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
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 '~/lifeCycle/errorHandler'
|
||||
|
||||
import path from 'node:path'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
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 windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
import { app, dialog, globalShortcut, Notification, protocol, screen, shell } from 'electron'
|
||||
import updater from 'electron-updater'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { notificationList } from '#/utils/notification'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
import busEventList from '~/events/busEventList'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
import { startFileServer, stopFileServer } from '~/fileServer'
|
||||
import { T } from '~/i18n'
|
||||
import '~/lifeCycle/errorHandler'
|
||||
import fixPath from '~/lifeCycle/fixPath'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import getManageApi from '~/manage/Main'
|
||||
@@ -32,11 +36,6 @@ import { getUploadFiles } from '~/utils/handleArgv'
|
||||
import { initI18n } from '~/utils/handleI18n'
|
||||
import updateChecker from '~/utils/updateChecker'
|
||||
|
||||
import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
@@ -58,15 +57,15 @@ const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
return false
|
||||
}
|
||||
|
||||
autoUpdater.setFeedURL({
|
||||
updater.autoUpdater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: 'https://release.piclist.cn/latest',
|
||||
channel: 'latest'
|
||||
})
|
||||
|
||||
autoUpdater.autoDownload = false
|
||||
updater.autoUpdater.autoDownload = false
|
||||
|
||||
autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
updater.autoUpdater.on('update-available', async (info: updater.UpdateInfo) => {
|
||||
const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
let updateLog = ''
|
||||
try {
|
||||
@@ -110,7 +109,7 @@ autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.response === 0) {
|
||||
autoUpdater.downloadUpdate()
|
||||
updater.autoUpdater.downloadUpdate()
|
||||
} else {
|
||||
shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest')
|
||||
}
|
||||
@@ -121,7 +120,7 @@ autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
updater.autoUpdater.on('download-progress', progressObj => {
|
||||
const percent = {
|
||||
progress: progressObj.percent
|
||||
}
|
||||
@@ -129,7 +128,7 @@ autoUpdater.on('download-progress', progressObj => {
|
||||
window.webContents.send('updateProgress', percent)
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
updater.autoUpdater.on('update-downloaded', () => {
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
@@ -141,7 +140,7 @@ autoUpdater.on('update-downloaded', () => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', { progress: 100 })
|
||||
if (result.response === 0) {
|
||||
autoUpdater.quitAndInstall()
|
||||
updater.autoUpdater.quitAndInstall()
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -149,12 +148,12 @@ autoUpdater.on('update-downloaded', () => {
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('error', err => {
|
||||
updater.autoUpdater.on('error', err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
class LifeCycle {
|
||||
async #beforeReady() {
|
||||
async #beforeReady () {
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
// fix the $PATH in macOS & linux
|
||||
fixPath()
|
||||
@@ -166,9 +165,8 @@ class LifeCycle {
|
||||
busEventList.listen()
|
||||
}
|
||||
|
||||
#onReady() {
|
||||
#onReady () {
|
||||
const readyFunction = async () => {
|
||||
createProtocol('picgo')
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
const isAutoListenClipboard = db.get(configPaths.settings.isAutoListenClipboard) || false
|
||||
@@ -192,7 +190,7 @@ class LifeCycle {
|
||||
const currentPicBedConfig = db.get(`picBed.${currentPicBed}`)?._configName || 'Default'
|
||||
const tooltip = `${currentPicBed} ${currentPicBedConfig}`
|
||||
if (process.platform === 'darwin') {
|
||||
isHideDock ? app.dock.hide() : setDockMenu()
|
||||
isHideDock ? app.dock?.hide() : setDockMenu()
|
||||
startMode !== ISartMode.NO_TRAY && createTray(tooltip)
|
||||
} else {
|
||||
createTray(tooltip)
|
||||
@@ -210,9 +208,9 @@ class LifeCycle {
|
||||
handleStartUpFiles(process.argv, process.cwd())
|
||||
}
|
||||
|
||||
if (global.notificationList && global.notificationList?.length > 0) {
|
||||
while (global.notificationList?.length) {
|
||||
const option = global.notificationList.pop()
|
||||
if (notificationList && notificationList.length > 0) {
|
||||
while (notificationList.length) {
|
||||
const option = notificationList.pop()
|
||||
const notice = new Notification(option!)
|
||||
notice.show()
|
||||
}
|
||||
@@ -252,7 +250,7 @@ class LifeCycle {
|
||||
app.whenReady().then(readyFunction)
|
||||
}
|
||||
|
||||
#onRunning() {
|
||||
#onRunning () {
|
||||
app.on('second-instance', (_, commandLine, workingDirectory) => {
|
||||
logger.info('detect second instance')
|
||||
const result = handleStartUpFiles(commandLine, workingDirectory)
|
||||
@@ -267,7 +265,6 @@ class LifeCycle {
|
||||
}
|
||||
})
|
||||
app.on('activate', () => {
|
||||
createProtocol('picgo')
|
||||
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
}
|
||||
@@ -287,7 +284,7 @@ class LifeCycle {
|
||||
}
|
||||
}
|
||||
|
||||
#onQuit() {
|
||||
#onQuit () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
@@ -319,7 +316,7 @@ class LifeCycle {
|
||||
}
|
||||
}
|
||||
|
||||
async launchApp() {
|
||||
async launchApp () {
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import OSS from 'ali-oss'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import { XMLParser } from 'fast-xml-parser'
|
||||
import path from 'path'
|
||||
|
||||
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 * as fastxml from 'fast-xml-parser'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
ConcurrencyPromisePool,
|
||||
formatError,
|
||||
getFileMimeType,
|
||||
hmacSha1Base64,
|
||||
NewDownloader
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
// 坑爹阿里云 返回数据类型标注和实际各种不一致
|
||||
class AliyunApi {
|
||||
@@ -28,7 +28,7 @@ class AliyunApi {
|
||||
timeOut = 30000
|
||||
logger: ManageLogger
|
||||
|
||||
constructor(accessKeyId: string, accessKeySecret: string, logger: ManageLogger) {
|
||||
constructor (accessKeyId: string, accessKeySecret: string, logger: ManageLogger) {
|
||||
this.ctx = new OSS({
|
||||
accessKeyId,
|
||||
accessKeySecret,
|
||||
@@ -39,7 +39,7 @@ class AliyunApi {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
formatFolder(item: string, slicedPrefix: string, urlPrefix: string): any {
|
||||
formatFolder (item: string, slicedPrefix: string, urlPrefix: string): any {
|
||||
return {
|
||||
key: item,
|
||||
url: `${urlPrefix}/${item}`,
|
||||
@@ -54,7 +54,7 @@ class AliyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: OSS.ObjectMeta, slicedPrefix: string, urlPrefix: string): any {
|
||||
formatFile (item: OSS.ObjectMeta, slicedPrefix: string, urlPrefix: string): any {
|
||||
const fileName = item.name.replace(slicedPrefix, '')
|
||||
return {
|
||||
...item,
|
||||
@@ -71,7 +71,7 @@ class AliyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
getCanonicalizedOSSHeaders(headers: IStringKeyMap) {
|
||||
getCanonicalizedOSSHeaders (headers: IStringKeyMap) {
|
||||
const lowerCaseHeaders = Object.keys(headers).reduce((acc, key) => {
|
||||
acc[key.toLowerCase()] = headers[key]
|
||||
return acc
|
||||
@@ -84,7 +84,7 @@ class AliyunApi {
|
||||
return canonicalizedOSSHeaders
|
||||
}
|
||||
|
||||
authorization(
|
||||
authorization (
|
||||
method: string,
|
||||
canonicalizedResource: string,
|
||||
headers: IStringKeyMap,
|
||||
@@ -96,7 +96,7 @@ class AliyunApi {
|
||||
return `OSS ${this.accessKeyId}:${hmacSha1Base64(this.accessKeySecret, stringToSign)}`
|
||||
}
|
||||
|
||||
getNewCtx(region: string, bucket: string) {
|
||||
getNewCtx (region: string, bucket: string) {
|
||||
return new OSS({
|
||||
accessKeyId: this.accessKeyId,
|
||||
accessKeySecret: this.accessKeySecret,
|
||||
@@ -109,7 +109,7 @@ class AliyunApi {
|
||||
/**
|
||||
* 获取存储桶列表
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
const getBuckets = async (marker?: string) => {
|
||||
const res = (await this.ctx.listBuckets({
|
||||
marker,
|
||||
@@ -143,7 +143,7 @@ class AliyunApi {
|
||||
/**
|
||||
* 获取自定义域名
|
||||
*/
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<any> {
|
||||
async getBucketDomain (param: IStringKeyMap): Promise<any> {
|
||||
const headers = {
|
||||
Date: new Date().toUTCString()
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class AliyunApi {
|
||||
})
|
||||
|
||||
if (res?.status === 200) {
|
||||
const parser = new XMLParser()
|
||||
const parser = new fastxml.XMLParser()
|
||||
const result = parser.parse(res.data)
|
||||
|
||||
if (result.ListCnameResult?.Cname) {
|
||||
@@ -186,7 +186,7 @@ class AliyunApi {
|
||||
* @description
|
||||
* acl: private | publicRead | publicReadWrite
|
||||
*/
|
||||
async createBucket(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucket (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const client = new OSS({
|
||||
accessKeyId: this.accessKeyId,
|
||||
accessKeySecret: this.accessKeySecret,
|
||||
@@ -207,7 +207,7 @@ class AliyunApi {
|
||||
return res?.res?.status === 200
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -227,7 +227,7 @@ class AliyunApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -262,7 +262,7 @@ class AliyunApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -282,7 +282,7 @@ class AliyunApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -336,7 +336,7 @@ class AliyunApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList (configMap: IStringKeyMap): Promise<any> {
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
@@ -393,7 +393,7 @@ class AliyunApi {
|
||||
* newKey: string
|
||||
* }
|
||||
*/
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
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
|
||||
@@ -413,7 +413,7 @@ class AliyunApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
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
|
||||
@@ -424,7 +424,7 @@ class AliyunApi {
|
||||
* 删除文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
let marker
|
||||
@@ -486,7 +486,7 @@ class AliyunApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName, region, key, expires, customUrl } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const res = client.signatureUrl(key, {
|
||||
@@ -499,7 +499,7 @@ class AliyunApi {
|
||||
* 上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
// fileArray = [{
|
||||
// bucketName: string,
|
||||
@@ -586,7 +586,7 @@ class AliyunApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
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
|
||||
@@ -597,7 +597,7 @@ class AliyunApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import got from 'got'
|
||||
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 { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { formatHttpProxy, isImage, trimPath } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { formatHttpProxy, trimPath } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
ConcurrencyPromisePool,
|
||||
formatError,
|
||||
getAgent,
|
||||
getOptions,
|
||||
gotUpload,
|
||||
NewDownloader
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class GithubApi {
|
||||
token: string
|
||||
@@ -29,7 +30,7 @@ class GithubApi {
|
||||
baseUrl = 'https://api.github.com'
|
||||
commonHeaders: IStringKeyMap
|
||||
|
||||
constructor(token: string, username: string, proxy: string | undefined, logger: ManageLogger) {
|
||||
constructor (token: string, username: string, proxy: string | undefined, logger: ManageLogger) {
|
||||
this.logger = logger
|
||||
this.token = token.startsWith('Bearer ') ? token : `Bearer ${token}`.trim()
|
||||
this.username = username
|
||||
@@ -41,7 +42,7 @@ class GithubApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFolder(item: any, slicedPrefix: string, branch: string, repo: string, cdnUrl: string | undefined) {
|
||||
formatFolder (item: any, slicedPrefix: string, branch: string, repo: string, cdnUrl: string | undefined) {
|
||||
const key = `${slicedPrefix ? `${slicedPrefix}/` : ''}${item.path}/`
|
||||
let rawUrl = ''
|
||||
const placeholders = ['{username}', '{repo}', '{branch}', '{path}']
|
||||
@@ -78,7 +79,7 @@ class GithubApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: any, slicedPrefix: string, branch: string, repo: string, cdnUrl: string | undefined) {
|
||||
formatFile (item: any, slicedPrefix: string, branch: string, repo: string, cdnUrl: string | undefined) {
|
||||
let rawUrl = ''
|
||||
const placeholders = ['{username}', '{repo}', '{branch}', '{path}']
|
||||
const key = slicedPrefix === '' ? item.path : `${slicedPrefix}/${item.path}`
|
||||
@@ -119,7 +120,7 @@ class GithubApi {
|
||||
/**
|
||||
* get repo list
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
let initPage = 1
|
||||
let res
|
||||
const result = [] as any[]
|
||||
@@ -156,7 +157,7 @@ class GithubApi {
|
||||
/**
|
||||
* 获取branch列表
|
||||
*/
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<any> {
|
||||
async getBucketDomain (param: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: repo } = param
|
||||
let initPage = 1
|
||||
let res
|
||||
@@ -184,7 +185,7 @@ class GithubApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap
|
||||
const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '')
|
||||
@@ -197,7 +198,7 @@ class GithubApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -235,7 +236,7 @@ class GithubApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: repo, customUrl: branch, prefix, cancelToken, cdnUrl } = configMap
|
||||
const slicedPrefix = prefix.replace(/(^\/+|\/+$)/g, '')
|
||||
@@ -248,7 +249,7 @@ class GithubApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -285,7 +286,7 @@ class GithubApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName: repo, githubBranch: branch, key, DeleteHash: sha } = configMap
|
||||
const body = {
|
||||
message: 'deleted by PicList',
|
||||
@@ -303,7 +304,7 @@ class GithubApi {
|
||||
* create a new tree to delete a folder
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName: repo, githubBranch: branch, key } = configMap
|
||||
// get sha of the branch
|
||||
const refRes = (await got(
|
||||
@@ -412,7 +413,7 @@ class GithubApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName: repo, customUrl: branch, key, rawUrl, githubPrivate: isPrivate } = configMap
|
||||
if (!isPrivate) return rawUrl
|
||||
const res = (await got(
|
||||
@@ -436,7 +437,7 @@ class GithubApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName: repo, githubBranch: branch, key } = configMap
|
||||
const newFileKey = `${trimPath(key)}/.gitkeep`
|
||||
const base64Content = Buffer.from('created by PicList').toString('base64')
|
||||
@@ -456,7 +457,7 @@ class GithubApi {
|
||||
* 上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
fileArray.forEach((item: any) => {
|
||||
@@ -505,7 +506,7 @@ class GithubApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs-extra'
|
||||
import got from 'got'
|
||||
import path from 'path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { formatHttpProxy } from '#/utils/common'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
ConcurrencyPromisePool,
|
||||
formatError,
|
||||
getAgent,
|
||||
getFileMimeType,
|
||||
getOptions,
|
||||
getAgent,
|
||||
gotUpload,
|
||||
NewDownloader
|
||||
} from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { formatHttpProxy, isImage } from '#/utils/common'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class ImgurApi {
|
||||
userName: string
|
||||
@@ -31,7 +32,7 @@ class ImgurApi {
|
||||
idHeaders: any
|
||||
baseUrl = 'https://api.imgur.com/3'
|
||||
|
||||
constructor(userName: string, accessToken: string, proxy: any, logger: ManageLogger) {
|
||||
constructor (userName: string, accessToken: string, proxy: any, logger: ManageLogger) {
|
||||
this.userName = userName
|
||||
this.accessToken = accessToken.startsWith('Bearer ') ? accessToken : `Bearer ${accessToken}`
|
||||
this.proxy = proxy
|
||||
@@ -42,7 +43,7 @@ class ImgurApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: any) {
|
||||
formatFile (item: any) {
|
||||
const fileName = path.basename(item.link)
|
||||
const isImg = isImage(fileName)
|
||||
return {
|
||||
@@ -64,7 +65,7 @@ class ImgurApi {
|
||||
/**
|
||||
* get repo list
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
let initPage = 0
|
||||
let res
|
||||
const result = [] as any[]
|
||||
@@ -93,7 +94,7 @@ class ImgurApi {
|
||||
return finalResult
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketConfig: { Location: albumHash },
|
||||
@@ -108,7 +109,7 @@ class ImgurApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -153,7 +154,7 @@ class ImgurApi {
|
||||
ipcMain.removeAllListeners('cancelLoadingFileList')
|
||||
}
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { DeleteHash: deleteHash } = configMap
|
||||
const res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/image/${deleteHash}`,
|
||||
@@ -166,7 +167,7 @@ class ImgurApi {
|
||||
* 上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
fileArray.forEach((item: any) => {
|
||||
@@ -226,7 +227,7 @@ class ImgurApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import * as fsWalk from '@nodelib/fs.walk'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import * as fsWalk from '@nodelib/fs.walk'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError } from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class LocalApi {
|
||||
logger: ManageLogger
|
||||
isWindows: boolean
|
||||
|
||||
constructor(logger: ManageLogger) {
|
||||
constructor (logger: ManageLogger) {
|
||||
this.logger = logger
|
||||
this.isWindows = process.platform === 'win32'
|
||||
}
|
||||
@@ -24,12 +25,12 @@ class LocalApi {
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'LocalApi', method }))
|
||||
|
||||
// windows 系统下将路径转换为 unix 风格
|
||||
transPathToUnix(filePath: string | undefined) {
|
||||
transPathToUnix (filePath: string | undefined) {
|
||||
if (!filePath) return ''
|
||||
return this.isWindows ? filePath.split(path.sep).join(path.posix.sep) : filePath.replace(/^\/+/, '')
|
||||
}
|
||||
|
||||
transBack(filePath: string | undefined) {
|
||||
transBack (filePath: string | undefined) {
|
||||
if (!filePath) return ''
|
||||
return this.isWindows
|
||||
? filePath
|
||||
@@ -39,7 +40,7 @@ class LocalApi {
|
||||
: `/${filePath.replace(/^\/+|\/+$/g, '')}`
|
||||
}
|
||||
|
||||
formatFolder(item: fs.Stats, urlPrefix: string, fileName: string, filePath: string) {
|
||||
formatFolder (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string) {
|
||||
const key = `${this.transPathToUnix(filePath)}/`.replace(/\/+$/, '/')
|
||||
return {
|
||||
...item,
|
||||
@@ -56,7 +57,7 @@ class LocalApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: fs.Stats, urlPrefix: string, fileName: string, filePath: string, isDownload = false) {
|
||||
formatFile (item: fs.Stats, urlPrefix: string, fileName: string, filePath: string, isDownload = false) {
|
||||
const key = isDownload ? filePath : this.transPathToUnix(filePath)
|
||||
return {
|
||||
...item,
|
||||
@@ -73,7 +74,7 @@ class LocalApi {
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { prefix, customUrl = '', cancelToken } = configMap
|
||||
const urlPrefix = customUrl.replace(/\/+$/, '')
|
||||
@@ -86,7 +87,7 @@ class LocalApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -113,7 +114,7 @@ class LocalApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { customUrl = '', cancelToken, baseDir } = configMap
|
||||
let prefix = configMap.prefix
|
||||
@@ -132,7 +133,7 @@ class LocalApi {
|
||||
}
|
||||
})
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -169,7 +170,7 @@ class LocalApi {
|
||||
ipcMain.removeAllListeners('cancelLoadingFileList')
|
||||
}
|
||||
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { oldKey, newKey } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -181,7 +182,7 @@ class LocalApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -193,7 +194,7 @@ class LocalApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -207,7 +208,7 @@ class LocalApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
@@ -249,7 +250,7 @@ class LocalApi {
|
||||
return true
|
||||
}
|
||||
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -263,7 +264,7 @@ class LocalApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import path from 'path'
|
||||
import qiniu from 'qiniu/index'
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
formatError,
|
||||
ConcurrencyPromisePool
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import qiniu from 'qiniu'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
ConcurrencyPromisePool,
|
||||
formatError,
|
||||
getFileMimeType,
|
||||
hmacSha1Base64,
|
||||
NewDownloader
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class QiniuApi {
|
||||
mac: qiniu.auth.digest.Mac
|
||||
@@ -33,14 +33,14 @@ class QiniuApi {
|
||||
getBucketDomain: 'https://uc.qiniuapi.com/v2/domains'
|
||||
}
|
||||
|
||||
constructor(accessKey: string, secretKey: string, logger: ManageLogger) {
|
||||
constructor (accessKey: string, secretKey: string, logger: ManageLogger) {
|
||||
this.mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
|
||||
this.accessKey = accessKey
|
||||
this.secretKey = secretKey
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
formatFolder(item: string, slicedPrefix: string, urlPrefix: string) {
|
||||
formatFolder (item: string, slicedPrefix: string, urlPrefix: string) {
|
||||
return {
|
||||
Key: item,
|
||||
key: item,
|
||||
@@ -54,7 +54,7 @@ class QiniuApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
formatFile (item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
const fileName = item.key.replace(slicedPrefix, '')
|
||||
return {
|
||||
...item,
|
||||
@@ -69,7 +69,7 @@ class QiniuApi {
|
||||
}
|
||||
}
|
||||
|
||||
authorization(
|
||||
authorization (
|
||||
method: string,
|
||||
urlPath: string,
|
||||
host: string,
|
||||
@@ -98,7 +98,7 @@ class QiniuApi {
|
||||
/**
|
||||
* 获取存储桶列表
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
const host = this.hostList.getBucketList
|
||||
const authorization = qiniu.util.generateAccessToken(this.mac, host, undefined)
|
||||
const res = await axios.get(host, {
|
||||
@@ -110,11 +110,11 @@ class QiniuApi {
|
||||
})
|
||||
if (res?.status === 200 && res?.data?.length) {
|
||||
const result = [] as any[]
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
const info = await this.getBucketInfo({ bucketName: res.data[i] })
|
||||
for (const dataItem of res.data) {
|
||||
const info = await this.getBucketInfo({ bucketName: dataItem })
|
||||
if (!info.success) return []
|
||||
result.push({
|
||||
Name: res.data[i],
|
||||
Name: dataItem,
|
||||
Location: info.zone,
|
||||
CreationDate: new Date().toISOString(),
|
||||
Private: info.private
|
||||
@@ -128,7 +128,7 @@ class QiniuApi {
|
||||
/**
|
||||
* 获取存储桶详细信息
|
||||
*/
|
||||
async getBucketInfo(param: IStringKeyMap): Promise<any> {
|
||||
async getBucketInfo (param: IStringKeyMap): Promise<any> {
|
||||
const { bucketName } = param
|
||||
const urlPath = `/v2/bucketInfo?bucket=${bucketName}&fs=true`
|
||||
const authorization = this.authorization('POST', urlPath, this.host, '', '', 'application/json')
|
||||
@@ -160,7 +160,7 @@ class QiniuApi {
|
||||
/**
|
||||
* 获取自定义域名
|
||||
*/
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<any> {
|
||||
async getBucketDomain (param: IStringKeyMap): Promise<any> {
|
||||
const { bucketName } = param
|
||||
const host = this.hostList.getBucketDomain
|
||||
const authorization = qiniu.util.generateAccessToken(this.mac, `${host}?tbl=${bucketName}`, undefined)
|
||||
@@ -180,7 +180,7 @@ class QiniuApi {
|
||||
/**
|
||||
* 修改存储桶权限
|
||||
*/
|
||||
async setBucketAclPolicy(param: IStringKeyMap): Promise<boolean> {
|
||||
async setBucketAclPolicy (param: IStringKeyMap): Promise<boolean> {
|
||||
// 0: 公开访问 1: 私有访问
|
||||
const { bucketName } = param
|
||||
let { isPrivate } = param
|
||||
@@ -213,7 +213,7 @@ class QiniuApi {
|
||||
* acl: boolean // 是否公开访问
|
||||
* }
|
||||
*/
|
||||
async createBucket(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucket (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { BucketName, region, acl } = configMap
|
||||
const urlPath = `/mkbucketv3/${BucketName}/region/${region}`
|
||||
const authorization = this.authorization('POST', urlPath, this.host, '', '', 'application/json')
|
||||
@@ -235,7 +235,7 @@ class QiniuApi {
|
||||
: false
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap
|
||||
let marker = undefined as any
|
||||
@@ -249,7 +249,7 @@ class QiniuApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -297,7 +297,7 @@ class QiniuApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, prefix, cancelToken, customUrl: urlPrefix } = configMap
|
||||
let marker = undefined as any
|
||||
@@ -311,7 +311,7 @@ class QiniuApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -380,14 +380,14 @@ class QiniuApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList (configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, prefix, marker, itemsPerPage, customUrl: urlPrefix } = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
isTruncated: false,
|
||||
nextMarker: '',
|
||||
success: false
|
||||
@@ -440,7 +440,7 @@ class QiniuApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, key } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
@@ -463,7 +463,7 @@ class QiniuApi {
|
||||
* 删除文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, key } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
@@ -535,7 +535,7 @@ class QiniuApi {
|
||||
* newKey: string
|
||||
* }
|
||||
*/
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, oldKey, newKey } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
@@ -574,7 +574,7 @@ class QiniuApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { key, expires, customUrl } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
@@ -588,7 +588,7 @@ class QiniuApi {
|
||||
* 上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
fileArray.forEach((item: any) => {
|
||||
@@ -667,7 +667,7 @@ class QiniuApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, key } = configMap
|
||||
const putPolicy = new qiniu.rs.PutPolicy({
|
||||
scope: `${bucketName}:${key}`
|
||||
@@ -694,7 +694,7 @@ class QiniuApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,40 +1,41 @@
|
||||
import http, { AgentOptions } from 'node:http'
|
||||
import https from 'node:https'
|
||||
import path from 'node:path'
|
||||
|
||||
import {
|
||||
S3Client,
|
||||
ListBucketsCommand,
|
||||
ListObjectsV2Command,
|
||||
GetBucketLocationCommand,
|
||||
_Object,
|
||||
CommonPrefix,
|
||||
ListObjectsV2CommandOutput,
|
||||
CopyObjectCommand,
|
||||
GetObjectCommand,
|
||||
CreateBucketCommand,
|
||||
DeleteObjectCommand,
|
||||
DeleteObjectsCommand,
|
||||
GetBucketLocationCommand,
|
||||
GetObjectCommand,
|
||||
ListBucketsCommand,
|
||||
ListObjectsV2Command,
|
||||
ListObjectsV2CommandOutput,
|
||||
PutBucketAclCommand,
|
||||
PutObjectCommand,
|
||||
S3ClientConfig,
|
||||
CreateBucketCommand,
|
||||
PutPublicAccessBlockCommand,
|
||||
PutBucketAclCommand
|
||||
S3Client,
|
||||
S3ClientConfig
|
||||
} from '@aws-sdk/client-s3'
|
||||
import { Upload, Progress } from '@aws-sdk/lib-storage'
|
||||
import { Progress, Upload } from '@aws-sdk/lib-storage'
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
|
||||
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import http, { AgentOptions } from 'http'
|
||||
import https from 'https'
|
||||
import path from 'path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError, getAgent, getFileMimeType, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import { dogecloudApi, DogecloudToken, getTempToken } from '~/manage/utils/dogeAPI'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage, formatEndpoint, formatHttpProxy } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { formatEndpoint, formatHttpProxy } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ConcurrencyPromisePool, formatError, getAgent, getFileMimeType, NewDownloader } from '~/manage/utils/common'
|
||||
import { dogecloudApi, DogecloudToken, getTempToken } from '~/manage/utils/dogeAPI'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class S3plistApi {
|
||||
baseOptions: S3ClientConfig
|
||||
@@ -46,7 +47,7 @@ class S3plistApi {
|
||||
secretAccessKey: string
|
||||
bucketName: string
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
accessKeyId: string,
|
||||
secretAccessKey: string,
|
||||
endpoint: string | undefined,
|
||||
@@ -75,7 +76,7 @@ class S3plistApi {
|
||||
this.proxy = formatHttpProxy(proxy, 'string') as string | undefined
|
||||
}
|
||||
|
||||
async getDogeCloudToken() {
|
||||
async getDogeCloudToken () {
|
||||
if (!this.dogeCloudSupport) return
|
||||
const token = (await getTempToken(this.accessKeyId, this.secretAccessKey)) as DogecloudToken
|
||||
if (Object.keys(token).length === 0) {
|
||||
@@ -88,7 +89,7 @@ class S3plistApi {
|
||||
}
|
||||
}
|
||||
|
||||
setAgent(proxy: string | undefined, sslEnabled: boolean): NodeHttpHandler {
|
||||
setAgent (proxy: string | undefined, sslEnabled: boolean): NodeHttpHandler {
|
||||
const agent = getAgent(proxy, sslEnabled)
|
||||
const commonOptions: AgentOptions = {
|
||||
keepAlive: true,
|
||||
@@ -98,26 +99,26 @@ class S3plistApi {
|
||||
const extraOptions = sslEnabled ? { rejectUnauthorized: false } : {}
|
||||
return sslEnabled
|
||||
? new NodeHttpHandler({
|
||||
httpsAgent: agent.https
|
||||
? agent.https
|
||||
: new https.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
httpsAgent: agent.https
|
||||
? agent.https
|
||||
: new https.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
: new NodeHttpHandler({
|
||||
httpAgent: agent.http
|
||||
? agent.http
|
||||
: new http.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
httpAgent: agent.http
|
||||
? agent.http
|
||||
: new http.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'S3plistApi', method }))
|
||||
|
||||
formatFolder(item: CommonPrefix, slicedPrefix: string, urlPrefix: string): any {
|
||||
formatFolder (item: CommonPrefix, slicedPrefix: string, urlPrefix: string): any {
|
||||
return {
|
||||
Key: item.Prefix,
|
||||
url: `${urlPrefix}/${item.Prefix}`,
|
||||
@@ -132,7 +133,7 @@ class S3plistApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: _Object, slicedPrefix: string, urlPrefix: string): any {
|
||||
formatFile (item: _Object, slicedPrefix: string, urlPrefix: string): any {
|
||||
const fileName = item.Key?.replace(slicedPrefix, '')
|
||||
return {
|
||||
...item,
|
||||
@@ -148,7 +149,7 @@ class S3plistApi {
|
||||
}
|
||||
}
|
||||
|
||||
async putPublicAccess(bucketName: string, client: S3Client) {
|
||||
async putPublicAccess (bucketName: string, client: S3Client) {
|
||||
const input = {
|
||||
Bucket: bucketName,
|
||||
PublicAccessBlockConfiguration: {
|
||||
@@ -175,11 +176,11 @@ class S3plistApi {
|
||||
* acl: string
|
||||
* }
|
||||
*/
|
||||
async createBucket(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucket (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { BucketName, region, acl, endpoint } = configMap
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new ListBucketsCommand({})
|
||||
@@ -234,7 +235,7 @@ class S3plistApi {
|
||||
/**
|
||||
* 获取存储桶列表
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
if (this.dogeCloudSupport) {
|
||||
try {
|
||||
const res = await dogecloudApi('/oss/bucket/list.json', {}, false, this.accessKeyId, this.secretAccessKey)
|
||||
@@ -255,7 +256,7 @@ class S3plistApi {
|
||||
}
|
||||
return []
|
||||
}
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
const result: IStringKeyMap[] = []
|
||||
const endpoint = (options.endpoint as string) || ''
|
||||
options.region = endpoint.includes('cloudflarestorage') ? 'auto' : 'us-east-1'
|
||||
@@ -305,7 +306,7 @@ class S3plistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -325,13 +326,13 @@ class S3plistApi {
|
||||
})
|
||||
let res = {} as ListObjectsV2CommandOutput
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
try {
|
||||
do {
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
@@ -369,7 +370,7 @@ class S3plistApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -389,14 +390,14 @@ class S3plistApi {
|
||||
})
|
||||
let res = {} as ListObjectsV2CommandOutput
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
do {
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
@@ -439,7 +440,7 @@ class S3plistApi {
|
||||
ipcMain.removeAllListeners('cancelLoadingFileList')
|
||||
}
|
||||
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList (configMap: IStringKeyMap): Promise<any> {
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
@@ -450,17 +451,17 @@ class S3plistApi {
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
isTruncated: false,
|
||||
nextMarker: '',
|
||||
success: false
|
||||
}
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const options = ({
|
||||
|
||||
...this.baseOptions, region: String(region) || 'us-east-1'
|
||||
}) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
Bucket: bucket,
|
||||
@@ -495,15 +496,15 @@ class S3plistApi {
|
||||
* newKey: string
|
||||
* }
|
||||
*/
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, oldKey, newKey } = configMap
|
||||
let result = false
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const options = ({
|
||||
|
||||
...this.baseOptions, region: String(region) || 'us-east-1'
|
||||
}) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new CopyObjectCommand({
|
||||
Bucket: bucketName,
|
||||
@@ -540,12 +541,12 @@ class S3plistApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new DeleteObjectCommand({
|
||||
@@ -568,7 +569,7 @@ class S3plistApi {
|
||||
* 删除文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
let marker
|
||||
let result = false
|
||||
@@ -581,7 +582,7 @@ class S3plistApi {
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
do {
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
@@ -616,7 +617,7 @@ class S3plistApi {
|
||||
}
|
||||
if (allFileList.Contents.length > 0) {
|
||||
const cycle = Math.ceil(allFileList.Contents.length / 1000)
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
for (let i = 0; i < cycle; i++) {
|
||||
@@ -657,11 +658,11 @@ class S3plistApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName, region, key, expires } = configMap
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const signedUrl = await getSignedUrl(
|
||||
@@ -685,12 +686,12 @@ class S3plistApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const command = new PutObjectCommand({
|
||||
@@ -713,7 +714,7 @@ class S3plistApi {
|
||||
* upload file
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
// fileArray = [{
|
||||
// bucketName: string,
|
||||
@@ -764,7 +765,7 @@ class S3plistApi {
|
||||
})
|
||||
continue
|
||||
}
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const options = ({ ...this.baseOptions }) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const fileStream = fs.createReadStream(filePath)
|
||||
@@ -825,7 +826,7 @@ class S3plistApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import path from 'path'
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError } from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { Undefinable } from '#/types/manage'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError } from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
|
||||
interface listDirResult {
|
||||
permissions: string
|
||||
@@ -45,7 +44,7 @@ class SftpApi {
|
||||
passphrase: string
|
||||
}
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
host: string,
|
||||
port: Undefinable<number>,
|
||||
username: Undefinable<string>,
|
||||
@@ -95,7 +94,7 @@ class SftpApi {
|
||||
return `0${result}`
|
||||
}
|
||||
|
||||
formatFolder(item: listDirResult, urlPrefix: string, isWebPath = false) {
|
||||
formatFolder (item: listDirResult, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.key
|
||||
let url: string
|
||||
if (isWebPath) {
|
||||
@@ -122,7 +121,7 @@ class SftpApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: listDirResult, urlPrefix: string, isWebPath = false) {
|
||||
formatFile (item: listDirResult, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.key
|
||||
return {
|
||||
...item,
|
||||
@@ -152,7 +151,7 @@ class SftpApi {
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { prefix, customUrl, cancelToken } = configMap
|
||||
const urlPrefix = customUrl || `${this.host}:${this.port}`
|
||||
@@ -165,7 +164,7 @@ class SftpApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -192,7 +191,7 @@ class SftpApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
formatLSResult(res: string, cwd: string): listDirResult[] {
|
||||
formatLSResult (res: string, cwd: string): listDirResult[] {
|
||||
const result = [] as listDirResult[]
|
||||
const resArray = res.trim().split('\n')
|
||||
resArray.slice(resArray[0].startsWith('total') ? 1 : 0).forEach((item: string) => {
|
||||
@@ -218,7 +217,7 @@ class SftpApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { prefix, customUrl, cancelToken, baseDir } = configMap
|
||||
let urlPrefix = customUrl || `${this.host}:${this.port}`
|
||||
@@ -236,7 +235,7 @@ class SftpApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -277,7 +276,7 @@ class SftpApi {
|
||||
ipcMain.removeAllListeners('cancelLoadingFileList')
|
||||
}
|
||||
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { oldKey, newKey } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -291,7 +290,7 @@ class SftpApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -305,7 +304,7 @@ class SftpApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -322,7 +321,7 @@ class SftpApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
@@ -377,7 +376,7 @@ class SftpApi {
|
||||
return true
|
||||
}
|
||||
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -391,7 +390,7 @@ class SftpApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Agent } from 'node:https'
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios, { AxiosInstance } from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { getFileMimeType, gotUpload, NewDownloader, ConcurrencyPromisePool, formatError } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ConcurrencyPromisePool, formatError, getFileMimeType, gotUpload, NewDownloader } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class SmmsApi {
|
||||
baseUrl = 'https://smms.app/api/v2'
|
||||
@@ -20,7 +21,7 @@ class SmmsApi {
|
||||
logger: ManageLogger
|
||||
timeout = 30000
|
||||
|
||||
constructor(token: string, logger: ManageLogger) {
|
||||
constructor (token: string, logger: ManageLogger) {
|
||||
this.token = token
|
||||
this.axiosInstance = axios.create({
|
||||
baseURL: this.baseUrl,
|
||||
@@ -28,7 +29,7 @@ class SmmsApi {
|
||||
headers: {
|
||||
Authorization: this.token
|
||||
},
|
||||
httpsAgent: new (require('https').Agent)({
|
||||
httpsAgent: new Agent({
|
||||
keepAlive: true,
|
||||
timeout: this.timeout
|
||||
})
|
||||
@@ -36,7 +37,7 @@ class SmmsApi {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
formatFile(item: any) {
|
||||
formatFile (item: any) {
|
||||
return {
|
||||
...item,
|
||||
Key: item.path,
|
||||
@@ -53,7 +54,7 @@ class SmmsApi {
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { cancelToken } = configMap
|
||||
let marker = 1
|
||||
@@ -66,7 +67,7 @@ class SmmsApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -122,9 +123,9 @@ class SmmsApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList({ currentPage }: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList ({ currentPage }: IStringKeyMap): Promise<any> {
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
isTruncated: false,
|
||||
nextMarker: '',
|
||||
success: false
|
||||
@@ -161,7 +162,7 @@ class SmmsApi {
|
||||
* DeleteHash: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile({ DeleteHash }: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile ({ DeleteHash }: IStringKeyMap): Promise<boolean> {
|
||||
const res = await this.axiosInstance(`/delete/${DeleteHash}`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
@@ -176,7 +177,7 @@ class SmmsApi {
|
||||
* 上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
@@ -213,7 +214,7 @@ class SmmsApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import COS from 'cos-nodejs-sdk-v5'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { handleUrlEncode } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError, getFileMimeType } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
|
||||
import { handleUrlEncode, isImage } from '#/utils/common'
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class TcyunApi {
|
||||
ctx: COS
|
||||
logger: ManageLogger
|
||||
|
||||
constructor(secretId: string, secretKey: string, logger: ManageLogger) {
|
||||
constructor (secretId: string, secretKey: string, logger: ManageLogger) {
|
||||
this.ctx = new COS({
|
||||
SecretId: secretId,
|
||||
SecretKey: secretKey
|
||||
@@ -25,7 +26,7 @@ class TcyunApi {
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
formatFolder(item: { Prefix: string }, slicedPrefix: string, urlPrefix: string) {
|
||||
formatFolder (item: { Prefix: string }, slicedPrefix: string, urlPrefix: string) {
|
||||
return {
|
||||
...item,
|
||||
key: item.Prefix,
|
||||
@@ -40,7 +41,7 @@ class TcyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: COS.CosObject, slicedPrefix: string, urlPrefix: string): any {
|
||||
formatFile (item: COS.CosObject, slicedPrefix: string, urlPrefix: string): any {
|
||||
return {
|
||||
...item,
|
||||
key: item.Key,
|
||||
@@ -58,7 +59,7 @@ class TcyunApi {
|
||||
/**
|
||||
* 获取存储桶列表
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
const res = await this.ctx.getService({})
|
||||
return res?.Buckets || []
|
||||
}
|
||||
@@ -66,7 +67,7 @@ class TcyunApi {
|
||||
/**
|
||||
* 获取自定义域名
|
||||
*/
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<any> {
|
||||
async getBucketDomain (param: IStringKeyMap): Promise<any> {
|
||||
const { bucketName, region } = param
|
||||
const res = await this.ctx.getBucketDomain({
|
||||
Bucket: bucketName,
|
||||
@@ -87,7 +88,7 @@ class TcyunApi {
|
||||
* @description
|
||||
* acl: private | publicRead | publicReadWrite
|
||||
*/
|
||||
async createBucket(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucket (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const res = await this.ctx.putBucket({
|
||||
ACL: configMap.acl,
|
||||
Bucket: configMap.BucketName,
|
||||
@@ -96,7 +97,7 @@ class TcyunApi {
|
||||
return res?.statusCode === 200
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -117,7 +118,7 @@ class TcyunApi {
|
||||
}
|
||||
})
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -150,7 +151,7 @@ class TcyunApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const {
|
||||
bucketName: bucket,
|
||||
@@ -172,7 +173,7 @@ class TcyunApi {
|
||||
})
|
||||
let res = {} as COS.GetBucketResult
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -221,7 +222,7 @@ class TcyunApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList (configMap: IStringKeyMap): Promise<any> {
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
@@ -272,7 +273,7 @@ class TcyunApi {
|
||||
* newKey: string
|
||||
* }
|
||||
*/
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, oldKey, newKey } = configMap
|
||||
const copyRes = await this.ctx.putObjectCopy({
|
||||
Bucket: bucketName,
|
||||
@@ -301,7 +302,7 @@ class TcyunApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const res = await this.ctx.deleteObject({
|
||||
Bucket: bucketName,
|
||||
@@ -315,7 +316,7 @@ class TcyunApi {
|
||||
* 删除文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
let marker
|
||||
let res: any
|
||||
@@ -346,8 +347,7 @@ class TcyunApi {
|
||||
region,
|
||||
key: item.Prefix
|
||||
}))
|
||||
)
|
||||
return false
|
||||
) { return false }
|
||||
}
|
||||
const cycles = Math.ceil(allFileList.Contents.length / 1000)
|
||||
for (let i = 0; i < cycles; i++) {
|
||||
@@ -372,7 +372,7 @@ class TcyunApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName, region, key, expires, customUrl } = configMap
|
||||
const res = this.ctx.getObjectUrl(
|
||||
{
|
||||
@@ -391,7 +391,7 @@ class TcyunApi {
|
||||
* 高级上传文件
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
// fileArray = [{
|
||||
// bucketName: string,
|
||||
@@ -471,7 +471,7 @@ class TcyunApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const res = await this.ctx.putObject({
|
||||
Bucket: bucketName,
|
||||
@@ -486,7 +486,7 @@ class TcyunApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray } = configMap
|
||||
// fileArray = [{
|
||||
// bucketName: string,
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import Upyun from 'upyun'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import {
|
||||
md5,
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
gotUpload,
|
||||
ConcurrencyPromisePool,
|
||||
formatError
|
||||
formatError,
|
||||
getFileMimeType,
|
||||
gotUpload,
|
||||
hmacSha1Base64,
|
||||
md5,
|
||||
NewDownloader
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import { isImage } from '~/utils/common'
|
||||
|
||||
class UpyunApi {
|
||||
ser: Upyun.Service
|
||||
@@ -34,7 +34,7 @@ class UpyunApi {
|
||||
stopMarker = 'g2gCZAAEbmV4dGQAA2VvZg'
|
||||
logger: ManageLogger
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
bucket: string,
|
||||
operator: string,
|
||||
password: string,
|
||||
@@ -52,7 +52,7 @@ class UpyunApi {
|
||||
this.expireTime = expireTime || 24 * 60 * 60
|
||||
}
|
||||
|
||||
getAntiLeechParam(key: string): string {
|
||||
getAntiLeechParam (key: string): string {
|
||||
const uri = `/${key}`.replace(/%2F/g, '/').replace(/^\/+/g, '/')
|
||||
const now = Math.round(new Date().getTime() / 1000)
|
||||
const expire = this.expireTime ? now + parseInt(this.expireTime.toString(), 10) : now + 1800
|
||||
@@ -61,7 +61,7 @@ class UpyunApi {
|
||||
return `_upt=${upt}`
|
||||
}
|
||||
|
||||
formatFolder(item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
formatFolder (item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
const key = `${slicedPrefix}${item.name}/`
|
||||
let url = `${urlPrefix}/${key}`
|
||||
if (this.antiLeechToken) {
|
||||
@@ -82,7 +82,7 @@ class UpyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
formatFile (item: any, slicedPrefix: string, urlPrefix: string) {
|
||||
const key = `${slicedPrefix}${item.name}`
|
||||
let url = `${urlPrefix}/${key}`
|
||||
if (this.antiLeechToken) {
|
||||
@@ -102,7 +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}` : ''}`
|
||||
@@ -112,11 +112,11 @@ class UpyunApi {
|
||||
/**
|
||||
* 获取空间列表
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
async getBucketList (): Promise<any> {
|
||||
return this.bucket
|
||||
}
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, prefix, cancelToken } = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
@@ -130,7 +130,7 @@ class UpyunApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class UpyunApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, prefix, cancelToken } = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
@@ -183,7 +183,7 @@ class UpyunApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -227,13 +227,13 @@ class UpyunApi {
|
||||
* customUrl: string
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketFileList (configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, prefix, marker, itemsPerPage } = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `http://${bucket}.test.upcdn.net`
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
isTruncated: false,
|
||||
nextMarker: '',
|
||||
success: false
|
||||
@@ -264,7 +264,7 @@ class UpyunApi {
|
||||
* newKey: string
|
||||
* }
|
||||
*/
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const oldKey = configMap.oldKey
|
||||
let newKey = configMap.newKey
|
||||
const method = 'PUT'
|
||||
@@ -297,7 +297,7 @@ class UpyunApi {
|
||||
* key: string
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
const res = await this.cli.deleteFile(key)
|
||||
return res
|
||||
@@ -307,7 +307,7 @@ class UpyunApi {
|
||||
* delete bucket folder
|
||||
* @param configMap
|
||||
*/
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let marker = ''
|
||||
let isTruncated
|
||||
@@ -341,9 +341,8 @@ class UpyunApi {
|
||||
} while (isTruncated)
|
||||
if (allFileList.Contents.length > 0) {
|
||||
let success = false
|
||||
for (let i = 0; i < allFileList.Contents.length; i++) {
|
||||
const item = allFileList.Contents[i]
|
||||
success = await this.cli.deleteFile(item.key)
|
||||
for (const allFileListItem of allFileList.Contents) {
|
||||
success = await this.cli.deleteFile(allFileListItem.key)
|
||||
if (!success) {
|
||||
return false
|
||||
}
|
||||
@@ -371,7 +370,7 @@ class UpyunApi {
|
||||
* axiso:onUploadProgress not work in nodejs , use got instead
|
||||
* @param configMap
|
||||
*/
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
fileArray.forEach((item: any) => {
|
||||
@@ -427,7 +426,7 @@ class UpyunApi {
|
||||
* 新建文件夹
|
||||
* @param configMap
|
||||
*/
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
const res = await this.cli.makeDir(`/${key}`)
|
||||
return res
|
||||
@@ -437,7 +436,7 @@ class UpyunApi {
|
||||
* 下载文件
|
||||
* @param configMap
|
||||
*/
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import http from 'http'
|
||||
import https from 'https'
|
||||
import path from 'path'
|
||||
import { createClient, WebDAVClient, FileStat, ProgressEvent, AuthType, WebDAVClientOptions } from 'webdav'
|
||||
import http from 'node:http'
|
||||
import https from 'node:https'
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { formatError, getInnerAgent, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
|
||||
import { getAuthHeader } from '@/manage/utils/digestAuth'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { AuthType, createClient, FileStat, ProgressEvent, WebDAVClient, WebDAVClientOptions } from 'webdav'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage, formatEndpoint, formatHttpProxy } from '#/utils/common'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { formatEndpoint, formatHttpProxy } from '#/utils/common'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ConcurrencyPromisePool, formatError, getInnerAgent, NewDownloader } from '~/manage/utils/common'
|
||||
import ManageLogger from '~/manage/utils/logger'
|
||||
import { isImage } from '~/utils/common'
|
||||
import { getAuthHeader } from '~/utils/digestAuth'
|
||||
|
||||
class WebdavplistApi {
|
||||
endpoint: string
|
||||
@@ -29,7 +29,7 @@ class WebdavplistApi {
|
||||
agent: https.Agent | http.Agent
|
||||
ctx: WebDAVClient
|
||||
|
||||
constructor(
|
||||
constructor (
|
||||
endpoint: string,
|
||||
username: string,
|
||||
password: string,
|
||||
@@ -63,7 +63,7 @@ class WebdavplistApi {
|
||||
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
|
||||
|
||||
formatFolder(item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
formatFolder (item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.filename.replace(/^\/+/, '')
|
||||
return {
|
||||
...item,
|
||||
@@ -80,7 +80,7 @@ class WebdavplistApi {
|
||||
}
|
||||
}
|
||||
|
||||
formatFile(item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
formatFile (item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.filename.replace(/^\/+/, '')
|
||||
return {
|
||||
...item,
|
||||
@@ -99,7 +99,7 @@ class WebdavplistApi {
|
||||
|
||||
isRequestSuccess = (code: number) => code >= 200 && code < 300
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { prefix, customUrl, cancelToken } = configMap
|
||||
const urlPrefix = customUrl || this.endpoint
|
||||
@@ -112,7 +112,7 @@ class WebdavplistApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class WebdavplistApi {
|
||||
ipcMain.removeAllListeners(cancelDownloadLoadingFileList)
|
||||
}
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { prefix, customUrl, cancelToken, baseDir } = configMap
|
||||
let urlPrefix = customUrl || this.endpoint
|
||||
@@ -157,7 +157,7 @@ class WebdavplistApi {
|
||||
})
|
||||
let res = {} as any
|
||||
const result = {
|
||||
fullList: <any>[],
|
||||
fullList: [] as any,
|
||||
success: false,
|
||||
finished: false
|
||||
}
|
||||
@@ -198,7 +198,7 @@ class WebdavplistApi {
|
||||
ipcMain.removeAllListeners('cancelLoadingFileList')
|
||||
}
|
||||
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async renameBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { oldKey, newKey } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -210,7 +210,7 @@ class WebdavplistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -222,7 +222,7 @@ class WebdavplistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async deleteBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -234,7 +234,7 @@ class WebdavplistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
async getPreSignedUrl (configMap: IStringKeyMap): Promise<string> {
|
||||
const { key } = configMap
|
||||
let result = ''
|
||||
try {
|
||||
@@ -246,7 +246,7 @@ class WebdavplistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async uploadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async uploadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { fileArray } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
for (const item of fileArray) {
|
||||
@@ -307,7 +307,7 @@ class WebdavplistApi {
|
||||
return true
|
||||
}
|
||||
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async createBucketFolder (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { key } = configMap
|
||||
let result = false
|
||||
try {
|
||||
@@ -321,7 +321,7 @@ class WebdavplistApi {
|
||||
return result
|
||||
}
|
||||
|
||||
async downloadBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
async downloadBucketFile (configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { downloadPath, fileArray, maxDownloadFileCount } = configMap
|
||||
const instance = UpDownTaskQueue.getInstance()
|
||||
const promises = [] as any
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { JSONStore } from '@picgo/store'
|
||||
import { IJSON } from '@picgo/store/dist/types'
|
||||
import { JSONStore } from '@piclist/store'
|
||||
|
||||
import { IManageApiType, IManageConfigType } from '#/types/manage'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
interface IJSON {
|
||||
[propsName: string]: string | number | IJSON
|
||||
}
|
||||
|
||||
class ManageDB {
|
||||
readonly #ctx: IManageApiType
|
||||
readonly #db: JSONStore
|
||||
constructor(ctx: IManageApiType) {
|
||||
constructor (ctx: IManageApiType) {
|
||||
this.#ctx = ctx
|
||||
this.#db = new JSONStore(this.#ctx.configPath)
|
||||
const initParams: IStringKeyMap = {
|
||||
@@ -25,37 +28,37 @@ class ManageDB {
|
||||
}
|
||||
}
|
||||
|
||||
read(flush?: boolean): IJSON {
|
||||
read (flush?: boolean): IJSON {
|
||||
return this.#db.read(flush)
|
||||
}
|
||||
|
||||
get(key: string = ''): any {
|
||||
get (key: string = ''): any {
|
||||
this.read(true)
|
||||
return this.#db.get(key)
|
||||
}
|
||||
|
||||
set(key: string, value: any): void {
|
||||
set (key: string, value: any): void {
|
||||
this.read(true)
|
||||
return this.#db.set(key, value)
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
has (key: string): boolean {
|
||||
this.read(true)
|
||||
return this.#db.has(key)
|
||||
}
|
||||
|
||||
unset(key: string, value: any): boolean {
|
||||
unset (key: string, value: any): boolean {
|
||||
this.read(true)
|
||||
return this.#db.unset(key, value)
|
||||
}
|
||||
|
||||
saveConfig(config: Partial<IManageConfigType>): void {
|
||||
saveConfig (config: Partial<IManageConfigType>): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.set(name, config[name])
|
||||
})
|
||||
}
|
||||
|
||||
removeConfig(config: IManageConfigType): void {
|
||||
removeConfig (config: IManageConfigType): void {
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
this.unset(name, config[name])
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { getLogger } from '@core/utils/localLogger'
|
||||
import dayjs from 'dayjs'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import writeFile from 'write-file-atomic'
|
||||
|
||||
import { getLogger } from '@core/utils/localLogger'
|
||||
|
||||
import { notificationList } from '#/utils/notification'
|
||||
import { T } from '~/i18n'
|
||||
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
@@ -20,10 +21,7 @@ const errorMsg = {
|
||||
brokenButBackup: T('TIPS_PICGO_CONFIG_FILE_BROKEN_WITH_BACKUP')
|
||||
}
|
||||
|
||||
/** ensure notification list */
|
||||
if (!global.notificationList) global.notificationList = []
|
||||
|
||||
function manageDbChecker() {
|
||||
function manageDbChecker () {
|
||||
if (process.type !== 'renderer') {
|
||||
const manageConfigFilePath = managePathChecker()
|
||||
if (!fs.existsSync(manageConfigFilePath)) {
|
||||
@@ -53,16 +51,16 @@ function manageDbChecker() {
|
||||
optionsTpl.body = `${errorMsg.brokenButBackup}\n${T('TIPS_PICGO_BACKUP_FILE_VERSION', {
|
||||
v: dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')
|
||||
})}`
|
||||
global.notificationList.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
} catch (e) {
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
}
|
||||
optionsTpl.body = errorMsg.broken
|
||||
global.notificationList.push(optionsTpl)
|
||||
notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
writeFile.sync(manageConfigFileBackupPath, configFile, {
|
||||
@@ -74,7 +72,7 @@ function manageDbChecker() {
|
||||
/**
|
||||
* Get manage config path
|
||||
*/
|
||||
function managePathChecker(): string {
|
||||
function managePathChecker (): string {
|
||||
if (_configFilePath) {
|
||||
return _configFilePath
|
||||
}
|
||||
@@ -106,7 +104,7 @@ function managePathChecker(): string {
|
||||
title: T('TIPS_NOTICE'),
|
||||
body: T('TIPS_CUSTOM_CONFIG_FILE_PATH_ERROR')
|
||||
}
|
||||
global.notificationList?.push(optionsTpl)
|
||||
notificationList?.push(optionsTpl)
|
||||
hasCheckPath = true
|
||||
}
|
||||
logger('error', e)
|
||||
@@ -115,8 +113,8 @@ function managePathChecker(): string {
|
||||
}
|
||||
}
|
||||
|
||||
function managePathDir() {
|
||||
function managePathDir () {
|
||||
return path.dirname(managePathChecker())
|
||||
}
|
||||
|
||||
export { managePathChecker, managePathDir, manageDbChecker }
|
||||
export { manageDbChecker, managePathChecker, managePathDir }
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
// a singleton class to manage the up/down task queue
|
||||
// qiniu tcyun aliyun smms imgur github upyun
|
||||
|
||||
import path from 'node:path'
|
||||
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { IDownloadTask, IUploadTask } from '#/types/manage'
|
||||
|
||||
class UpDownTaskQueue {
|
||||
/* eslint-disable */
|
||||
private static instance: UpDownTaskQueue
|
||||
/* eslint-enable */
|
||||
private uploadTaskQueue = <IUploadTask[]>[]
|
||||
private static instance: UpDownTaskQueue
|
||||
|
||||
private downloadTaskQueue = <IDownloadTask[]>[]
|
||||
private uploadTaskQueue = [] as IUploadTask[]
|
||||
|
||||
private downloadTaskQueue = [] as IDownloadTask[]
|
||||
|
||||
private persistPath = path.join(app.getPath('userData'), 'UpDownTaskQueue.json')
|
||||
|
||||
private constructor() {
|
||||
private constructor () {
|
||||
this.restore()
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
static getInstance () {
|
||||
if (!UpDownTaskQueue.instance) {
|
||||
UpDownTaskQueue.instance = new UpDownTaskQueue()
|
||||
}
|
||||
return UpDownTaskQueue.instance
|
||||
}
|
||||
|
||||
getUploadTaskQueue() {
|
||||
getUploadTaskQueue () {
|
||||
return UpDownTaskQueue.getInstance().uploadTaskQueue
|
||||
}
|
||||
|
||||
getDownloadTaskQueue() {
|
||||
getDownloadTaskQueue () {
|
||||
return UpDownTaskQueue.getInstance().downloadTaskQueue
|
||||
}
|
||||
|
||||
getUploadTask(taskId: string) {
|
||||
getUploadTask (taskId: string) {
|
||||
return UpDownTaskQueue.getInstance().uploadTaskQueue.find(item => item.id === taskId)
|
||||
}
|
||||
|
||||
getAllUploadTask() {
|
||||
getAllUploadTask () {
|
||||
return UpDownTaskQueue.getInstance().uploadTaskQueue
|
||||
}
|
||||
|
||||
addUploadTask(task: IUploadTask) {
|
||||
addUploadTask (task: IUploadTask) {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue.push(task)
|
||||
}
|
||||
|
||||
updateUploadTask(task: Partial<IUploadTask>) {
|
||||
updateUploadTask (task: Partial<IUploadTask>) {
|
||||
const taskIndex = UpDownTaskQueue.getInstance().uploadTaskQueue.findIndex(item => item.id === task.id)
|
||||
if (taskIndex !== -1) {
|
||||
const taskKeys = Object.keys(task)
|
||||
@@ -61,33 +61,33 @@ class UpDownTaskQueue {
|
||||
}
|
||||
}
|
||||
|
||||
removeUploadTask(taskId: string) {
|
||||
removeUploadTask (taskId: string) {
|
||||
const taskIndex = UpDownTaskQueue.getInstance().uploadTaskQueue.findIndex(item => item.id === taskId)
|
||||
if (taskIndex !== -1) {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue.splice(taskIndex, 1)
|
||||
}
|
||||
}
|
||||
|
||||
removeDownloadTask(taskId: string) {
|
||||
removeDownloadTask (taskId: string) {
|
||||
const taskIndex = UpDownTaskQueue.getInstance().downloadTaskQueue.findIndex(item => item.id === taskId)
|
||||
if (taskIndex !== -1) {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue.splice(taskIndex, 1)
|
||||
}
|
||||
}
|
||||
|
||||
getDownloadTask(taskId: string) {
|
||||
getDownloadTask (taskId: string) {
|
||||
return UpDownTaskQueue.getInstance().downloadTaskQueue.find(item => item.id === taskId)
|
||||
}
|
||||
|
||||
getAllDownloadTask() {
|
||||
getAllDownloadTask () {
|
||||
return UpDownTaskQueue.getInstance().downloadTaskQueue
|
||||
}
|
||||
|
||||
addDownloadTask(task: IDownloadTask) {
|
||||
addDownloadTask (task: IDownloadTask) {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue.push(task)
|
||||
}
|
||||
|
||||
updateDownloadTask(task: Partial<IDownloadTask>) {
|
||||
updateDownloadTask (task: Partial<IDownloadTask>) {
|
||||
const taskIndex = UpDownTaskQueue.getInstance().downloadTaskQueue.findIndex(item => item.id === task.id)
|
||||
if (taskIndex !== -1) {
|
||||
const taskKeys = Object.keys(task)
|
||||
@@ -99,11 +99,11 @@ class UpDownTaskQueue {
|
||||
}
|
||||
}
|
||||
|
||||
clearUploadTaskQueue() {
|
||||
clearUploadTaskQueue () {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = []
|
||||
}
|
||||
|
||||
removeUploadedTask() {
|
||||
removeUploadedTask () {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = UpDownTaskQueue.getInstance().uploadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== uploadTaskSpecialStatus.uploaded &&
|
||||
@@ -112,7 +112,7 @@ class UpDownTaskQueue {
|
||||
)
|
||||
}
|
||||
|
||||
removeDownloadedTask() {
|
||||
removeDownloadedTask () {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = UpDownTaskQueue.getInstance().downloadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== downloadTaskSpecialStatus.downloaded &&
|
||||
@@ -121,16 +121,16 @@ class UpDownTaskQueue {
|
||||
)
|
||||
}
|
||||
|
||||
clearDownloadTaskQueue() {
|
||||
clearDownloadTaskQueue () {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = []
|
||||
}
|
||||
|
||||
clearAllTaskQueue() {
|
||||
clearAllTaskQueue () {
|
||||
this.clearUploadTaskQueue()
|
||||
this.clearDownloadTaskQueue()
|
||||
}
|
||||
|
||||
persist() {
|
||||
persist () {
|
||||
try {
|
||||
this.checkPersistPath()
|
||||
fs.writeFileSync(
|
||||
@@ -145,7 +145,7 @@ class UpDownTaskQueue {
|
||||
}
|
||||
}
|
||||
|
||||
private restore() {
|
||||
private restore () {
|
||||
try {
|
||||
this.checkPersistPath()
|
||||
const persistData = JSON.parse(fs.readFileSync(this.persistPath, { encoding: 'utf-8' }))
|
||||
@@ -157,7 +157,7 @@ class UpDownTaskQueue {
|
||||
}
|
||||
}
|
||||
|
||||
private checkPersistPath() {
|
||||
private checkPersistPath () {
|
||||
if (!fs.existsSync(this.persistPath)) {
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import fs from 'fs-extra'
|
||||
import { get, set, unset } from 'lodash'
|
||||
import { homedir } from 'os'
|
||||
import path from 'path'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import { homedir } from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import API from '~/manage/apis/api'
|
||||
import ManageDB from '~/manage/datastore/db'
|
||||
import { managePathChecker } from '~/manage/datastore/dbChecker'
|
||||
import { isInputConfigValid, formatError } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { ipcMain } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { get, set, unset } from 'lodash-es'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import { IManageApiType, IManageConfigType, IManageError, IPicBedMangeConfig } from '#/types/manage'
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
import API from '~/manage/apis/api'
|
||||
import ManageDB from '~/manage/datastore/db'
|
||||
import { managePathChecker } from '~/manage/datastore/dbChecker'
|
||||
import { formatError, isInputConfigValid } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
private _config!: Partial<IManageConfigType>
|
||||
@@ -26,7 +26,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
logger: ManageLogger
|
||||
currentPicBedConfig: IPicBedMangeConfig
|
||||
|
||||
constructor(currentPicBed: string = '') {
|
||||
constructor (currentPicBed: string = '') {
|
||||
super()
|
||||
this.currentPicBed = currentPicBed || 'placeholder'
|
||||
this.configPath = managePathChecker()
|
||||
@@ -36,7 +36,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
this.currentPicBedConfig = this.getPicBedConfig(this.currentPicBed)
|
||||
}
|
||||
|
||||
getMsgParam(method: string) {
|
||||
getMsgParam (method: string) {
|
||||
return {
|
||||
class: 'ManageApi',
|
||||
method,
|
||||
@@ -44,11 +44,11 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
errorMsg(err: any, param: IStringKeyMap) {
|
||||
errorMsg (err: any, param: IStringKeyMap) {
|
||||
this.logger.error(formatError(err, param))
|
||||
}
|
||||
|
||||
createClient() {
|
||||
createClient () {
|
||||
const name = this.currentPicBedConfig.picBedName
|
||||
switch (name) {
|
||||
case 'aliyun':
|
||||
@@ -127,11 +127,11 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
private getPicBedConfig(picBedName: string): IPicBedMangeConfig {
|
||||
private getPicBedConfig (picBedName: string): IPicBedMangeConfig {
|
||||
return this.getConfig<IPicBedMangeConfig>(`picBed.${picBedName}`)
|
||||
}
|
||||
|
||||
private initConfigPath(): void {
|
||||
private initConfigPath (): void {
|
||||
if (this.configPath === '') {
|
||||
this.configPath = `${homedir()}/.piclist/manage.json`
|
||||
}
|
||||
@@ -146,7 +146,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
private initconfig(): void {
|
||||
private initconfig (): void {
|
||||
this.db = new ManageDB(this)
|
||||
this._config = this.db.read(true) as IManageConfigType
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
return get(this._config, name)
|
||||
}
|
||||
|
||||
saveConfig(config: IStringKeyMap): void {
|
||||
saveConfig (config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
@@ -167,7 +167,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
this.db.saveConfig(config)
|
||||
}
|
||||
|
||||
removeConfig(key: string, propName: string): void {
|
||||
removeConfig (key: string, propName: string): void {
|
||||
if (!key || !propName) {
|
||||
return
|
||||
}
|
||||
@@ -175,7 +175,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
this.db.unset(key, propName)
|
||||
}
|
||||
|
||||
setConfig(config: IStringKeyMap): void {
|
||||
setConfig (config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
@@ -185,12 +185,12 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
})
|
||||
}
|
||||
|
||||
unsetConfig(key: string, propName: string): void {
|
||||
unsetConfig (key: string, propName: string): void {
|
||||
if (!key || !propName) return
|
||||
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) {
|
||||
@@ -232,12 +232,12 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
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':
|
||||
@@ -262,7 +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':
|
||||
@@ -281,32 +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':
|
||||
@@ -322,7 +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 = {
|
||||
@@ -365,7 +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 = {
|
||||
@@ -412,9 +412,9 @@ 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>[],
|
||||
fullList: [] as any,
|
||||
isTruncated: false,
|
||||
nextMarker: '',
|
||||
success: false
|
||||
@@ -439,7 +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':
|
||||
@@ -466,7 +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':
|
||||
@@ -490,7 +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':
|
||||
@@ -513,7 +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':
|
||||
@@ -540,12 +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':
|
||||
@@ -569,7 +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':
|
||||
@@ -595,7 +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':
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import crypto from 'node:crypto'
|
||||
import http from 'node:http'
|
||||
import https from 'node:https'
|
||||
import path from 'node:path'
|
||||
import { Stream } from 'node:stream'
|
||||
import { promisify } from 'node:util'
|
||||
|
||||
import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import got, { OptionsOfTextResponseBody, RequestError } from 'got'
|
||||
import { HttpsProxyAgent, HttpProxyAgent } from 'hpagent'
|
||||
import http from 'http'
|
||||
import https from 'https'
|
||||
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'
|
||||
import mime from 'mime-types'
|
||||
import Downloader from 'nodejs-file-downloader'
|
||||
import path from 'path'
|
||||
import { Stream } from 'stream'
|
||||
import { promisify } from 'util'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { IHTTPProxy, IStringKeyMap } from '#/types/types'
|
||||
import { formatHttpProxy } from '#/utils/common'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
export const getFSFile = async (filePath: string, stream: boolean = false): Promise<IStringKeyMap> => {
|
||||
try {
|
||||
@@ -33,7 +34,7 @@ export const getFSFile = async (filePath: string, stream: boolean = false): Prom
|
||||
}
|
||||
}
|
||||
|
||||
export function isInputConfigValid(config: any): boolean {
|
||||
export function isInputConfigValid (config: any): boolean {
|
||||
return typeof config === 'object' && !Array.isArray(config) && Object.keys(config).length > 0
|
||||
}
|
||||
|
||||
@@ -55,14 +56,14 @@ export const downloadFileFromUrl = async (urls: string[]) => {
|
||||
const tempPath = getTempDirPath()
|
||||
await checkTempFolderExist(tempPath)
|
||||
const result = [] as string[]
|
||||
for (let i = 0; i < urls.length; i++) {
|
||||
for (const url of urls) {
|
||||
const finishDownload = promisify(Stream.finished)
|
||||
const fileName = path.basename(urls[i]).split('?')[0]
|
||||
const fileName = path.basename(url).split('?')[0]
|
||||
const filePath = path.join(tempPath, fileName)
|
||||
const writer = fs.createWriteStream(filePath)
|
||||
const res = await axios({
|
||||
method: 'get',
|
||||
url: urls[i],
|
||||
url,
|
||||
responseType: 'stream'
|
||||
})
|
||||
res.data.pipe(writer)
|
||||
@@ -279,7 +280,7 @@ export const getInnerAgent = (proxy: any, sslEnabled: boolean = true) => {
|
||||
}
|
||||
}
|
||||
|
||||
export function getOptions(
|
||||
export function getOptions (
|
||||
method?: string,
|
||||
headers?: IStringKeyMap,
|
||||
searchParams?: IStringKeyMap,
|
||||
@@ -308,14 +309,14 @@ export class ConcurrencyPromisePool {
|
||||
runningNum: number
|
||||
results: any[]
|
||||
|
||||
constructor(limit: number) {
|
||||
constructor (limit: number) {
|
||||
this.limit = limit
|
||||
this.queue = []
|
||||
this.runningNum = 0
|
||||
this.results = []
|
||||
}
|
||||
|
||||
all(promises: any[] = []) {
|
||||
all (promises: any[] = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const promise of promises) {
|
||||
this._run(promise, resolve, reject)
|
||||
@@ -323,7 +324,7 @@ export class ConcurrencyPromisePool {
|
||||
})
|
||||
}
|
||||
|
||||
_run(promise: any, resolve: any, reject: any) {
|
||||
_run (promise: any, resolve: any, reject: any) {
|
||||
if (this.runningNum >= this.limit) {
|
||||
this.queue.push(promise)
|
||||
return
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
|
||||
const AliyunAreaCodeName: IStringKeyMap = {
|
||||
'oss-cn-hangzhou': '华东1(杭州)',
|
||||
'oss-cn-shanghai': '华东2(上海)',
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
import querystring from 'querystring'
|
||||
import crypto from 'node:crypto'
|
||||
import querystring from 'node:querystring'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import axios from 'axios'
|
||||
|
||||
import { IObj } from '#/types/types'
|
||||
|
||||
export interface DogecloudToken {
|
||||
accessKeyId: string
|
||||
@@ -10,7 +12,7 @@ export interface DogecloudToken {
|
||||
sessionToken: string
|
||||
}
|
||||
|
||||
export async function dogecloudApi(
|
||||
export async function dogecloudApi (
|
||||
apiPath: string,
|
||||
data = {},
|
||||
jsonMode: boolean = false,
|
||||
@@ -43,7 +45,7 @@ export async function dogecloudApi(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTempToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudToken> {
|
||||
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
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import path from 'node:path'
|
||||
import util from 'node:util'
|
||||
|
||||
import chalk from 'chalk'
|
||||
import dayjs from 'dayjs'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { ILogColor, ILogger } from 'piclist/dist/types'
|
||||
import util from 'util'
|
||||
|
||||
import { ILogType } from '#/types/enum'
|
||||
import { IManageApiType, Undefinable } from '#/types/manage'
|
||||
import { enforceNumber, isDev } from '#/utils/common'
|
||||
import { ILogArgvType, ILogArgvTypeWithError } from '#/types/types'
|
||||
import { enforceNumber } from '#/utils/common'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
export class ManageLogger implements ILogger {
|
||||
@@ -22,11 +24,11 @@ export class ManageLogger implements ILogger {
|
||||
#logLevel!: string
|
||||
#logPath!: string
|
||||
|
||||
constructor(ctx: IManageApiType) {
|
||||
constructor (ctx: IManageApiType) {
|
||||
this.#ctx = ctx
|
||||
}
|
||||
|
||||
#handleLog(type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
#handleLog (type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](`[PicList ${type.toUpperCase()}]`)
|
||||
console.log(logHeader, ...msg)
|
||||
this.#logLevel = this.#ctx.getConfig(configPaths.settings.logLevel)
|
||||
@@ -51,7 +53,7 @@ export class ManageLogger implements ILogger {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
#checkLogFileIsLarge(logPath: string): {
|
||||
#checkLogFileIsLarge (logPath: string): {
|
||||
isLarge: boolean
|
||||
logFileSize?: number
|
||||
logFileSizeLimit?: number
|
||||
@@ -74,14 +76,14 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
#recreateLogFile(logPath: string): void {
|
||||
#recreateLogFile (logPath: string): void {
|
||||
if (fs.existsSync(logPath)) {
|
||||
fs.unlinkSync(logPath)
|
||||
fs.createFileSync(logPath)
|
||||
}
|
||||
}
|
||||
|
||||
#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()}] `
|
||||
@@ -96,7 +98,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
#formatLogItem(item: ILogArgvTypeWithError, type: string): string {
|
||||
#formatLogItem (item: ILogArgvTypeWithError, type: string): string {
|
||||
let result = ''
|
||||
if (item instanceof Error && type === 'error') {
|
||||
result += `\n------Error Stack Begin------\n${util.format(item?.stack)}\n-------Error Stack End------- `
|
||||
@@ -112,7 +114,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
|
||||
}
|
||||
@@ -122,24 +124,24 @@ export class ManageLogger implements ILogger {
|
||||
return type === level
|
||||
}
|
||||
|
||||
success(...msq: ILogArgvType[]): void {
|
||||
success (...msq: ILogArgvType[]): void {
|
||||
return this.#handleLog(ILogType.success, ...msq)
|
||||
}
|
||||
|
||||
info(...msq: ILogArgvType[]): void {
|
||||
info (...msq: ILogArgvType[]): void {
|
||||
return this.#handleLog(ILogType.info, ...msq)
|
||||
}
|
||||
|
||||
error(...msq: ILogArgvTypeWithError[]): void {
|
||||
error (...msq: ILogArgvTypeWithError[]): void {
|
||||
return this.#handleLog(ILogType.error, ...msq)
|
||||
}
|
||||
|
||||
warn(...msq: ILogArgvType[]): void {
|
||||
warn (...msq: ILogArgvType[]): void {
|
||||
return this.#handleLog(ILogType.warn, ...msq)
|
||||
}
|
||||
|
||||
debug(...msq: ILogArgvType[]): void {
|
||||
if (isDev) {
|
||||
debug (...msq: ILogArgvType[]): void {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
this.#handleLog(ILogType.info, ...msq)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import axios from 'axios'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import http from 'http'
|
||||
import multer from 'multer'
|
||||
import path from 'path'
|
||||
import http from 'node:http'
|
||||
import path from 'node:path'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import axios from 'axios'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import multer from 'multer'
|
||||
|
||||
import routers from '~/server/routerManager'
|
||||
import { handleResponse, ensureHTTPLink } from '~/server/utils'
|
||||
|
||||
import { ErrnoException, IObj, IServerConfig } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import routers from '~/server/routerManager'
|
||||
import { ensureHTTPLink, handleResponse } from '~/server/utils'
|
||||
|
||||
const DEFAULT_PORT = 36677
|
||||
const DEFAULT_HOST = '0.0.0.0'
|
||||
@@ -26,7 +26,6 @@ const multerStorage = multer.diskStorage({
|
||||
cb(null, serverTempDir)
|
||||
},
|
||||
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')
|
||||
}
|
||||
@@ -42,12 +41,12 @@ class Server {
|
||||
#httpServer: http.Server
|
||||
#config: IServerConfig
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.#config = this.getConfigWithDefaults()
|
||||
this.#httpServer = http.createServer(this.#handleRequest)
|
||||
}
|
||||
|
||||
getConfigWithDefaults() {
|
||||
getConfigWithDefaults () {
|
||||
let config = picgo.getConfig<IServerConfig>(configPaths.settings.server)
|
||||
if (!this.#isValidConfig(config)) {
|
||||
config = { port: DEFAULT_PORT, host: DEFAULT_HOST, enable: true }
|
||||
@@ -56,7 +55,7 @@ class Server {
|
||||
return config
|
||||
}
|
||||
|
||||
#isValidConfig(config: IObj | undefined) {
|
||||
#isValidConfig (config: IObj | undefined) {
|
||||
return config && config.port && config.host && config.enable !== undefined
|
||||
}
|
||||
|
||||
@@ -199,20 +198,20 @@ class Server {
|
||||
})
|
||||
}
|
||||
|
||||
startup() {
|
||||
startup () {
|
||||
if (this.#config.enable) {
|
||||
this.#listen(this.#config.port)
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(hasStarted?: boolean) {
|
||||
shutdown (hasStarted?: boolean) {
|
||||
this.#httpServer.close()
|
||||
if (!hasStarted) {
|
||||
logger.info('[PicList Server] shutdown')
|
||||
}
|
||||
}
|
||||
|
||||
restart() {
|
||||
restart () {
|
||||
this.shutdown()
|
||||
this.#config = this.getConfigWithDefaults()
|
||||
this.startup()
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import { routeHandler } from '#/types/types'
|
||||
|
||||
type HttpMethod = 'GET' | 'POST'
|
||||
|
||||
class Router {
|
||||
#router = new Map<string, Map<HttpMethod, { handler: routeHandler; urlparams?: URLSearchParams }>>()
|
||||
|
||||
#addRoute(method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
#addRoute (method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
if (!this.#router.has(url)) {
|
||||
this.#router.set(url, new Map())
|
||||
}
|
||||
this.#router.get(url)!.set(method, { handler: callback, urlparams })
|
||||
}
|
||||
|
||||
get(url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
get (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.#addRoute('GET', url, callback, urlparams)
|
||||
}
|
||||
|
||||
post(url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
post (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.#addRoute('POST', url, callback, urlparams)
|
||||
}
|
||||
|
||||
any(url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
any (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.#addRoute('GET', url, callback, urlparams)
|
||||
this.#addRoute('POST', url, callback, urlparams)
|
||||
}
|
||||
|
||||
getHandler(url: string, method: HttpMethod) {
|
||||
getHandler (url: string, method: HttpMethod) {
|
||||
if (this.#router.has(url)) {
|
||||
const methods = this.#router.get(url)!
|
||||
if (methods.has(method)) {
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import http from 'http'
|
||||
import { marked } from 'marked'
|
||||
import path from 'path'
|
||||
import http from 'node:http'
|
||||
import path from 'node:path'
|
||||
|
||||
import { dbPathDir } from '@core/datastore/dbChecker'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import { AESHelper } from '~/utils/aesHelper'
|
||||
import { changeCurrentUploader } from '~/utils/handleUploaderConfig'
|
||||
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { app } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { marked } from 'marked'
|
||||
|
||||
import { IHttpResponse, IStringKeyMap } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { markdownContent } from '~/server/apiDoc'
|
||||
import router from '~/server/router'
|
||||
import { deleteChoosedFiles, handleResponse } from '~/server/utils'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { AESHelper } from '~/utils/aesHelper'
|
||||
import { changeCurrentUploader } from '~/utils/handleUploaderConfig'
|
||||
|
||||
const appPath = app.getPath('userData')
|
||||
const serverTempDir = path.join(appPath, 'serverTemp')
|
||||
@@ -29,7 +27,7 @@ const LOG_PATH = path.join(STORE_PATH, 'piclist.log')
|
||||
const errorMessage = `upload error. see ${LOG_PATH} for more detail.`
|
||||
const deleteErrorMessage = `delete error. see ${LOG_PATH} for more detail.`
|
||||
|
||||
async function responseForGet({ response }: { response: http.ServerResponse }) {
|
||||
async function responseForGet ({ response }: { response: http.ServerResponse }) {
|
||||
response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
|
||||
const htmlContent = marked(markdownContent)
|
||||
response.write(htmlContent)
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import db, { GalleryDB } from '@core/datastore'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import ALLApi from 'apis/delete/allApi'
|
||||
import GuiApi from 'apis/gui'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import GuiApi from '~/apis/gui'
|
||||
import { T } from '~/i18n/index'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
import { IHttpResponse, ImgInfo, IObj } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { picBedsCanbeDeleted } from '#/utils/static'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
|
||||
import ALLApi from '@/apis/allApi'
|
||||
import { T } from '~/i18n/index'
|
||||
|
||||
export const handleResponse = ({
|
||||
response,
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import fs from 'fs-extra'
|
||||
import http from 'http'
|
||||
import path from 'path'
|
||||
import http from 'node:http'
|
||||
import path from 'node:path'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
import { encodeFilePath } from '#/utils/common'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const defaultPath = process.platform === 'win32' ? 'C:\\Users' : '/'
|
||||
|
||||
function generateDirectoryListingHtml(files: any[], requestPath: any) {
|
||||
function generateDirectoryListingHtml (files: any[], requestPath: any) {
|
||||
let html = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><h1>Directory Listing</h1><ul>'
|
||||
files.forEach((file: string) => {
|
||||
const filePath = path.join(requestPath, file)
|
||||
@@ -20,7 +21,7 @@ function generateDirectoryListingHtml(files: any[], requestPath: any) {
|
||||
return html
|
||||
}
|
||||
|
||||
function serveDirectory(res: http.ServerResponse, filePath: fs.PathLike, requestPath: any) {
|
||||
function serveDirectory (res: http.ServerResponse, filePath: fs.PathLike, requestPath: any) {
|
||||
fs.readdir(filePath, (err, files) => {
|
||||
if (err) {
|
||||
res.writeHead(500)
|
||||
@@ -32,7 +33,7 @@ function serveDirectory(res: http.ServerResponse, filePath: fs.PathLike, request
|
||||
})
|
||||
}
|
||||
|
||||
function serveFile(res: http.ServerResponse, filePath: fs.PathLike) {
|
||||
function serveFile (res: http.ServerResponse, filePath: fs.PathLike) {
|
||||
const readStream = fs.createReadStream(filePath)
|
||||
readStream.pipe(res)
|
||||
readStream.on('error', () => {
|
||||
@@ -45,12 +46,12 @@ class WebServer {
|
||||
#server!: http.Server
|
||||
#config!: IStringKeyMap
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
this.loadConfig()
|
||||
this.initServer()
|
||||
}
|
||||
|
||||
loadConfig(): void {
|
||||
loadConfig (): void {
|
||||
this.#config = {
|
||||
enableWebServer: picgo.getConfig<boolean>(configPaths.settings.enableWebServer) || false,
|
||||
webServerHost: picgo.getConfig<string>(configPaths.settings.webServerHost) || '0.0.0.0',
|
||||
@@ -59,7 +60,7 @@ class WebServer {
|
||||
}
|
||||
}
|
||||
|
||||
initServer(): void {
|
||||
initServer (): void {
|
||||
this.#server = http.createServer((req, res) => {
|
||||
const requestPath = req.url?.split('?')[0]
|
||||
const filePath = path.join(this.#config.webServerPath, decodeURIComponent(requestPath || ''))
|
||||
@@ -78,7 +79,7 @@ class WebServer {
|
||||
})
|
||||
}
|
||||
|
||||
start() {
|
||||
start () {
|
||||
if (this.#config.enableWebServer) {
|
||||
this.#server
|
||||
.listen(
|
||||
@@ -98,13 +99,13 @@ class WebServer {
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
stop () {
|
||||
this.#server.close(() => {
|
||||
logger.info('Web server is stopped')
|
||||
})
|
||||
}
|
||||
|
||||
restart() {
|
||||
restart () {
|
||||
this.stop()
|
||||
this.loadConfig()
|
||||
this.initServer()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import crypto from 'crypto'
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
@@ -14,7 +14,7 @@ export class AESHelper {
|
||||
'sha512'
|
||||
)
|
||||
|
||||
encrypt(plainText: string) {
|
||||
encrypt (plainText: string) {
|
||||
const iv = crypto.randomBytes(16)
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv)
|
||||
let encrypted = cipher.update(plainText, 'utf8', 'hex')
|
||||
@@ -22,7 +22,7 @@ export class AESHelper {
|
||||
return `${iv.toString('hex')}:${encrypted}`
|
||||
}
|
||||
|
||||
decrypt(encryptedData: string) {
|
||||
decrypt (encryptedData: string) {
|
||||
const [ivHex, encryptedText] = encryptedData.split(':')
|
||||
if (!ivHex || !encryptedText) return '{}'
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import fs from 'fs-extra'
|
||||
import yaml from 'js-yaml'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
import { dbPathChecker } from '@core/datastore/dbChecker'
|
||||
import fs from 'fs-extra'
|
||||
import yaml from 'js-yaml'
|
||||
import { ILocales } from 'root/src/universal/types/i18n'
|
||||
|
||||
import { i18nManager } from '~/i18n'
|
||||
|
||||
const configPath = dbPathChecker()
|
||||
const CONFIG_DIR = path.dirname(configPath)
|
||||
|
||||
function beforeOpen() {
|
||||
function beforeOpen () {
|
||||
if (process.platform === 'darwin') {
|
||||
resolveMacWorkFlow()
|
||||
}
|
||||
@@ -18,7 +19,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()) {
|
||||
@@ -42,16 +43,16 @@ function copyFileOutsideOfElectronAsar(sourceInAsarArchive: string, destOutsideA
|
||||
/**
|
||||
* macOS 右键菜单
|
||||
*/
|
||||
function resolveMacWorkFlow() {
|
||||
function resolveMacWorkFlow () {
|
||||
const dest = `${os.homedir()}/Library/Services/Upload pictures with PicList.workflow`
|
||||
try {
|
||||
copyFileOutsideOfElectronAsar(path.join(__static, 'Upload pictures with PicList.workflow'), dest)
|
||||
copyFileOutsideOfElectronAsar(path.join('./resources', 'Upload pictures with PicList.workflow'), dest)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
function diffFilesAndUpdate(filePath1: string, filePath2: string) {
|
||||
function diffFilesAndUpdate (filePath1: string, filePath2: string) {
|
||||
try {
|
||||
const file1 = fs.existsSync(filePath1) && fs.readFileSync(filePath1)
|
||||
const file2 = fs.existsSync(filePath1) && fs.readFileSync(filePath2)
|
||||
@@ -68,7 +69,7 @@ function diffFilesAndUpdate(filePath1: string, filePath2: string) {
|
||||
/**
|
||||
* 初始化剪贴板生成图片的脚本
|
||||
*/
|
||||
function resolveClipboardImageGenerator() {
|
||||
function resolveClipboardImageGenerator () {
|
||||
const clipboardFiles = getClipboardFiles()
|
||||
if (!fs.pathExistsSync(path.join(CONFIG_DIR, 'windows10.ps1'))) {
|
||||
clipboardFiles.forEach(item => {
|
||||
@@ -80,12 +81,12 @@ function resolveClipboardImageGenerator() {
|
||||
})
|
||||
}
|
||||
|
||||
function getClipboardFiles() {
|
||||
const files = ['/linux.sh', '/mac.applescript', '/windows.ps1', '/windows10.ps1', '/wsl.sh']
|
||||
function getClipboardFiles () {
|
||||
const files = ['linux.sh', 'mac.applescript', 'windows.ps1', 'windows10.ps1', 'wsl.sh']
|
||||
|
||||
return files.map(item => {
|
||||
return {
|
||||
origin: path.join(__static, item),
|
||||
origin: path.join('./resources', item),
|
||||
dest: path.join(CONFIG_DIR, item)
|
||||
}
|
||||
})
|
||||
@@ -95,7 +96,7 @@ function resolveClipboardImageGenerator() {
|
||||
/**
|
||||
* 初始化其他语言文件
|
||||
*/
|
||||
function resolveOtherI18nFiles() {
|
||||
function resolveOtherI18nFiles () {
|
||||
const i18nFolder = path.join(CONFIG_DIR, 'i18n')
|
||||
if (!fs.pathExistsSync(i18nFolder)) {
|
||||
fs.mkdirSync(i18nFolder)
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import crypto from 'crypto'
|
||||
import { clipboard } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import crypto from 'node:crypto'
|
||||
import { EventEmitter } from 'node:events'
|
||||
|
||||
import logger from '@core/picgo/logger'
|
||||
import { clipboard, NativeImage } from 'electron'
|
||||
|
||||
class ClipboardWatcher extends EventEmitter {
|
||||
// eslint-disable-next-line no-undef
|
||||
timer: NodeJS.Timeout | null
|
||||
lastImageHash: string | null
|
||||
|
||||
constructor() {
|
||||
constructor () {
|
||||
super()
|
||||
this.lastImageHash = null
|
||||
this.timer = null
|
||||
}
|
||||
|
||||
startListening(watchDelay = 500) {
|
||||
startListening (watchDelay = 500) {
|
||||
this.stopListening(false)
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
@@ -33,7 +34,7 @@ class ClipboardWatcher extends EventEmitter {
|
||||
logger.info('Start to watch clipboard')
|
||||
}
|
||||
|
||||
stopListening(isLog = true) {
|
||||
stopListening (isLog = true) {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
@@ -42,7 +43,7 @@ class ClipboardWatcher extends EventEmitter {
|
||||
isLog && logger.info('Stop to watch clipboard')
|
||||
}
|
||||
|
||||
getImageHash(image: Electron.NativeImage): string {
|
||||
getImageHash (image: NativeImage): string {
|
||||
const buffer = image.toBitmap()
|
||||
return crypto.createHash('md5').update(buffer).digest('hex')
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import axios from 'axios'
|
||||
import { clipboard, Notification, dialog, Tray } from 'electron'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'node:path'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import logger from '@core/picgo/logger'
|
||||
import axios from 'axios'
|
||||
import { clipboard, dialog, Notification, Tray } from 'electron'
|
||||
import FormData from 'form-data'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import { IShortUrlServer } from '#/types/enum'
|
||||
import { IPrivateShowNotificationOption, IShowMessageBoxResult } from '#/types/types'
|
||||
import { handleUrlEncode } from '#/utils/common'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const getExtension = (fileName: string) => path.extname(fileName).slice(1)
|
||||
|
||||
export const isImage = (fileName: string) =>
|
||||
['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'].includes(getExtension(fileName))
|
||||
|
||||
export let tray: Tray
|
||||
|
||||
export const setTray = (t: Tray) => {
|
||||
@@ -18,7 +25,7 @@ export const setTray = (t: Tray) => {
|
||||
|
||||
export const getTray = () => tray
|
||||
|
||||
export function setTrayToolTip(title: string): void {
|
||||
export function setTrayToolTip (title: string): void {
|
||||
if (tray) {
|
||||
tray.setToolTip(title)
|
||||
}
|
||||
@@ -64,7 +71,7 @@ export const showNotification = (
|
||||
}
|
||||
|
||||
export const showMessageBox = (options: any) => {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
return new Promise<IShowMessageBoxResult>(resolve => {
|
||||
dialog.showMessageBox(options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import axios from 'axios'
|
||||
import crypto from 'crypto'
|
||||
import http, { AgentOptions } from 'http'
|
||||
import https from 'https'
|
||||
import path from 'path'
|
||||
import { ISftpPlistConfig } from 'piclist'
|
||||
import querystring from 'querystring'
|
||||
import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3'
|
||||
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
||||
import crypto from 'node:crypto'
|
||||
import http, { AgentOptions } from 'node:http'
|
||||
import https from 'node:https'
|
||||
import path from 'node:path'
|
||||
import querystring from 'node:querystring'
|
||||
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
import { DeleteObjectCommand, S3Client, S3ClientConfig } from '@aws-sdk/client-s3'
|
||||
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
||||
import axios from 'axios'
|
||||
import { ISftpPlistConfig } from 'piclist'
|
||||
|
||||
import { IObj, IStringKeyMap } from '#/types/types'
|
||||
import { getAgent } from '~/manage/utils/common'
|
||||
import SSHClient from '~/utils/sshClient'
|
||||
|
||||
interface DogecloudTokenFull {
|
||||
Credentials: {
|
||||
@@ -32,7 +34,7 @@ const dogeRegionMap: IStringKeyMap = {
|
||||
'ap-chengdu': '3'
|
||||
}
|
||||
|
||||
async function dogecloudApi(
|
||||
async function dogecloudApi (
|
||||
apiPath: string,
|
||||
data = {},
|
||||
jsonMode: boolean = false,
|
||||
@@ -65,7 +67,7 @@ async function dogecloudApi(
|
||||
}
|
||||
}
|
||||
|
||||
async function getDogeToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
||||
async function getDogeToken (accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
||||
try {
|
||||
const data = await dogecloudApi(
|
||||
'/auth/tmp_token.json',
|
||||
@@ -84,7 +86,7 @@ async function getDogeToken(accessKey: string, secretKey: string): Promise<IObj
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||
export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||
try {
|
||||
const {
|
||||
url: rawUrl,
|
||||
@@ -121,21 +123,21 @@ export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode:
|
||||
const extraOptions = sslEnabled ? { rejectUnauthorized: !!rejectUnauthorized } : {}
|
||||
const handler = sslEnabled
|
||||
? new NodeHttpHandler({
|
||||
httpsAgent: agent.https
|
||||
? agent.https
|
||||
: new https.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
httpsAgent: agent.https
|
||||
? agent.https
|
||||
: new https.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
: new NodeHttpHandler({
|
||||
httpAgent: agent.http
|
||||
? agent.http
|
||||
: new http.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
httpAgent: agent.http
|
||||
? agent.http
|
||||
: new http.Agent({
|
||||
...commonOptions,
|
||||
...extraOptions
|
||||
})
|
||||
})
|
||||
const s3Options: S3ClientConfig = {
|
||||
credentials: {
|
||||
accessKeyId: accessKeyID,
|
||||
@@ -181,14 +183,14 @@ export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode:
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeFileFromDogeInMain(configMap: IStringKeyMap) {
|
||||
export async function removeFileFromDogeInMain (configMap: IStringKeyMap) {
|
||||
try {
|
||||
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)
|
||||
const newConfigMap = { ...configMap }
|
||||
newConfigMap.config = {
|
||||
...newConfigMap.config,
|
||||
accessKeyID: token.Credentials?.accessKeyId,
|
||||
@@ -205,7 +207,7 @@ export async function removeFileFromDogeInMain(configMap: IStringKeyMap) {
|
||||
}
|
||||
}
|
||||
|
||||
function createHuaweiAuthorization(
|
||||
function createHuaweiAuthorization (
|
||||
bucketName: string,
|
||||
path: string,
|
||||
fileName: string,
|
||||
@@ -218,7 +220,7 @@ function createHuaweiAuthorization(
|
||||
return `OBS ${accessKey}:${singature}`
|
||||
}
|
||||
|
||||
export async function removeFileFromHuaweiInMain(configMap: IStringKeyMap) {
|
||||
export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) {
|
||||
const { fileName, config } = configMap
|
||||
const { accessKeyId, accessKeySecret, bucketName, endpoint } = config
|
||||
let path = config.path || '/'
|
||||
@@ -244,7 +246,7 @@ export async function removeFileFromHuaweiInMain(configMap: IStringKeyMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeFileFromSFTPInMain(config: ISftpPlistConfig, fileName: string) {
|
||||
export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileName: string) {
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
|
||||
8
src/main/utils/deleteLog.ts
Normal file
8
src/main/utils/deleteLog.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const deleteLog = (fileName?: string, type?: string, isSuccess = true, msg?: string) => {
|
||||
console.log(`Delete ${fileName} on ${type} ${isSuccess ? 'success' : 'failed'}, message: ${msg || ''}`)
|
||||
}
|
||||
|
||||
export const deleteFailedLog = (fileName: string, type: string, error: any) => {
|
||||
deleteLog(fileName, type, false)
|
||||
console.error(error)
|
||||
}
|
||||
80
src/main/utils/digestAuth.ts
Normal file
80
src/main/utils/digestAuth.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
import { IStringKeyMap } from '#/types/types'
|
||||
const AUTH_KEY_VALUE_RE = /(\w+)=["']?([^'"]{1,10000})["']?/
|
||||
let NC = 0
|
||||
const NC_PAD = '00000000'
|
||||
|
||||
function md5 (text: crypto.BinaryLike) {
|
||||
return crypto.createHash('md5').update(text).digest('hex')
|
||||
}
|
||||
|
||||
export function digestAuthHeader (
|
||||
method: string,
|
||||
uri: string,
|
||||
wwwAuthenticate: string,
|
||||
username: string,
|
||||
password: string
|
||||
) {
|
||||
const parts = wwwAuthenticate.split(',')
|
||||
const opts = {} as IStringKeyMap
|
||||
for (const i of parts) {
|
||||
const m = AUTH_KEY_VALUE_RE.exec(i)
|
||||
if (m) {
|
||||
opts[m[1]] = m[2].replace(/["']/g, '')
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.realm || !opts.nonce) {
|
||||
return ''
|
||||
}
|
||||
|
||||
let qop = opts.qop || ''
|
||||
|
||||
const userpassArray = [username, password]
|
||||
|
||||
let nc = String(++NC)
|
||||
nc = NC_PAD.substring(nc.length) + nc
|
||||
const cnonce = crypto.randomBytes(8).toString('hex')
|
||||
|
||||
const ha1 = md5(userpassArray[0] + ':' + opts.realm + ':' + userpassArray[1])
|
||||
const ha2 = md5(method.toUpperCase() + ':' + uri)
|
||||
let s = ha1 + ':' + opts.nonce
|
||||
if (qop) {
|
||||
qop = qop.split(',')[0]
|
||||
s += ':' + nc + ':' + cnonce + ':' + qop
|
||||
}
|
||||
s += ':' + ha2
|
||||
const response = md5(s)
|
||||
let authstring =
|
||||
'Digest username="' +
|
||||
userpassArray[0] +
|
||||
'", realm="' +
|
||||
opts.realm +
|
||||
'", nonce="' +
|
||||
opts.nonce +
|
||||
'", uri="' +
|
||||
uri +
|
||||
'", response="' +
|
||||
response +
|
||||
'"'
|
||||
if (opts.opaque) {
|
||||
authstring += ', opaque="' + opts.opaque + '"'
|
||||
}
|
||||
if (qop) {
|
||||
authstring += ', qop=' + qop + ', nc=' + nc + ', cnonce="' + cnonce + '"'
|
||||
}
|
||||
return authstring
|
||||
}
|
||||
|
||||
export async function getAuthHeader (method: string, host: string, uri: string, username: string, password: string) {
|
||||
try {
|
||||
await axios.get(`${host}${uri}`)
|
||||
} catch (error: any) {
|
||||
if (error.response.status === 401 && error.response.headers['www-authenticate']) {
|
||||
return digestAuthHeader(method, uri, error.response.headers['www-authenticate'], username, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
// fork from https://github.com/sindresorhus/macos-version
|
||||
// cause I can't change it to common-js module
|
||||
|
||||
import fs from 'fs'
|
||||
import process from 'process'
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
|
||||
import semver from 'semver'
|
||||
|
||||
export const isMacOS = process.platform === 'darwin'
|
||||
@@ -21,7 +22,7 @@ const parseVersion = (plist: string) => {
|
||||
return matches[1].replace('10.16', '11')
|
||||
}
|
||||
|
||||
export function macOSVersion(): string {
|
||||
export function macOSVersion (): string {
|
||||
if (!isMacOS) return ''
|
||||
|
||||
if (!version) {
|
||||
@@ -42,7 +43,7 @@ if (process.env.NODE_ENV === 'test') {
|
||||
macOSVersion._parseVersion = parseVersion
|
||||
}
|
||||
|
||||
export function isMacOSVersion(semverRange: string) {
|
||||
export function isMacOSVersion (semverRange: string) {
|
||||
if (!isMacOS) {
|
||||
return false
|
||||
}
|
||||
@@ -52,7 +53,7 @@ export function isMacOSVersion(semverRange: string) {
|
||||
return semver.satisfies(macOSVersion(), clean(semverRange))
|
||||
}
|
||||
|
||||
export function isMacOSVersionGreaterThanOrEqualTo(version: string) {
|
||||
export function isMacOSVersionGreaterThanOrEqualTo (version: string) {
|
||||
if (!isMacOS) {
|
||||
return false
|
||||
}
|
||||
@@ -62,7 +63,7 @@ export function isMacOSVersionGreaterThanOrEqualTo(version: string) {
|
||||
return semver.gte(macOSVersion(), clean(version))
|
||||
}
|
||||
|
||||
export function assertMacOSVersion(semverRange: string) {
|
||||
export function assertMacOSVersion (semverRange: string) {
|
||||
semverRange = semverRange.replace('10.16', '11')
|
||||
|
||||
if (!isMacOSVersion(semverRange)) {
|
||||
@@ -70,7 +71,7 @@ export function assertMacOSVersion(semverRange: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function assertMacOSVersionGreaterThanOrEqualTo(version: string) {
|
||||
export function assertMacOSVersionGreaterThanOrEqualTo (version: string) {
|
||||
version = version.replace('10.16', '11')
|
||||
|
||||
if (!isMacOSVersionGreaterThanOrEqualTo(version)) {
|
||||
@@ -78,7 +79,7 @@ export function assertMacOSVersionGreaterThanOrEqualTo(version: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function assertMacOS() {
|
||||
export function assertMacOS () {
|
||||
if (!isMacOS) {
|
||||
throw new Error('Requires macOS')
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { IPicBedType } from '#/types/types'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const getPicBeds = () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { Logger } from 'piclist'
|
||||
|
||||
import { isUrl } from '#/utils/common'
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import db from '@core/datastore'
|
||||
|
||||
import { i18nManager } from '~/i18n'
|
||||
|
||||
import { II18nLanguage } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { i18nManager } from '~/i18n'
|
||||
|
||||
export const initI18n = () => {
|
||||
const currentLanguage = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user