mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-06-03 14:50:57 +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
|
||||
|
||||
Reference in New Issue
Block a user