From 0feca7bef27a73faa8f1c60748bf878a19bdd52f Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:51:51 +0800 Subject: [PATCH] :bug: Fix(custom): fix delete api --- src/main/apis/app/uploader/apis.ts | 472 ++++++++++++++--------------- src/main/server/routerManager.ts | 468 ++++++++++++++-------------- 2 files changed, 470 insertions(+), 470 deletions(-) diff --git a/src/main/apis/app/uploader/apis.ts b/src/main/apis/app/uploader/apis.ts index 67f0afdc..fbf21a4b 100644 --- a/src/main/apis/app/uploader/apis.ts +++ b/src/main/apis/app/uploader/apis.ts @@ -1,236 +1,236 @@ -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 { Notification, WebContents } from 'electron' -import fs from 'fs-extra' -import { cloneDeep } from 'lodash-es' -import type { IPicGo } from 'piclist' - -import type { IFileWithPath, ImgInfo, IStringKeyMap, IUploadOption } from '#/types/types' -import { T as $t } from '~/i18n/index' -import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common' -import { configPaths } from '~/utils/configPaths' -import { IPasteStyle, IWindowList } from '~/utils/enum' -import { changeCurrentUploader } from '~/utils/handleUploaderConfig' -import pasteTemplate from '~/utils/pasteTemplate' - -const handleClipboardUploading = async (): Promise => { - const useBuiltinClipboard = - db.get(configPaths.settings.useBuiltinClipboard) === undefined - ? true - : !!db.get(configPaths.settings.useBuiltinClipboard) - const win = windowManager.getAvailableWindow() - if (useBuiltinClipboard) { - return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboard() - } - return await uploader.setWebContents(win!.webContents).upload() -} - -const handleClipboardUploadingReturnCtx = async (img?: IUploadOption, skipProcess = false): Promise => { - const useBuiltinClipboard = - db.get(configPaths.settings.useBuiltinClipboard) === undefined - ? true - : !!db.get(configPaths.settings.useBuiltinClipboard) - const win = windowManager.getAvailableWindow() - if (useBuiltinClipboard) { - return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboardReturnCtx(img, skipProcess) - } - return await uploader.setWebContents(win!.webContents).uploadReturnCtx(img, skipProcess) -} - -export const uploadClipboardFiles = async (): Promise => { - const { needRestore, ctx } = await handleSecondaryUpload(undefined, undefined, 'clipboard') - let img: ImgInfo[] | false = false - if (needRestore) { - const res = await handleClipboardUploadingReturnCtx(ctx ? ctx.processedInput : undefined, true) - img = res ? res.output : false - } else { - img = await handleClipboardUploading() - } - if (img !== false) { - if (img.length > 0) { - const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) - const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN - const [pastedText, shortUrl] = await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)) - img[0].shortUrl = shortUrl - handleCopyUrl(pastedText) - 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 || img[0].imgUrl! - // icon: img[0].imgUrl - }) - setTimeout(() => { - notification.show() - }, 100) - } - await GalleryDB.getInstance().insert(img[0]) - // trayWindow just be created in mac/windows, not in linux - trayWindow?.webContents?.send('clipboardFiles', []) - trayWindow?.webContents?.send('uploadFiles', img) - if (windowManager.has(IWindowList.SETTING_WINDOW)) { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') - } - return { - url: handleUrlEncodeWithSetting(img[0].imgUrl as string), - fullResult: img[0] - } - } else { - const notification = new Notification({ - title: $t('UPLOAD_FAILED'), - body: $t('TIPS_UPLOAD_NOT_PICTURES') - }) - notification.show() - return { - url: '', - fullResult: {} - } - } - } else { - return { - url: '', - fullResult: {} - } - } -} - -export const uploadChoosedFiles = async ( - webContents: WebContents, - files: IFileWithPath[] -): Promise => { - const input = files.map(item => item.path) - const rawInput = cloneDeep(input) - const { needRestore, ctx } = await handleSecondaryUpload(webContents, input) - let imgs: ImgInfo[] | false = false - if (needRestore) { - const res = await uploader.setWebContents(webContents).uploadReturnCtx(ctx ? ctx.processedInput : input, true) - imgs = res ? res.output : false - } else { - imgs = await uploader.setWebContents(webContents).upload(input) - } - const result = [] - if (imgs !== false) { - const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN - const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false - const pasteText: string[] = [] - for (let i = 0; i < imgs.length; i++) { - if (deleteLocalFile) { - fs.remove(rawInput[i]) - .then(() => { - picgo.log.info(`delete local file: ${rawInput[i]}`) - }) - .catch((err: Error) => { - picgo.log.error(err) - }) - } - 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! - // icon: files[i].path - }) - setTimeout(() => { - notification.show() - }, i * 100) - } - await GalleryDB.getInstance().insert(imgs[i]) - result.push({ - url: handleUrlEncodeWithSetting(imgs[i].imgUrl!), - fullResult: imgs[i] - }) - } - handleCopyUrl(pasteText.join('\n')) - // trayWindow just be created in mac/windows, not in linux - windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles', imgs) - if (windowManager.has(IWindowList.SETTING_WINDOW)) { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') - } - return result - } else { - return [] - } -} - -export const handleSecondaryUpload = async ( - webContents?: WebContents, - input?: string[], - uploadType: 'clipboard' | 'file' | 'tray' = 'file' -): Promise<{ needRestore: boolean; ctx: IPicGo | false }> => { - const enableSecondUploader = db.get(configPaths.settings.enableSecondUploader) || false - let currentPicBedType = '' - let currentPicBedConfig = {} as IStringKeyMap - let currentPicBedConfigId = '' - let needRestore = false - let ctx: IPicGo | false = false - if (enableSecondUploader) { - const secondUploader = db.get(configPaths.picBed.secondUploader) - const secondUploaderConfig = db.get(configPaths.picBed.secondUploaderConfig) - const secondUploaderId = db.get(configPaths.picBed.secondUploaderId) - const currentPicBed = db.get('picBed') || ({} as IStringKeyMap) - currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms' - currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap) - currentPicBedConfigId = currentPicBedConfig._id - if ( - secondUploader === currentPicBedType && - secondUploaderConfig._configName === currentPicBedConfig._configName && - secondUploaderId === currentPicBedConfigId - ) { - picgo.log.info('second uploader is the same as current uploader') - } else { - needRestore = true - let secondImgs: ImgInfo[] | false = false - changeCurrentUploader(secondUploader, secondUploaderConfig, secondUploaderId) - if (uploadType === 'clipboard') { - ctx = await handleClipboardUploadingReturnCtx(undefined) - } else { - ctx = await uploader.setWebContents(webContents!).uploadReturnCtx(input) - } - secondImgs = ctx ? ctx.output : false - if (secondImgs !== false) { - const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) - if (uploadType === 'clipboard') { - if (secondImgs.length > 0) { - await GalleryDB.getInstance().insert(secondImgs[0]) - trayWindow?.webContents?.send('clipboardFiles', []) - trayWindow?.webContents?.send('uploadFiles', secondImgs) - } - } else { - for (const secondImgsItem of secondImgs) { - await GalleryDB.getInstance().insert(secondImgsItem) - } - if (uploadType === 'tray') { - trayWindow?.webContents?.send('dragFiles', secondImgs) - } else { - trayWindow?.webContents?.send('uploadFiles', secondImgs) - } - } - if (windowManager.has(IWindowList.SETTING_WINDOW) && uploadType !== 'tray') { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') - } - } - } - } - if (needRestore) { - changeCurrentUploader(currentPicBedType, currentPicBedConfig, currentPicBedConfigId) - } - return { - needRestore, - ctx - } -} +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 { Notification, WebContents } from 'electron' +import fs from 'fs-extra' +import { cloneDeep } from 'lodash-es' +import type { IPicGo } from 'piclist' + +import type { IFileWithPath, ImgInfo, IStringKeyMap, IUploadOption } from '#/types/types' +import { T as $t } from '~/i18n/index' +import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common' +import { configPaths } from '~/utils/configPaths' +import { IPasteStyle, IWindowList } from '~/utils/enum' +import { changeCurrentUploader } from '~/utils/handleUploaderConfig' +import pasteTemplate from '~/utils/pasteTemplate' + +const handleClipboardUploading = async (): Promise => { + const useBuiltinClipboard = + db.get(configPaths.settings.useBuiltinClipboard) === undefined + ? true + : !!db.get(configPaths.settings.useBuiltinClipboard) + const win = windowManager.getAvailableWindow() + if (useBuiltinClipboard) { + return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboard() + } + return await uploader.setWebContents(win!.webContents).upload() +} + +const handleClipboardUploadingReturnCtx = async (img?: IUploadOption, skipProcess = false): Promise => { + const useBuiltinClipboard = + db.get(configPaths.settings.useBuiltinClipboard) === undefined + ? true + : !!db.get(configPaths.settings.useBuiltinClipboard) + const win = windowManager.getAvailableWindow() + if (useBuiltinClipboard) { + return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboardReturnCtx(img, skipProcess) + } + return await uploader.setWebContents(win!.webContents).uploadReturnCtx(img, skipProcess) +} + +export const uploadClipboardFiles = async (): Promise => { + const { needRestore, ctx } = await handleSecondaryUpload(undefined, undefined, 'clipboard') + let img: ImgInfo[] | false = false + if (needRestore) { + const res = await handleClipboardUploadingReturnCtx(ctx ? ctx.processedInput : undefined, true) + img = res ? res.output : false + } else { + img = await handleClipboardUploading() + } + if (img !== false) { + if (img.length > 0) { + const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) + const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + const [pastedText, shortUrl] = await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)) + img[0].shortUrl = shortUrl + handleCopyUrl(pastedText) + 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 || img[0].imgUrl! + // icon: img[0].imgUrl + }) + setTimeout(() => { + notification.show() + }, 100) + } + const inserted = await GalleryDB.getInstance().insert(img[0]) + // trayWindow just be created in mac/windows, not in linux + trayWindow?.webContents?.send('clipboardFiles', []) + trayWindow?.webContents?.send('uploadFiles', img) + if (windowManager.has(IWindowList.SETTING_WINDOW)) { + windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') + } + return { + url: handleUrlEncodeWithSetting(inserted.imgUrl as string), + fullResult: inserted + } + } else { + const notification = new Notification({ + title: $t('UPLOAD_FAILED'), + body: $t('TIPS_UPLOAD_NOT_PICTURES') + }) + notification.show() + return { + url: '', + fullResult: {} + } + } + } else { + return { + url: '', + fullResult: {} + } + } +} + +export const uploadChoosedFiles = async ( + webContents: WebContents, + files: IFileWithPath[] +): Promise => { + const input = files.map(item => item.path) + const rawInput = cloneDeep(input) + const { needRestore, ctx } = await handleSecondaryUpload(webContents, input) + let imgs: ImgInfo[] | false = false + if (needRestore) { + const res = await uploader.setWebContents(webContents).uploadReturnCtx(ctx ? ctx.processedInput : input, true) + imgs = res ? res.output : false + } else { + imgs = await uploader.setWebContents(webContents).upload(input) + } + const result = [] + if (imgs !== false) { + const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false + const pasteText: string[] = [] + for (let i = 0; i < imgs.length; i++) { + if (deleteLocalFile) { + fs.remove(rawInput[i]) + .then(() => { + picgo.log.info(`delete local file: ${rawInput[i]}`) + }) + .catch((err: Error) => { + picgo.log.error(err) + }) + } + 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! + // icon: files[i].path + }) + setTimeout(() => { + notification.show() + }, i * 100) + } + const inserted = await GalleryDB.getInstance().insert(imgs[i]) + result.push({ + url: handleUrlEncodeWithSetting(inserted.imgUrl!), + fullResult: inserted + }) + } + handleCopyUrl(pasteText.join('\n')) + // trayWindow just be created in mac/windows, not in linux + windowManager.get(IWindowList.TRAY_WINDOW)?.webContents?.send('uploadFiles', imgs) + if (windowManager.has(IWindowList.SETTING_WINDOW)) { + windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') + } + return result + } else { + return [] + } +} + +export const handleSecondaryUpload = async ( + webContents?: WebContents, + input?: string[], + uploadType: 'clipboard' | 'file' | 'tray' = 'file' +): Promise<{ needRestore: boolean; ctx: IPicGo | false }> => { + const enableSecondUploader = db.get(configPaths.settings.enableSecondUploader) || false + let currentPicBedType = '' + let currentPicBedConfig = {} as IStringKeyMap + let currentPicBedConfigId = '' + let needRestore = false + let ctx: IPicGo | false = false + if (enableSecondUploader) { + const secondUploader = db.get(configPaths.picBed.secondUploader) + const secondUploaderConfig = db.get(configPaths.picBed.secondUploaderConfig) + const secondUploaderId = db.get(configPaths.picBed.secondUploaderId) + const currentPicBed = db.get('picBed') || ({} as IStringKeyMap) + currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms' + currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap) + currentPicBedConfigId = currentPicBedConfig._id + if ( + secondUploader === currentPicBedType && + secondUploaderConfig._configName === currentPicBedConfig._configName && + secondUploaderId === currentPicBedConfigId + ) { + picgo.log.info('second uploader is the same as current uploader') + } else { + needRestore = true + let secondImgs: ImgInfo[] | false = false + changeCurrentUploader(secondUploader, secondUploaderConfig, secondUploaderId) + if (uploadType === 'clipboard') { + ctx = await handleClipboardUploadingReturnCtx(undefined) + } else { + ctx = await uploader.setWebContents(webContents!).uploadReturnCtx(input) + } + secondImgs = ctx ? ctx.output : false + if (secondImgs !== false) { + const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) + if (uploadType === 'clipboard') { + if (secondImgs.length > 0) { + await GalleryDB.getInstance().insert(secondImgs[0]) + trayWindow?.webContents?.send('clipboardFiles', []) + trayWindow?.webContents?.send('uploadFiles', secondImgs) + } + } else { + for (const secondImgsItem of secondImgs) { + await GalleryDB.getInstance().insert(secondImgsItem) + } + if (uploadType === 'tray') { + trayWindow?.webContents?.send('dragFiles', secondImgs) + } else { + trayWindow?.webContents?.send('uploadFiles', secondImgs) + } + } + if (windowManager.has(IWindowList.SETTING_WINDOW) && uploadType !== 'tray') { + windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') + } + } + } + } + if (needRestore) { + changeCurrentUploader(currentPicBedType, currentPicBedConfig, currentPicBedConfigId) + } + return { + needRestore, + ctx + } +} diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index db95b678..2b61d058 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -1,234 +1,234 @@ -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 { 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 type { IHttpResponse, IStringKeyMap } from '#/types/types' -import { markdownContent } from '~/server/apiDoc' -import router from '~/server/router' -import { deleteChoosedFiles, handleResponse } from '~/server/utils' -import { AESHelper } from '~/utils/aesHelper' -import { configPaths } from '~/utils/configPaths' -import { changeCurrentUploader } from '~/utils/handleUploaderConfig' - -const appPath = app.getPath('userData') -const serverTempDir = path.join(appPath, 'serverTemp') - -const STORE_PATH = dbPathDir() -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 }) { - response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) - const htmlContent = marked(markdownContent) - response.write(htmlContent) - response.end() -} - -router.get('/', responseForGet) -router.get('/upload', responseForGet) - -router.post( - '/upload', - async ({ - response, - list = [], - urlparams - }: { - response: IHttpResponse - list?: string[] - urlparams?: URLSearchParams - }): Promise => { - try { - const picbed = urlparams?.get('picbed') - const passedKey = urlparams?.get('key') - const serverKey = picgo.getConfig(configPaths.settings.serverKey) || '' - const useShortUrl = picgo.getConfig(configPaths.settings.useShortUrl) - if (serverKey && passedKey !== serverKey) { - handleResponse({ - response, - body: { - success: false, - message: 'server key is uncorrect' - } - }) - return - } - let currentPicBedType = '' - let currentPicBedConfig = {} as IStringKeyMap - let currentPicBedConfigId = '' - let needRestore = false - if (picbed) { - const currentPicBed = picgo.getConfig('picBed') || ({} as IStringKeyMap) - currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms' - currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap) - currentPicBedConfigId = currentPicBedConfig._id - const configName = urlparams?.get('configName') || currentPicBed[picbed]?._configName - if (picbed === currentPicBedType && configName === currentPicBedConfig._configName) { - // do nothing - } else { - needRestore = true - const picBeds = picgo.getConfig('uploader') - const currentPicBedList = picBeds?.[picbed]?.configList - if (currentPicBedList) { - const currentConfig = currentPicBedList?.find((item: any) => item._configName === configName) - if (currentConfig) { - changeCurrentUploader(picbed, currentConfig, currentConfig._id) - } - } - } - } - if (list.length === 0) { - // upload with clipboard - logger.info('[PicList Server] upload clipboard file') - const result = await uploadClipboardFiles() - const res = useShortUrl ? result.fullResult.shortUrl || result.url : result.url - const fullResult = result.fullResult - fullResult.imgUrl = useShortUrl ? fullResult.shortUrl || fullResult.imgUrl : fullResult.imgUrl - logger.info('[PicList Server] upload result:', res) - if (res) { - const treatedFullResult = { - isEncrypted: 1, - EncryptedData: new AESHelper().encrypt(JSON.stringify(fullResult)), - ...fullResult - } - delete treatedFullResult.config - handleResponse({ - response, - body: { - success: true, - result: [res], - fullResult: [treatedFullResult] - } - }) - } else { - handleResponse({ - response, - body: { - success: false, - message: errorMessage - } - }) - } - } else { - logger.info('[PicList Server] upload files in list') - // upload with files - const pathList = list.map(item => { - return { - path: item - } - }) - const win = windowManager.getAvailableWindow() - const result = await uploadChoosedFiles(win.webContents, pathList) - const res = result.map(item => { - return useShortUrl ? item.fullResult.shortUrl || item.url : item.url - }) - const fullResult = result.map((item: any) => { - const treatedItem = { - isEncrypted: 1, - EncryptedData: new AESHelper().encrypt(JSON.stringify(item.fullResult)), - ...item.fullResult - } - delete treatedItem.config - treatedItem.imgUrl = useShortUrl ? treatedItem.shortUrl || treatedItem.imgUrl : treatedItem.imgUrl - return treatedItem - }) - logger.info('[PicList Server] upload result', res.join(' ; ')) - if (res.length) { - handleResponse({ - response, - body: { - success: true, - result: res, - fullResult - } - }) - } else { - handleResponse({ - response, - body: { - success: false, - message: errorMessage - } - }) - } - } - fs.emptyDirSync(serverTempDir) - if (needRestore) { - changeCurrentUploader(currentPicBedType, currentPicBedConfig, currentPicBedConfigId) - } - } catch (err: any) { - logger.error(err) - handleResponse({ - response, - body: { - success: false, - message: errorMessage - } - }) - } - } -) - -router.post( - '/delete', - async ({ response, list = [] }: { response: IHttpResponse; list?: IStringKeyMap[] }): Promise => { - if (list.length === 0) { - handleResponse({ - response, - body: { - success: false, - message: 'no file to delete' - } - }) - return - } - try { - const aesHelper = new AESHelper() - const treatList = list.map(item => { - if (!item.isEncrypted) return item - return JSON.parse(aesHelper.decrypt(item.EncryptedData)) - }) - const result = await deleteChoosedFiles(treatList) - const successCount = result.filter(item => item).length - const failCount = result.length - successCount - handleResponse({ - response, - body: { - success: !!successCount, - message: successCount ? `delete success: ${successCount}, fail: ${failCount}` : deleteErrorMessage - } - }) - } catch (err: any) { - logger.error(err) - handleResponse({ - response, - body: { - success: false, - message: deleteErrorMessage - } - }) - } - } -) - -router.any('/heartbeat', async ({ response }: { response: IHttpResponse }) => { - handleResponse({ - response, - body: { - success: true, - result: 'alive' - } - }) -}) - -export default router +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 { 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 type { IHttpResponse, IStringKeyMap } from '#/types/types' +import { markdownContent } from '~/server/apiDoc' +import router from '~/server/router' +import { deleteChoosedFiles, handleResponse } from '~/server/utils' +import { AESHelper } from '~/utils/aesHelper' +import { configPaths } from '~/utils/configPaths' +import { changeCurrentUploader } from '~/utils/handleUploaderConfig' + +const appPath = app.getPath('userData') +const serverTempDir = path.join(appPath, 'serverTemp') + +const STORE_PATH = dbPathDir() +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 }) { + response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) + const htmlContent = marked(markdownContent) + response.write(htmlContent) + response.end() +} + +router.get('/', responseForGet) +router.get('/upload', responseForGet) + +router.post( + '/upload', + async ({ + response, + list = [], + urlparams + }: { + response: IHttpResponse + list?: string[] + urlparams?: URLSearchParams + }): Promise => { + try { + const picbed = urlparams?.get('picbed') + const passedKey = urlparams?.get('key') + const serverKey = picgo.getConfig(configPaths.settings.serverKey) || '' + const useShortUrl = picgo.getConfig(configPaths.settings.useShortUrl) + if (serverKey && passedKey !== serverKey) { + handleResponse({ + response, + body: { + success: false, + message: 'server key is uncorrect' + } + }) + return + } + let currentPicBedType = '' + let currentPicBedConfig = {} as IStringKeyMap + let currentPicBedConfigId = '' + let needRestore = false + if (picbed) { + const currentPicBed = picgo.getConfig('picBed') || ({} as IStringKeyMap) + currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms' + currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap) + currentPicBedConfigId = currentPicBedConfig._id + const configName = urlparams?.get('configName') || currentPicBed[picbed]?._configName + if (picbed === currentPicBedType && configName === currentPicBedConfig._configName) { + // do nothing + } else { + needRestore = true + const picBeds = picgo.getConfig('uploader') + const currentPicBedList = picBeds?.[picbed]?.configList + if (currentPicBedList) { + const currentConfig = currentPicBedList?.find((item: any) => item._configName === configName) + if (currentConfig) { + changeCurrentUploader(picbed, currentConfig, currentConfig._id) + } + } + } + } + if (list.length === 0) { + // upload with clipboard + logger.info('[PicList Server] upload clipboard file') + const result = await uploadClipboardFiles() + const res = useShortUrl ? result.fullResult.shortUrl || result.url : result.url + const fullResult = result.fullResult + fullResult.imgUrl = useShortUrl ? fullResult.shortUrl || fullResult.imgUrl : fullResult.imgUrl + logger.info('[PicList Server] upload result:', res) + if (res) { + const treatedFullResult = { + isEncrypted: 1, + EncryptedData: new AESHelper().encrypt(JSON.stringify(fullResult)), + ...fullResult + } + delete treatedFullResult.config + handleResponse({ + response, + body: { + success: true, + result: [res], + fullResult: [treatedFullResult] + } + }) + } else { + handleResponse({ + response, + body: { + success: false, + message: errorMessage + } + }) + } + } else { + logger.info('[PicList Server] upload files in list') + // upload with files + const pathList = list.map(item => { + return { + path: item + } + }) + const win = windowManager.getAvailableWindow() + const result = await uploadChoosedFiles(win.webContents, pathList) + const res = result.map(item => { + return useShortUrl ? item.fullResult.shortUrl || item.url : item.url + }) + const fullResult = result.map((item: any) => { + const treatedItem = { + isEncrypted: 1, + EncryptedData: new AESHelper().encrypt(JSON.stringify(item.fullResult)), + ...item.fullResult + } + delete treatedItem.config + treatedItem.imgUrl = useShortUrl ? treatedItem.shortUrl || treatedItem.imgUrl : treatedItem.imgUrl + return treatedItem + }) + logger.info('[PicList Server] upload result', res.join(' ; ')) + if (res.length) { + handleResponse({ + response, + body: { + success: true, + result: res, + fullResult + } + }) + } else { + handleResponse({ + response, + body: { + success: false, + message: errorMessage + } + }) + } + } + fs.emptyDirSync(serverTempDir) + if (needRestore) { + changeCurrentUploader(currentPicBedType, currentPicBedConfig, currentPicBedConfigId) + } + } catch (err: any) { + logger.error(err) + handleResponse({ + response, + body: { + success: false, + message: errorMessage + } + }) + } + } +) + +router.post( + '/delete', + async ({ response, list = [] }: { response: IHttpResponse; list?: IStringKeyMap[] }): Promise => { + if (list.length === 0) { + handleResponse({ + response, + body: { + success: false, + message: 'no file to delete' + } + }) + return + } + try { + const aesHelper = new AESHelper() + const treatList = list.map(item => { + if (!item.isEncrypted) return item + return JSON.parse(aesHelper.decrypt(item.EncryptedData)) + }) + const result = await deleteChoosedFiles(treatList) + const successCount = result.filter(item => item).length + const failCount = result.length - successCount + handleResponse({ + response, + body: { + success: !!successCount, + message: successCount ? `delete success: ${successCount}, fail: ${failCount}` : deleteErrorMessage + } + }) + } catch (err: any) { + logger.error(err) + handleResponse({ + response, + body: { + success: false, + message: deleteErrorMessage + } + }) + } + } +) + +router.any('/heartbeat', async ({ response }: { response: IHttpResponse }) => { + handleResponse({ + response, + body: { + success: true, + result: 'alive' + } + }) +}) + +export default router