From 20f6accfbd235d3f8df455ef9e6a65a541945a4b Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Thu, 14 Mar 2024 13:19:49 +0800 Subject: [PATCH 01/23] :pencil: Docs(custom): update tg group link --- README.md | 4 ++-- README_cn.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fec8557..259b4ba 100644 --- a/README.md +++ b/README.md @@ -231,9 +231,9 @@ If you need to build it yourself, you can start building with `yarn run build`. ## Communication group -If you have any questions, you can join the WeChat group for communication. +If you have any questions, you can join the TG group for communication. -![wechat](https://pichoro.msq.pub/wechat.png) +[PicList TG Group](https://t.me/+rq8y7wsj7Pg5ZTg1) ## License diff --git a/README_cn.md b/README_cn.md index 8b308d8..e7e1b40 100644 --- a/README_cn.md +++ b/README_cn.md @@ -221,9 +221,9 @@ brew uninstall piclist ## 交流群 -如果有任何问题,可以加入微信群进行交流. +如果有任何问题,可以加入TG群进行交流. -![wechat](https://pichoro.msq.pub/wechat.png) +[PicList交流群](https://t.me/+rq8y7wsj7Pg5ZTg1) ## License From 5cd34a6abedab0861270650c432f0a18ee153c92 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Mon, 18 Mar 2024 11:52:57 +0800 Subject: [PATCH 02/23] :pencil: Docs(custom): add tg group qr code --- README.md | 2 ++ README_cn.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 259b4ba..6d80d87 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,8 @@ If you have any questions, you can join the TG group for communication. [PicList TG Group](https://t.me/+rq8y7wsj7Pg5ZTg1) +![tg](https://pichoro.msq.pub/wechat.png) + ## License This project is open source under the MIT license. Welcome everyone to use and contribute code. Thank you for the open source spirit of the original author Molunerfinn. diff --git a/README_cn.md b/README_cn.md index e7e1b40..e7d174b 100644 --- a/README_cn.md +++ b/README_cn.md @@ -225,6 +225,8 @@ brew uninstall piclist [PicList交流群](https://t.me/+rq8y7wsj7Pg5ZTg1) +![tg](https://pichoro.msq.pub/wechat.png) + ## License 本项目基于MIT协议开源,欢迎大家使用和贡献代码,感谢原作者Molunerfinn的开源精神。 From 872a1a799e0f48282bfd3edf5a72fee2791da636 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Mon, 18 Mar 2024 17:22:04 +0800 Subject: [PATCH 03/23] :sparkles: Feature(custom): add api doc for 36677 port --- src/main/server/apiDoc.ts | 78 +++++++++++++++++++++++++++++++++++++++ src/main/server/index.ts | 7 ++++ 2 files changed, 85 insertions(+) create mode 100644 src/main/server/apiDoc.ts diff --git a/src/main/server/apiDoc.ts b/src/main/server/apiDoc.ts new file mode 100644 index 0000000..a1e1515 --- /dev/null +++ b/src/main/server/apiDoc.ts @@ -0,0 +1,78 @@ +export const markdownContent = ` +## 内置Server的使用 + +PicList内置了一个小型的服务器,用于接收来自其他应用或其他主机的HTTP请求来上传图片。 + +默认监听地址:\`0.0.0.0\`,默认监听端口:\`36677\` + +### 接口鉴权 + +当将接口暴露于公网时,为了防止恶意上传,PicList提供了接口鉴权功能。 + +![202310102349225](https://assets.piclist.cn/image/202310102349225.webp) + +发送请求时添加URL查询参数\`key\`即可,例如:\`http://xxx:36677/upload?key=xxx\`。 + +### 表单上传 + +- 请求方法: \`POST\` +- url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) +- 请求body: \`multipart/form-data\`格式,key任选,value为图片文件 + +即可上传。 + +### HTTP调用上传剪贴板图片 + +- 请求方法: \`POST\` +- url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) +- 请求body: \`{list: ['xxx.jpg']}\` 必须是JSON格式 + +即可上传。 + +::: tip Tip +PicList支持通过设置\`picbed\`和\`configName\`两个URL查询参数来指定上传图床和配置文件。例如: +\`http://127.0.0.1:36677/upload?picbed=aws-s3&configName=piclist-test\` +该配置将会使用\`aws-s3\`图床,并且使用\`piclist-test\`配置文件。 +::: + +返回的数据: + +\`\`\`json +{ + "success": true, // or false + "result": ["url"] +} +\`\`\` + +### HTTP调用上传具体路径图片 + +- method: \`POST\` +- url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) +- request body: \`{list: ['xxx.jpg']}\` 必须是JSON格式 + +返回的数据: + +\`\`\`json +{ + "success": true, // or false + "result": ["url"] +} +\`\`\` + +### HTTP调用删除图片 + +- method: \`POST\` +- url: \`http://127.0.0.1:36677/delete\` (此处以默认配置为例) +- request body: \`{list: [{xx:xx}]}\` 必须是JSON格式 + +list中的每一项都是一个对象,由上传接口返回数据的\`fullResult\`字段组成。 + +返回的数据: + +\`\`\`json +{ + "success": true, // or false + "message": xxx +} +\`\`\` +` diff --git a/src/main/server/index.ts b/src/main/server/index.ts index d092d6f..a45387e 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -11,6 +11,8 @@ import multer from 'multer' import { app } from 'electron' import path from 'path' import fs from 'fs-extra' +import { marked } from 'marked' +import { markdownContent } from './apiDoc' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') @@ -141,6 +143,11 @@ class Server { }) } } + } else if (request.method === 'GET') { + response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) + const htmlContent = marked(markdownContent) + response.write(htmlContent) + response.end() } else { logger.warn(`[PicList Server] don't support [${request.method}] method`) response.statusCode = 404 From 922f04f672c4f56505e46f16e53d72dce3284270 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 19 Mar 2024 17:40:22 +0800 Subject: [PATCH 04/23] :sparkles: Feature(custom): update api doc --- src/main/server/apiDoc.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/server/apiDoc.ts b/src/main/server/apiDoc.ts index a1e1515..d683b55 100644 --- a/src/main/server/apiDoc.ts +++ b/src/main/server/apiDoc.ts @@ -13,7 +13,7 @@ PicList内置了一个小型的服务器,用于接收来自其他应用或其 发送请求时添加URL查询参数\`key\`即可,例如:\`http://xxx:36677/upload?key=xxx\`。 -### 表单上传 +### 表单上传 - 请求方法: \`POST\` - url: \`http://127.0.0.1:36677/upload\` (此处以默认配置为例) @@ -29,11 +29,12 @@ PicList内置了一个小型的服务器,用于接收来自其他应用或其 即可上传。 -::: tip Tip +### configName和picbed参数 + PicList支持通过设置\`picbed\`和\`configName\`两个URL查询参数来指定上传图床和配置文件。例如: \`http://127.0.0.1:36677/upload?picbed=aws-s3&configName=piclist-test\` + 该配置将会使用\`aws-s3\`图床,并且使用\`piclist-test\`配置文件。 -::: 返回的数据: From 475b85eb1f11b972aa29abb07435525ef1e8348d Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Wed, 20 Mar 2024 13:31:36 +0800 Subject: [PATCH 05/23] :sparkles: Feature(custom): refactor upload and delete api --- src/main/server/index.ts | 194 +++++++++++++++---------------- src/main/server/routerManager.ts | 41 +++---- 2 files changed, 110 insertions(+), 125 deletions(-) diff --git a/src/main/server/index.ts b/src/main/server/index.ts index a45387e..2eff0b9 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -14,6 +14,8 @@ import fs from 'fs-extra' import { marked } from 'marked' import { markdownContent } from './apiDoc' +const DEFAULT_PORT = 36677 +const DEFAULT_HOST = '0.0.0.0' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') @@ -44,117 +46,117 @@ class Server { private config: IServerConfig constructor () { - let config = picgo.getConfig('settings.server') - const result = this.checkIfConfigIsValid(config) - if (result) { - this.config = config - if (this.config.host === '127.0.0.1') { - this.config.host = '0.0.0.0' - } - } else { - config = { - port: 36677, - host: '0.0.0.0', - enable: true - } - this.config = config - picgo.saveConfig({ - 'settings.server': config - }) - } + this.config = this.getConfigWithDefaults() this.httpServer = http.createServer(this.handleRequest) } - private checkIfConfigIsValid (config: IObj | undefined) { + getConfigWithDefaults () { + let config = picgo.getConfig('settings.server') + if (!this.isValidConfig(config)) { + config = { port: DEFAULT_PORT, host: DEFAULT_HOST, enable: true } + picgo.saveConfig({ 'settings.server': config }) + } + config.host = config.host === '127.0.0.1' ? '0.0.0.0' : config.host + return config + } + + private isValidConfig (config: IObj | undefined) { return config && config.port && config.host && (config.enable !== undefined) } private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => { - if (request.method === 'OPTIONS') { - handleResponse({ - response - }) - return + switch (request.method) { + case 'OPTIONS': + handleResponse({ response }) + break + case 'POST': + this.handlePostRequest(request, response) + break + case 'GET': + this.handleGetRequest(request, response) + break + default: + logger.warn(`[PicList Server] don't support [${request.method}] method`) + response.statusCode = 405 + response.end() } + } - if (request.method === 'POST') { - const [url, query] = request.url!.split('?') - if (!routers.getHandler(url!)) { - logger.warn(`[PicList Server] don't support [${url}] url`) - handleResponse({ - response, - statusCode: 404, - body: { - success: false + private handlePostRequest = (request: http.IncomingMessage, response: http.ServerResponse) => { + const [url, query] = request.url!.split('?') + if (!routers.getHandler(url!)) { + logger.warn(`[PicList Server] don't support [${url}] url`) + handleResponse({ + response, + statusCode: 404, + body: { + success: false + } + }) + } else { + if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) { + // @ts-ignore + uploadMulter.any()(request, response, (err: any) => { + if (err) { + logger.info('[PicList Server]', err) + return handleResponse({ + response, + body: { + success: false, + message: 'Error processing formData' + } + }) } - }) - } else { - if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) { // @ts-ignore - uploadMulter.any()(request, response, (err: any) => { - if (err) { - logger.info('[PicList Server]', err) - return handleResponse({ - response, - body: { - success: false, - message: 'Error processing formData' - } - }) - } - // @ts-ignore - const list = request.files.map(file => file.path) - logger.info('[PicList Server] get a formData request') - const handler = routers.getHandler(url)?.handler - if (handler) { - handler({ - list, - response, - urlparams: query ? new URLSearchParams(query) : undefined - }) - } - }) - } else { - let body: string = '' - let postObj: IObj - request.on('data', chunk => { - body += chunk - }) - request.on('end', () => { - try { - postObj = (body === '') ? {} : JSON.parse(body) - } catch (err: any) { - logger.error('[PicList Server]', err) - return handleResponse({ - response, - body: { - success: false, - message: 'Not sending data in JSON format' - } - }) - } - logger.info('[PicList Server] get the request', body) - const handler = routers.getHandler(url!)?.handler - handler!({ - ...postObj, + const list = request.files.map(file => file.path) + logger.info('[PicList Server] get a formData request') + const handler = routers.getHandler(url)?.handler + if (handler) { + handler({ + list, response, urlparams: query ? new URLSearchParams(query) : undefined }) + } + }) + } else { + let body: string = '' + let postObj: IObj + request.on('data', chunk => { + body += chunk + }) + request.on('end', () => { + try { + postObj = (body === '') ? {} : JSON.parse(body) + } catch (err: any) { + logger.error('[PicList Server]', err) + return handleResponse({ + response, + body: { + success: false, + message: 'Not sending data in JSON format' + } + }) + } + logger.info('[PicList Server] get the request', body) + const handler = routers.getHandler(url!)?.handler + handler!({ + ...postObj, + response, + urlparams: query ? new URLSearchParams(query) : undefined }) - } + }) } - } else if (request.method === 'GET') { - response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) - const htmlContent = marked(markdownContent) - response.write(htmlContent) - response.end() - } else { - logger.warn(`[PicList Server] don't support [${request.method}] method`) - response.statusCode = 404 - response.end() } } + private handleGetRequest = (_request: http.IncomingMessage, response: http.ServerResponse) => { + response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) + const htmlContent = marked(markdownContent) + response.write(htmlContent) + response.end() + } + // port as string is a bug private listen = (port: number | string) => { logger.info(`[PicList Server] is listening at ${port} of ${this.config.host}`) @@ -173,12 +175,13 @@ class Server { // to solve the auto number problem this.listen((port as number) + 1) } + } else { + logger.error('[PicList Server]', err) } }) } startup () { - console.log('startup', this.config.enable) if (this.config.enable) { this.listen(this.config.port) } @@ -192,11 +195,8 @@ class Server { } restart () { - this.config = picgo.getConfig('settings.server') - if (this.config.host === '127.0.0.1') { - this.config.host = '0.0.0.0' - } this.shutdown() + this.config = this.getConfigWithDefaults() this.startup() } } diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index bde6231..a843ae3 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -32,7 +32,6 @@ router.post('/upload', async ({ urlparams?: URLSearchParams }): Promise => { try { - const picbed = urlparams?.get('picbed') const passedKey = urlparams?.get('key') const serverKey = picgo.getConfig('settings.serverKey') || '' if (serverKey && passedKey !== serverKey) { @@ -40,11 +39,12 @@ router.post('/upload', async ({ response, body: { success: false, - message: 'server key is not correct' + message: 'server key is uncorrect' } }) return } + const picbed = urlparams?.get('picbed') let currentPicBedType = '' let currentPicBedConfig = {} as IStringKeyMap let currentPicBedConfigId = '' @@ -176,36 +176,21 @@ router.post('/delete', async ({ return } try { - // 区分是否是加密的数据,如果不是直接传入list,如果是,解密后再传入 const treatList = list.map(item => { - if (item.isEncrypted) { - const aesHelper = new AESHelper() - const data = aesHelper.decrypt(item.EncryptedData) - return JSON.parse(data) - } else { - return item - } + if (!item.isEncrypted) return item + const aesHelper = new AESHelper() + return JSON.parse(aesHelper.decrypt(item.EncryptedData)) }) const result = await deleteChoosedFiles(treatList) const successCount = result.filter(item => item).length - const failCount = result.filter(item => !item).length - if (successCount) { - handleResponse({ - response, - body: { - success: true, - message: `delete success: ${successCount}, fail: ${failCount}` - } - }) - } else { - handleResponse({ - response, - body: { - success: false, - message: deleteErrorMessage - } - }) - } + 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({ From cf0450f29849e2702d6150cba1be36a487db6550 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Thu, 21 Mar 2024 13:04:31 +0800 Subject: [PATCH 06/23] :bug: Fix(custom): fix error handling in server startup --- src/main/server/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 2eff0b9..6de1839 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -164,10 +164,11 @@ class Server { port = parseInt(port, 10) } this.httpServer.listen(port, this.config.host).on('error', async (err: ErrnoException) => { - if (err.errno === 'EADDRINUSE') { + if (err.code === 'EADDRINUSE') { try { // make sure the system has a PicGo Server instance await axios.post(ensureHTTPLink(`${this.config.host}:${port}/heartbeat`)) + logger.info(`[PicList Server] server is already running at ${port}`) this.shutdown(true) } catch (e) { logger.warn(`[PicList Server] ${port} is busy, trying with port ${(port as number) + 1}`) From 348a118cd36bbf09827f2ab4068c4204a8f7a45a Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Thu, 21 Mar 2024 13:29:30 +0800 Subject: [PATCH 07/23] :arrow_up: Upgrade(custom): update axios --- package.json | 4 ++-- yarn.lock | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 16c4ac4..61ce4a8 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@smithy/node-http-handler": "^2.1.6", "@videojs-player/vue": "^1.0.0", "ali-oss": "^6.18.1", - "axios": "^1.6.2", + "axios": "^1.6.8", "compare-versions": "^4.1.3", "core-js": "^3.33.3", "cos-nodejs-sdk-v5": "^2.12.5", @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.7.12", + "piclist": "^1.7.13", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index d2f365d..0eb4c73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5079,12 +5079,12 @@ axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" -axios@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" - integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== +axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -8382,11 +8382,16 @@ follow-redirects@^1.0.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd" integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A== -follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.15.1: +follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.1: version "1.15.2" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -12392,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.7.12: - version "1.7.12" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.7.12.tgz#e26e234452dfa9b08c8e2e03896e195b8f74ce88" - integrity sha512-UZa3tHrhk3iJxXs8kTJyYJFsWW+o0JO2xXL7Xb0wMKkmXsRs2VyjD02cLkhCiYL/lkTmv1tcWWyQYAw3gcJshA== +piclist@^1.7.13: + version "1.7.13" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.7.13.tgz#27ef4d4a5a7a6313fa7a102d1bf5857a18a2b74b" + integrity sha512-yDFfXnbitzzaTkOO6Me/l2osc4njGtrXwV/Mw8d+wWkWwfvRxqWp5tenTdXODxWIf8qF947H7UcuedSyHfloSA== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" @@ -12403,7 +12408,7 @@ piclist@^1.7.12: "@picgo/i18n" "^1.0.0" "@picgo/store" "^2.1.0" "@smithy/node-http-handler" "2.1.6" - axios "^1.6.2" + axios "^1.6.8" chalk "^2.4.1" commander "^8.1.0" cross-spawn "^7.0.3" From e4c1c7f8ad2d7897fd4a5b2561651612d0106d0c Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Fri, 22 Mar 2024 14:12:09 +0800 Subject: [PATCH 08/23] :sparkles: Feature(custom): add log for image processing --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 61ce4a8..1686f50 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.7.13", + "piclist": "^1.7.14", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 0eb4c73..2a45741 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12397,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.7.13: - version "1.7.13" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.7.13.tgz#27ef4d4a5a7a6313fa7a102d1bf5857a18a2b74b" - integrity sha512-yDFfXnbitzzaTkOO6Me/l2osc4njGtrXwV/Mw8d+wWkWwfvRxqWp5tenTdXODxWIf8qF947H7UcuedSyHfloSA== +piclist@^1.7.14: + version "1.7.14" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.7.14.tgz#53d4c9942df56d93a1b0b9cd3fdaccf51898ebcd" + integrity sha512-sItCg7FAe2Eo+vlWw+vCrzKGExUYPb7wj7GBe38lg1xmj6Q0aCyElXS5eWHtTvFiECMHtKfayhGDyW59h8ifrQ== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" From 8e5e6d4f2cd8c1cc7d83f90d33f558f3912bf045 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Sat, 23 Mar 2024 13:05:57 +0800 Subject: [PATCH 09/23] :bug: Fix(custom): fix image watermark bug --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1686f50..4a394dc 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.7.14", + "piclist": "^1.8.0", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 2a45741..06d014c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12397,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.7.14: - version "1.7.14" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.7.14.tgz#53d4c9942df56d93a1b0b9cd3fdaccf51898ebcd" - integrity sha512-sItCg7FAe2Eo+vlWw+vCrzKGExUYPb7wj7GBe38lg1xmj6Q0aCyElXS5eWHtTvFiECMHtKfayhGDyW59h8ifrQ== +piclist@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.0.tgz#c17fe0c6c9ccb1d307e76aed32fd2769784623ac" + integrity sha512-hgsymm/qH7W+BSLX6+T8SLPMiJ/K13ex0wNdWW7VO+MqJWVl2Q0i+RDFxPhrrH6OG/0hAqnXCNhphjH714xu5A== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" From f73badd525090fb563041a4b7806a8189d1f9d02 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Mon, 25 Mar 2024 22:24:13 +0800 Subject: [PATCH 10/23] :pencil: Docs(custom): prepare for 2.8.1 --- currentVersion.md | 15 +++++---------- currentVersion_en.md | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/currentVersion.md b/currentVersion.md index 304d923..34e8528 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,15 +1,10 @@ -💥 Breaking Change - -- 由于[C1N短网址](https://www.c1n.cn/)的收费策略修改,预期可使用API的团队版在1000元/年,作者承担费用压力较大,现在修改为自己提供API token -- 原软件内置提供的c1n token即时失效 - ✨ Features -- 新增对cf workers 短链接项目[xyTom/Url-Shorten-Worker](https://github.com/xyTom/Url-Shorten-Worker)的支持 -- 现在相册页面支持强制无视缓存刷新 -- 优化了阿里云图床对无扩增名文件的上传处理 -- 优化了短网址功能的错误日志记录 +- 现在浏览器访问36677上传端口会显示API文档 +- 当使用了不支持的http请求方法时,现在服务接口返回405错误码 +- 现在图片处理过程会记录错误日志 🐛 Bug Fixes -- 修复了buffer上传时的文件名错误 +- 修复了未设置水印文字时,图片水印功能无法生效的bug +- 修复了server端口被占用时,端口探测功能没有正常发挥作用的问题 diff --git a/currentVersion_en.md b/currentVersion_en.md index 63e2cd3..5118192 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,15 +1,10 @@ -💥 Breaking Change - -- Due to the upcoming changes in the charging policy of [C1N short URL](https://www.c1n.cn/), the expected team version that can use the API will be around 1000 yuan/year, and the author bears a greater pressure on the cost. Now it is changed to provide the API token by yourself -- The c1n token provided by the original software is immediately invalid - ✨ Features -- Added support for the cf workers short link project [xyTom/Url-Shorten-Worker](https://github.com/xyTom/Url-Shorten-Worker) -- Now the album page supports forced cache refresh -- Optimized the upload processing of files without extension names for Aliyun image bed -- Optimized error log recording for short URL function +- Now the API document will be displayed when the browser accesses the 36677 upload port +- When an unsupported http request method is used, the service interface now returns a 405 error code +- Now the image processing process will record error logs 🐛 Bug Fixes -- Fixed the file name error when uploading buffer +- Fix the bug that the image watermark function does not take effect when the watermark text is not set +- Fixed the problem that the port detection function did not work properly when the server port was occupied From bf8d7b490329a286fb6c157dc00e8df9b92b0e54 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 26 Mar 2024 13:38:43 +0800 Subject: [PATCH 11/23] :sparkles: Feature(custom): change buildin rename md5 calculate ISSUES CLOSED: #178 --- currentVersion.md | 1 + currentVersion_en.md | 1 + package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/currentVersion.md b/currentVersion.md index 34e8528..4ecebb4 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,5 +1,6 @@ ✨ Features +- 现在高级重命名{md5}会使用文件内容的md5值,而不是文件名的md5值 - 现在浏览器访问36677上传端口会显示API文档 - 当使用了不支持的http请求方法时,现在服务接口返回405错误码 - 现在图片处理过程会记录错误日志 diff --git a/currentVersion_en.md b/currentVersion_en.md index 5118192..4f71e59 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,5 +1,6 @@ ✨ Features +- Now the advanced renaming {md5} will use the md5 value of the file content, not the file name - Now the API document will be displayed when the browser accesses the 36677 upload port - When an unsupported http request method is used, the service interface now returns a 405 error code - Now the image processing process will record error logs diff --git a/package.json b/package.json index 4a394dc..a1b8aec 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.8.0", + "piclist": "^1.8.1", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 06d014c..78c81df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12397,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.0.tgz#c17fe0c6c9ccb1d307e76aed32fd2769784623ac" - integrity sha512-hgsymm/qH7W+BSLX6+T8SLPMiJ/K13ex0wNdWW7VO+MqJWVl2Q0i+RDFxPhrrH6OG/0hAqnXCNhphjH714xu5A== +piclist@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.1.tgz#98f19b68245427d0e035885c4906742bd82cf2e7" + integrity sha512-1MXda5o509Hb7sobKGWBUS1Bs6Azm+3T8ZeARdIiXUNW58sC8yuO64PaHTqHRqe2lyQfEFAmBGz//4MNLFd52A== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" From 07c346a8922c802618a96550f4aa1fc687591cd4 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 26 Mar 2024 20:00:25 +0800 Subject: [PATCH 12/23] :hammer: Refactor(custom): refactor renameFileNameWithCustomString function in common.ts --- src/renderer/manage/utils/common.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/renderer/manage/utils/common.ts b/src/renderer/manage/utils/common.ts index 1e50aa3..e32120f 100644 --- a/src/renderer/manage/utils/common.ts +++ b/src/renderer/manage/utils/common.ts @@ -29,14 +29,25 @@ export function renameFileNameWithRandomString (oldName: string, length: number return `${randomStringGenerator(length)}${path.extname(oldName)}` } +function renameFormatHelper (num: number): string { + return num.toString().length === 1 ? `0${num}` : num.toString() +} + +function getMd5 (input: crypto.BinaryLike): string { + return crypto.createHash('md5').update(input).digest('hex') +} + export function renameFileNameWithCustomString (oldName: string, customFormat: string, affixFileName?: string): string { + const date = new Date() + const year = date.getFullYear().toString() + const fileBaseName = path.basename(oldName, path.extname(oldName)) const conversionMap : {[key: string]: () => string} = { - '{Y}': () => new Date().getFullYear().toString(), - '{y}': () => new Date().getFullYear().toString().slice(2), - '{m}': () => (new Date().getMonth() + 1).toString().length === 1 ? `0${new Date().getMonth() + 1}` : (new Date().getMonth() + 1).toString(), - '{d}': () => new Date().getDate().toString().length === 1 ? `0${new Date().getDate()}` : new Date().getDate().toString(), - '{md5}': () => crypto.createHash('md5').update(path.basename(oldName, path.extname(oldName))).digest('hex'), - '{md5-16}': () => crypto.createHash('md5').update(path.basename(oldName, path.extname(oldName))).digest('hex').slice(0, 16), + '{Y}': () => year, + '{y}': () => year.slice(2), + '{m}': () => renameFormatHelper(date.getMonth() + 1), + '{d}': () => renameFormatHelper(date.getDate()), + '{md5}': () => getMd5(fileBaseName), + '{md5-16}': () => getMd5(fileBaseName).slice(0, 16), '{str-10}': () => randomStringGenerator(10), '{str-20}': () => randomStringGenerator(20), '{filename}': () => affixFileName ? path.basename(affixFileName, path.extname(affixFileName)) : path.basename(oldName, path.extname(oldName)), From 47e5afe96387ea440082ed197a6e506c9782caa5 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Sat, 30 Mar 2024 22:46:51 +0800 Subject: [PATCH 13/23] :hammer: Refactor(custom): refactor server --- src/main/server/index.ts | 30 +++++++++++++++++++----------- src/main/server/router.ts | 30 +++++++++++++++++++++++------- src/main/server/routerManager.ts | 15 ++++++++++++++- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 6de1839..be140a5 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -11,8 +11,6 @@ import multer from 'multer' import { app } from 'electron' import path from 'path' import fs from 'fs-extra' -import { marked } from 'marked' -import { markdownContent } from './apiDoc' const DEFAULT_PORT = 36677 const DEFAULT_HOST = '0.0.0.0' @@ -83,9 +81,9 @@ class Server { } private handlePostRequest = (request: http.IncomingMessage, response: http.ServerResponse) => { - const [url, query] = request.url!.split('?') - if (!routers.getHandler(url!)) { - logger.warn(`[PicList Server] don't support [${url}] url`) + const [url, query] = (request.url || '').split('?') + if (!routers.getHandler(url, 'POST')) { + logger.warn(`[PicList Server] don't support [${url}] endpoint`) handleResponse({ response, statusCode: 404, @@ -110,7 +108,7 @@ class Server { // @ts-ignore const list = request.files.map(file => file.path) logger.info('[PicList Server] get a formData request') - const handler = routers.getHandler(url)?.handler + const handler = routers.getHandler(url!, 'POST')?.handler if (handler) { handler({ list, @@ -139,7 +137,7 @@ class Server { }) } logger.info('[PicList Server] get the request', body) - const handler = routers.getHandler(url!)?.handler + const handler = routers.getHandler(url!, 'POST')?.handler handler!({ ...postObj, response, @@ -151,10 +149,20 @@ class Server { } private handleGetRequest = (_request: http.IncomingMessage, response: http.ServerResponse) => { - response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) - const htmlContent = marked(markdownContent) - response.write(htmlContent) - response.end() + const [url, query] = (_request.url || '').split('?') + if (!routers.getHandler(url, 'GET')) { + console.log(`[PicList Server] don't support [${url}] endpoint`) + response.statusCode = 404 + response.end() + } else { + const handler = routers.getHandler(url, 'GET')?.handler + if (handler) { + handler({ + response, + urlparams: query ? new URLSearchParams(query) : undefined + }) + } + } } // port as string is a bug diff --git a/src/main/server/router.ts b/src/main/server/router.ts index 1f5dc3e..c128386 100644 --- a/src/main/server/router.ts +++ b/src/main/server/router.ts @@ -1,20 +1,36 @@ +type HttpMethod = 'GET' | 'POST' + class Router { - private router = new Map() + private router = new Map>() + + private addRoute (method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void { + if (!this.router.has(url)) { + this.router.set(url, new Map()) + } + this.router.get(url)!.set(method, { handler: callback, urlparams }) + } get (url: string, callback: routeHandler, urlparams?: URLSearchParams): void { - this.router.set(url, { handler: callback, urlparams }) + this.addRoute('GET', url, callback, urlparams) } post (url: string, callback: routeHandler, urlparams?: URLSearchParams): void { - this.router.set(url, { handler: callback, urlparams }) + this.addRoute('POST', url, callback, urlparams) } - getHandler (url: string) { + any (url: string, callback: routeHandler, urlparams?: URLSearchParams): void { + this.addRoute('GET', url, callback, urlparams) + this.addRoute('POST', url, callback, urlparams) + } + + getHandler (url: string, method: HttpMethod) { if (this.router.has(url)) { - return this.router.get(url) - } else { - return null + const methods = this.router.get(url)! + if (methods.has(method)) { + return methods.get(method) + } } + return null } } diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index a843ae3..17d3fa4 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -12,6 +12,9 @@ import { changeCurrentUploader } from '../utils/handleUploaderConfig' import { app } from 'electron' import fs from 'fs-extra' import { AESHelper } from '../utils/aesHelper' +import { marked } from 'marked' +import { markdownContent } from './apiDoc' +import http from 'http' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') @@ -22,6 +25,16 @@ 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 = [], @@ -203,7 +216,7 @@ router.post('/delete', async ({ } }) -router.post('/heartbeat', async ({ +router.any('/heartbeat', async ({ response } : { response: IHttpResponse, From 8ed24cb2b82ed7f13a5a2da6c3035a9f0e0609d8 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Sun, 31 Mar 2024 19:41:35 +0800 Subject: [PATCH 14/23] :construction: WIP(custom): build-in web server for files --- public/i18n/en.yml | 5 +++++ public/i18n/zh-CN.yml | 5 +++++ public/i18n/zh-TW.yml | 5 +++++ src/main/server/webServer/index.ts | 0 src/universal/types/i18n.d.ts | 3 +++ 5 files changed, 18 insertions(+) create mode 100644 src/main/server/webServer/index.ts diff --git a/public/i18n/en.yml b/public/i18n/en.yml index ece9875..0eaa130 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -209,6 +209,11 @@ SETTINGS_ENABLE_SERVER: Enable Server SETTINGS_SET_SERVER_HOST: Set Server Host SETTINGS_SET_SERVER_PORT: Set Server Port SETTINGS_SET_SERVER_KEY: Set Auth Key +SETTINGS_SET_ENABLE_WEB_SERVER: Enable Web Server +SETTINGS_SET_WEB_SERVER_HOST: Set Web Server Host +SETTINGS_SET_WEB_SERVER_PORT: Set Web Server Port +SETTINGS_TIP_PLACEHOLDER_WEB_HOST: Default:127.0.0.1 +SETTINGS_TIP_PLACEHOLDER_WEB_PORT: Default:37777 SETTINGS_TIP_PLACEHOLDER_HOST: Default:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: Default:36677 SETTINGS_TIP_PLACEHOLDER_KEY: This key is used to avoid malicious requests, through urlParams '?key=xxx' to pass diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 8715aed..f1d6306 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -211,6 +211,11 @@ SETTINGS_ENABLE_SERVER: 是否开启Server SETTINGS_SET_SERVER_HOST: 设置监听地址 SETTINGS_SET_SERVER_PORT: 设置监听端口 SETTINGS_SET_SERVER_KEY: 设置鉴权密钥 +SETTINGS_SET_ENABLE_WEB_SERVER: 是否开启Web服务 +SETTINGS_SET_WEB_SERVER_HOST: 设置Web服务监听地址 +SETTINGS_SET_WEB_SERVER_PORT: 设置Web服务监听端口 +SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推荐默认地址:127.0.0.1 +SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推荐默认端口:37777 SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推荐默认端口:36677 SETTINGS_TIP_PLACEHOLDER_KEY: 用于接口鉴权, 通过url参数添加'?key=xxx' diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index a83292c..e552e45 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -209,6 +209,11 @@ SETTINGS_ENABLE_SERVER: 是否開啟Server SETTINGS_SET_SERVER_HOST: 設定監聽地址 SETTINGS_SET_SERVER_PORT: 設定監聽端口 SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰 +SETTINGS_SET_ENABLE_WEB_SERVER: 是否開啟Web服務 +SETTINGS_SET_WEB_SERVER_HOST: 設定Web服務地 +SETTINGS_SET_WEB_SERVER_PORT: 設定Web服務端口 +SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推薦預設地址:127.0.0.1 +SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推薦預設端口:37777 SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推薦預設端口:36677 SETTINGS_TIP_PLACEHOLDER_KEY: 用於接口鑒權, 通過url參數添加'?key=xxx' diff --git a/src/main/server/webServer/index.ts b/src/main/server/webServer/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index 3c5db6d..2a5d59b 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -204,6 +204,9 @@ interface ILocales { SETTINGS_SET_SERVER_HOST: string SETTINGS_SET_SERVER_PORT: string SETTINGS_SET_SERVER_KEY: string + SETTINGS_SET_ENABLE_WEB_SERVER: string + SETTINGS_SET_WEB_SERVER_HOST: string + SETTINGS_SET_WEB_SERVER_PORT: string SETTINGS_TIP_PLACEHOLDER_HOST: string SETTINGS_TIP_PLACEHOLDER_PORT: string SETTINGS_TIP_PLACEHOLDER_KEY: string From ab8b62e661ee218a6a0f5f58155d37baf1f61d25 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 2 Apr 2024 13:29:55 +0800 Subject: [PATCH 15/23] :sparkles: Feature(custom): add buildin web server ISSUES CLOSED: #180 --- public/i18n/en.yml | 3 + public/i18n/zh-CN.yml | 3 + public/i18n/zh-TW.yml | 3 + src/main/events/ipcList.ts | 7 ++ src/main/lifeCycle/index.ts | 2 + src/main/server/webServer/index.ts | 96 ++++++++++++++++++++++ src/renderer/pages/PicGoSetting.vue | 119 +++++++++++++++++++++++++++- src/universal/types/i18n.d.ts | 5 ++ src/universal/types/view.d.ts | 6 +- 9 files changed, 242 insertions(+), 2 deletions(-) diff --git a/public/i18n/en.yml b/public/i18n/en.yml index 0eaa130..543dccb 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -209,9 +209,12 @@ SETTINGS_ENABLE_SERVER: Enable Server SETTINGS_SET_SERVER_HOST: Set Server Host SETTINGS_SET_SERVER_PORT: Set Server Port SETTINGS_SET_SERVER_KEY: Set Auth Key +SETTINGS_SET_WEB_SERVER: Set Web Server +SETTINGS_TIPS_WEB_SERVER_NOTICE: If you don't know what is the web server's function, please read the document, or don't modify the configuration. SETTINGS_SET_ENABLE_WEB_SERVER: Enable Web Server SETTINGS_SET_WEB_SERVER_HOST: Set Web Server Host SETTINGS_SET_WEB_SERVER_PORT: Set Web Server Port +SETTINGS_SET_WEB_SERVER_PATH: Set Web Server Path SETTINGS_TIP_PLACEHOLDER_WEB_HOST: Default:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: Default:37777 SETTINGS_TIP_PLACEHOLDER_HOST: Default:127.0.0.1 diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index f1d6306..38a3cde 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -211,9 +211,12 @@ SETTINGS_ENABLE_SERVER: 是否开启Server SETTINGS_SET_SERVER_HOST: 设置监听地址 SETTINGS_SET_SERVER_PORT: 设置监听端口 SETTINGS_SET_SERVER_KEY: 设置鉴权密钥 +SETTINGS_SET_WEB_SERVER: 设置Web服务 +SETTINGS_TIPS_WEB_SERVER_NOTICE: 如果你不知道Web服务的作用,请阅读文档,或者不用修改配置。 SETTINGS_SET_ENABLE_WEB_SERVER: 是否开启Web服务 SETTINGS_SET_WEB_SERVER_HOST: 设置Web服务监听地址 SETTINGS_SET_WEB_SERVER_PORT: 设置Web服务监听端口 +SETTINGS_SET_WEB_SERVER_PATH: 设置Web服务路径 SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推荐默认端口:37777 SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1 diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index e552e45..48d5e0d 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -209,9 +209,12 @@ SETTINGS_ENABLE_SERVER: 是否開啟Server SETTINGS_SET_SERVER_HOST: 設定監聽地址 SETTINGS_SET_SERVER_PORT: 設定監聽端口 SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰 +SETTINGS_SET_WEB_SERVER: 設定Web服務 +SETTINGS_TIPS_WEB_SERVER_NOTICE: 如果你不知道Web服務的作用,請閱讀文檔,或者不用修改設定。 SETTINGS_SET_ENABLE_WEB_SERVER: 是否開啟Web服務 SETTINGS_SET_WEB_SERVER_HOST: 設定Web服務地 SETTINGS_SET_WEB_SERVER_PORT: 設定Web服務端口 +SETTINGS_SET_WEB_SERVER_PATH: 設定Web服務路徑 SETTINGS_TIP_PLACEHOLDER_WEB_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_WEB_PORT: 推薦預設端口:37777 SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1 diff --git a/src/main/events/ipcList.ts b/src/main/events/ipcList.ts index 10584ba..66f2c45 100644 --- a/src/main/events/ipcList.ts +++ b/src/main/events/ipcList.ts @@ -89,6 +89,7 @@ import SSHClient from '../utils/sshClient' import { ISftpPlistConfig } from 'piclist' import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc' +import webServer from '../server/webServer' const STORE_PATH = app.getPath('userData') @@ -360,6 +361,12 @@ export default { ipcMain.on('updateServer', () => { server.restart() }) + ipcMain.on('stopWebServer', () => { + webServer.stop() + }) + ipcMain.on('restartWebServer', () => { + webServer.restart() + }) ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => { event.sender.openDevTools() }) diff --git a/src/main/lifeCycle/index.ts b/src/main/lifeCycle/index.ts index b045098..48a2919 100644 --- a/src/main/lifeCycle/index.ts +++ b/src/main/lifeCycle/index.ts @@ -45,6 +45,7 @@ import path from 'path' import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' import fs from 'fs-extra' import { startFileServer } from '../fileServer' +import webServer from '../server/webServer' import axios from 'axios' const isDevelopment = process.env.NODE_ENV !== 'production' @@ -179,6 +180,7 @@ class LifeCycle { }) server.startup() startFileServer() + webServer.start() if (process.env.NODE_ENV !== 'development') { handleStartUpFiles(process.argv, process.cwd()) } diff --git a/src/main/server/webServer/index.ts b/src/main/server/webServer/index.ts index e69de29..19bd790 100644 --- a/src/main/server/webServer/index.ts +++ b/src/main/server/webServer/index.ts @@ -0,0 +1,96 @@ +import http from 'http' +import fs from 'fs-extra' +import path from 'path' +import picgo from '@core/picgo' +import logger from '../../apis/core/picgo/logger' + +const defaultPath = process.platform === 'win32' ? 'C:/Users/' : '/' + +function generateDirectoryListingHtml (files: any[], requestPath: any) { + let html = '

Directory Listing

    ' + files.forEach((file: string) => { + html += `
  • ${file}
  • ` + }) + html += '
' + return html +} + +class WebServer { + private server: http.Server + private config: IStringKeyMap + + constructor () { + this.config = this.getConfigWithDefaults() + this.server = this.getServer() + } + + getConfigWithDefaults (): IStringKeyMap { + const enableWebServer = picgo.getConfig('settings.enableWebServer') || false + const webServerHost = picgo.getConfig('settings.webServerHost') || '0.0.0.0' + const webServerPort = picgo.getConfig('settings.webServerPort') || 37777 + const webServerPath = picgo.getConfig('settings.webServerPath') || defaultPath + return { enableWebServer, webServerHost, webServerPort, webServerPath } + } + + getServer (): http.Server { + return http.createServer((req, res) => { + const requestPath = req.url?.split('?')[0] + const filePath = path.join(this.config.webServerPath, decodeURIComponent(requestPath as string)) + + fs.stat(filePath, (err, stats) => { + if (err) { + res.writeHead(404) + res.end('404 Not Found') + return + } + + if (stats.isDirectory()) { + fs.readdir(filePath, (err, files) => { + if (err) { + res.writeHead(500) + res.end('Error listing directory contents') + } else { + res.writeHead(200, { 'Content-Type': 'text/html' }) + res.end(generateDirectoryListingHtml(files, requestPath)) + } + }) + } else { + fs.readFile(filePath, (err, data) => { + if (err) { + res.writeHead(404) + res.end('404 Not Found') + } else { + res.end(data) + } + }) + } + }) + }) + } + + start () { + if (this.config.enableWebServer) { + this.server.listen(this.config.webServerPort, this.config.webServerHost, () => { + logger.info(`Web server is running, http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`) + }).on('error', (err) => { + logger.error(err) + }) + } else { + logger.info('Web server is not enabled') + } + } + + stop () { + this.server.close() + logger.info('Web server is stopped') + } + + restart () { + this.stop() + this.config = this.getConfigWithDefaults() + this.server = this.getServer() + this.start() + } +} + +export default new WebServer() diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 33ebdab..8acfa08 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -654,6 +654,18 @@ {{ $T('SETTINGS_CLICK_TO_SET') }} + + + {{ $T('SETTINGS_CLICK_TO_SET') }} + + @@ -732,6 +744,7 @@ :title="$T('SETTINGS_CUSTOM_LINK_FORMAT')" :modal-append-to-body="false" center + draggable append-to-body >
@@ -1025,6 +1041,7 @@ :modal-append-to-body="false" width="500px" center + draggable append-to-body >
@@ -1187,6 +1205,69 @@ + +
+ {{ $T('SETTINGS_TIPS_WEB_SERVER_NOTICE') }} +
+ + + + + + +
@@ -1300,6 +1382,7 @@ :title="$T('SETTINGS_UP_DOWN_DESC')" :modal-append-to-body="false" center + draggable append-to-body > ({ deleteLocalFile: false, serverKey: '', aesPassword: '', - manualPageOpen: 'browser' + manualPageOpen: 'browser', + enableWebServer: false, + webServerHost: '0.0.0.0', + webServerPort: 37777, + webServerPath: '' }) const languageList = i18nManager.languageList.map(item => ({ @@ -1888,6 +1975,7 @@ const logFileVisible = ref(false) const customLinkVisible = ref(false) const checkUpdateVisible = ref(false) const serverVisible = ref(false) +const webServerVisible = ref(false) const syncVisible = ref(false) const upDownConfigVisible = ref(false) const proxyVisible = ref(false) @@ -2039,6 +2127,10 @@ async function initData () { form.serverKey = settings.serverKey || '' form.aesPassword = settings.aesPassword || 'PicList-aesPassword' form.manualPageOpen = settings.manualPageOpen || 'window' + form.enableWebServer = settings.enableWebServer || false + form.webServerHost = settings.webServerHost || '0.0.0.0' + form.webServerPort = settings.webServerPort || 37777 + form.webServerPath = settings.webServerPath || '' currentLanguage.value = settings.language ?? 'zh-CN' currentStartMode.value = settings.startMode || 'quiet' customLink.value = settings.customLink || '![$fileName]($url)' @@ -2296,6 +2388,31 @@ function cancelCheckVersion () { checkUpdateVisible.value = false } +function handleEnableWebServerChange (val: ICheckBoxValueType) { + saveConfig('settings.enableWebServer', val) +} + +function handleWebServerHostChange (val: string) { + saveConfig('settings.webServerHost', val) +} + +function handleWebServerPortChange (val?: number, oldVal?: number) { + saveConfig('settings.webServerPort', Number(val) || 37777) +} + +function handleWebServerPathChange (val: string) { + saveConfig('settings.webServerPath', val) +} + +function confirmWebServerSetting () { + console.log('confirmWebServerSetting') + if (form.enableWebServer) { + sendToMain('restartWebServer') + } else { + sendToMain('stopWebServer') + } +} + function handleServerKeyChange (val: string) { saveConfig('settings.serverKey', val) } diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index 2a5d59b..08de9b1 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -204,9 +204,14 @@ interface ILocales { SETTINGS_SET_SERVER_HOST: string SETTINGS_SET_SERVER_PORT: string SETTINGS_SET_SERVER_KEY: string + SETTINGS_SET_WEB_SERVER: string + SETTINGS_TIPS_WEB_SERVER_NOTICE: string SETTINGS_SET_ENABLE_WEB_SERVER: string SETTINGS_SET_WEB_SERVER_HOST: string SETTINGS_SET_WEB_SERVER_PORT: string + SETTINGS_SET_WEB_SERVER_PATH: string + SETTINGS_TIP_PLACEHOLDER_WEB_HOST: string + SETTINGS_TIP_PLACEHOLDER_WEB_PORT: string SETTINGS_TIP_PLACEHOLDER_HOST: string SETTINGS_TIP_PLACEHOLDER_PORT: string SETTINGS_TIP_PLACEHOLDER_KEY: string diff --git a/src/universal/types/view.d.ts b/src/universal/types/view.d.ts index 0e0ec2d..a4d875d 100644 --- a/src/universal/types/view.d.ts +++ b/src/universal/types/view.d.ts @@ -32,7 +32,11 @@ interface ISettingForm { deleteLocalFile: boolean, serverKey: string, aesPassword: string, - manualPageOpen: string + manualPageOpen: string, + enableWebServer: boolean, + webServerHost: string, + webServerPort: number, + webServerPath: string, } interface IShortKeyMap { From 75b0c7f5182cece9b35572c2b1908bf197c1b923 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 2 Apr 2024 14:10:49 +0800 Subject: [PATCH 16/23] :sparkles: Feature(custom): exclude svg from watermarker ISSUES CLOSED: #182 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a1b8aec..115c74e 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.8.1", + "piclist": "^1.8.2", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 78c81df..9927545 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12397,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.1.tgz#98f19b68245427d0e035885c4906742bd82cf2e7" - integrity sha512-1MXda5o509Hb7sobKGWBUS1Bs6Azm+3T8ZeARdIiXUNW58sC8yuO64PaHTqHRqe2lyQfEFAmBGz//4MNLFd52A== +piclist@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.2.tgz#d63dfc45d83c9801a905227a1729a84016b18ea8" + integrity sha512-GwhzM1+t01o6BP2VOmwZlOg5oylsPHQ7V+eezVvmll0I7dAtq9xvoalpQ+pFvvnkDmZALjEyPsElR+JLbV5Rwg== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" From 0476346070d6dd526df6d83f959c28daabb6cec4 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Wed, 3 Apr 2024 17:56:09 +0800 Subject: [PATCH 17/23] :pencil: Docs(custom): prepare for 2.81 --- currentVersion.md | 9 ++++++--- currentVersion_en.md | 11 +++++++---- src/main/apis/app/uploader/apis.ts | 1 - src/main/server/index.ts | 2 +- src/renderer/pages/PicGoSetting.vue | 1 - 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/currentVersion.md b/currentVersion.md index 4ecebb4..ef60d1a 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,8 +1,11 @@ ✨ Features -- 现在高级重命名{md5}会使用文件内容的md5值,而不是文件名的md5值 -- 现在浏览器访问36677上传端口会显示API文档 -- 当使用了不支持的http请求方法时,现在服务接口返回405错误码 +- 现在浏览器访问36677端口会显示API文档(支持`/`和`/upload`路径) +- 现在`heartbeat`接口支持`GET`请求 +- 新增内置web服务支持,默认在37777端口开启一个简易web服务器,类似`EasyWebSvr` +- 现在不再对`svg`图片进行水印处理 +- 现在内置高级重命名中`{md5}`使用文件内容而非文件名字符串计算 +- 现在设置界面的弹出窗口支持拖拽调整位置 - 现在图片处理过程会记录错误日志 🐛 Bug Fixes diff --git a/currentVersion_en.md b/currentVersion_en.md index 4f71e59..0e892b4 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,11 +1,14 @@ ✨ Features -- Now the advanced renaming {md5} will use the md5 value of the file content, not the file name -- Now the API document will be displayed when the browser accesses the 36677 upload port -- When an unsupported http request method is used, the service interface now returns a 405 error code +- Now the browser will display the API document when accessing port 36677 (support `/` and `/upload` paths) +- Now the `heartbeat` interface supports `GET` requests +- Added built-in web service support, a simple web server is opened by default on port 37777, similar to `EasyWebSvr` +- Now no longer watermark `svg` images +- Now in the advanced renaming, `{md5}` uses the file content instead of the file name string calculation +- Now the pop-up window of the setting interface supports dragging to adjust the position - Now the image processing process will record error logs 🐛 Bug Fixes -- Fix the bug that the image watermark function does not take effect when the watermark text is not set +- Fixed the bug that the image watermark function cannot take effect when the watermark text is not set - Fixed the problem that the port detection function did not work properly when the server port was occupied diff --git a/src/main/apis/app/uploader/apis.ts b/src/main/apis/app/uploader/apis.ts index 7488446..b33529e 100644 --- a/src/main/apis/app/uploader/apis.ts +++ b/src/main/apis/app/uploader/apis.ts @@ -36,7 +36,6 @@ const handleClipboardUploading = async (): Promise => { export const uploadClipboardFiles = async (): Promise => { const img = await handleClipboardUploading() - console.log(img) if (img !== false) { if (img.length > 0) { const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) diff --git a/src/main/server/index.ts b/src/main/server/index.ts index be140a5..30a0ef3 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -151,7 +151,7 @@ class Server { private handleGetRequest = (_request: http.IncomingMessage, response: http.ServerResponse) => { const [url, query] = (_request.url || '').split('?') if (!routers.getHandler(url, 'GET')) { - console.log(`[PicList Server] don't support [${url}] endpoint`) + logger.info(`[PicList Server] don't support [${url}] endpoint`) response.statusCode = 404 response.end() } else { diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 8acfa08..3ebb9fe 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -2405,7 +2405,6 @@ function handleWebServerPathChange (val: string) { } function confirmWebServerSetting () { - console.log('confirmWebServerSetting') if (form.enableWebServer) { sendToMain('restartWebServer') } else { From aa9e535fd1d8c785cf7d99add54f2e43ed312076 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Thu, 4 Apr 2024 09:10:41 +0800 Subject: [PATCH 18/23] :sparkles: Feature(custom): add u flag for gallery regexp match --- src/renderer/manage/utils/common.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/manage/utils/common.ts b/src/renderer/manage/utils/common.ts index e32120f..06c5543 100644 --- a/src/renderer/manage/utils/common.ts +++ b/src/renderer/manage/utils/common.ts @@ -182,7 +182,7 @@ export const svg = ` export function customStrMatch (str: string, pattern: string) : boolean { if (!str || !pattern) return false try { - const reg = new RegExp(pattern, 'g') + const reg = new RegExp(pattern, 'ug') return reg.test(str) } catch (e) { console.error(e) @@ -195,7 +195,7 @@ export function customStrReplace (str: string, pattern: string, replacement: str replacement = replacement || '' let result = str try { - const reg = new RegExp(pattern, 'g') + const reg = new RegExp(pattern, 'ug') result = str.replace(reg, replacement) result = renameFileNameWithCustomString(result, result, str) } catch (e) { From e6e94723d00176c3e579905e587654c63b1fe897 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Fri, 5 Apr 2024 15:18:20 +0800 Subject: [PATCH 19/23] :sparkles: Feature(custom): optimize webServer --- src/main/server/webServer/index.ts | 110 ++++++++++++++++------------- src/universal/utils/common.ts | 6 ++ 2 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/main/server/webServer/index.ts b/src/main/server/webServer/index.ts index 19bd790..7a6003d 100644 --- a/src/main/server/webServer/index.ts +++ b/src/main/server/webServer/index.ts @@ -3,92 +3,104 @@ import fs from 'fs-extra' import path from 'path' import picgo from '@core/picgo' import logger from '../../apis/core/picgo/logger' +import { encodeFilePath } from '~/universal/utils/common' -const defaultPath = process.platform === 'win32' ? 'C:/Users/' : '/' +const defaultPath = process.platform === 'win32' ? 'C:\\Users' : '/' function generateDirectoryListingHtml (files: any[], requestPath: any) { let html = '

Directory Listing

    ' files.forEach((file: string) => { - html += `
  • ${file}
  • ` + const filePath = path.join(requestPath, file) + html += `
  • ${file}
  • ` }) html += '
' return html } +function serveDirectory (res: http.ServerResponse, filePath: fs.PathLike, requestPath: any) { + fs.readdir(filePath, (err, files) => { + if (err) { + res.writeHead(500) + res.end('Error listing directory contents') + } else { + res.writeHead(200, { 'Content-Type': 'text/html' }) + res.end(generateDirectoryListingHtml(files, requestPath)) + } + }) +} + +function serveFile (res:http.ServerResponse, filePath: fs.PathLike) { + const readStream = fs.createReadStream(filePath) + readStream.pipe(res) + readStream.on('error', () => { + res.writeHead(500) + res.end('Error reading file') + }) +} + class WebServer { - private server: http.Server - private config: IStringKeyMap + private server!: http.Server + private config!: IStringKeyMap constructor () { - this.config = this.getConfigWithDefaults() - this.server = this.getServer() + this.loadConfig() + this.initServer() } - getConfigWithDefaults (): IStringKeyMap { - const enableWebServer = picgo.getConfig('settings.enableWebServer') || false - const webServerHost = picgo.getConfig('settings.webServerHost') || '0.0.0.0' - const webServerPort = picgo.getConfig('settings.webServerPort') || 37777 - const webServerPath = picgo.getConfig('settings.webServerPath') || defaultPath - return { enableWebServer, webServerHost, webServerPort, webServerPath } + loadConfig (): void { + this.config = { + enableWebServer: picgo.getConfig('settings.enableWebServer') || false, + webServerHost: picgo.getConfig('settings.webServerHost') || '0.0.0.0', + webServerPort: picgo.getConfig('settings.webServerPort') || 37777, + webServerPath: picgo.getConfig('settings.webServerPath') || defaultPath + } } - getServer (): http.Server { - return http.createServer((req, res) => { + initServer (): void { + this.server = http.createServer((req, res) => { const requestPath = req.url?.split('?')[0] - const filePath = path.join(this.config.webServerPath, decodeURIComponent(requestPath as string)) - - fs.stat(filePath, (err, stats) => { - if (err) { - res.writeHead(404) - res.end('404 Not Found') - return - } + const filePath = path.join(this.config.webServerPath, decodeURIComponent(requestPath || '')) + try { + const stats = fs.statSync(filePath) if (stats.isDirectory()) { - fs.readdir(filePath, (err, files) => { - if (err) { - res.writeHead(500) - res.end('Error listing directory contents') - } else { - res.writeHead(200, { 'Content-Type': 'text/html' }) - res.end(generateDirectoryListingHtml(files, requestPath)) - } - }) + serveDirectory(res, filePath, requestPath) } else { - fs.readFile(filePath, (err, data) => { - if (err) { - res.writeHead(404) - res.end('404 Not Found') - } else { - res.end(data) - } - }) + serveFile(res, filePath) } - }) + } catch (err) { + res.writeHead(404) + res.end('404 Not Found') + } }) } start () { if (this.config.enableWebServer) { - this.server.listen(this.config.webServerPort, this.config.webServerHost, () => { - logger.info(`Web server is running, http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`) - }).on('error', (err) => { - logger.error(err) - }) + this.server + .listen( + this.config.webServerPort === 36699 ? 37777 : this.config.webServerPort, + this.config.webServerHost, () => { + logger.info(`Web server is running at http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`) + }) + .on('error', (err) => { + logger.error(err) + }) } else { logger.info('Web server is not enabled') } } stop () { - this.server.close() - logger.info('Web server is stopped') + this.server.close(() => { + logger.info('Web server is stopped') + }) } restart () { this.stop() - this.config = this.getConfigWithDefaults() - this.server = this.getServer() + this.loadConfig() + this.initServer() this.start() } } diff --git a/src/universal/utils/common.ts b/src/universal/utils/common.ts index 40f381d..94f516b 100644 --- a/src/universal/utils/common.ts +++ b/src/universal/utils/common.ts @@ -52,3 +52,9 @@ export function safeSliceF (str:string, total: number) { } return result } + +export function encodeFilePath (filePath: string) { + filePath = filePath.replace(/\\/g, '/') + const parts = filePath.split('/') + return parts.map(encodeURIComponent).join('/') +} From 762913c372b021cb164bc0f3a6d553cba19d258f Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Fri, 5 Apr 2024 20:34:14 +0800 Subject: [PATCH 20/23] :bug: Fix(custom): fix compatiblity with typora --- src/main/server/index.ts | 15 +++++++++++++-- src/main/server/routerManager.ts | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 30a0ef3..158a12b 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -92,6 +92,17 @@ class Server { } }) } else { + const remoteAddress = request.socket.remoteAddress || 'unknown' + logger.info('[PicList Server] get a POST request from IP:', remoteAddress) + let urlSP = query ? new URLSearchParams(query) : undefined + if (remoteAddress === '::1' || remoteAddress === '127.0.0.1') { + const serverKey = picgo.getConfig('settings.serverKey') || '' + if (urlSP) { + urlSP.set('key', serverKey) + } else { + urlSP = new URLSearchParams('key=' + serverKey) + } + } if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) { // @ts-ignore uploadMulter.any()(request, response, (err: any) => { @@ -113,7 +124,7 @@ class Server { handler({ list, response, - urlparams: query ? new URLSearchParams(query) : undefined + urlparams: urlSP }) } }) @@ -141,7 +152,7 @@ class Server { handler!({ ...postObj, response, - urlparams: query ? new URLSearchParams(query) : undefined + urlparams: urlSP }) }) } diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index 17d3fa4..43dba2c 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -46,7 +46,7 @@ router.post('/upload', async ({ }): Promise => { try { const passedKey = urlparams?.get('key') - const serverKey = picgo.getConfig('settings.serverKey') || '' + const serverKey = picgo.getConfig('settings.serverKey') || '' if (serverKey && passedKey !== serverKey) { handleResponse({ response, From ae40a1615ecc661745a9511df2b97a298a05812f Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Fri, 5 Apr 2024 22:02:01 +0800 Subject: [PATCH 21/23] :sparkles: Feature(custom): optimize the processing of upload path and custom url --- currentVersion.md | 12 ++++++++++-- currentVersion_en.md | 20 ++++++++++++++------ package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/currentVersion.md b/currentVersion.md index ef60d1a..6ea379e 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,14 +1,22 @@ ✨ Features -- 现在浏览器访问36677端口会显示API文档(支持`/`和`/upload`路径) -- 现在`heartbeat`接口支持`GET`请求 +- 图床功能优化 + - 优化了github等图床路径设置为`/`时的处理 + - 现在会自动对上传路径开始和结尾的`/`进行处理,大部分图床不再强制要求路径以`/`结尾 + - 优化了对自定义网站的处理,现在会自动去除最后多余的`/` +- 上传服务器 + - 现在来自本机的上传请求不再进行鉴权 + - 现在浏览器访问36677端口会显示API文档(支持`/`和`/upload`路径) + - 现在`heartbeat`接口支持`GET`请求 - 新增内置web服务支持,默认在37777端口开启一个简易web服务器,类似`EasyWebSvr` - 现在不再对`svg`图片进行水印处理 - 现在内置高级重命名中`{md5}`使用文件内容而非文件名字符串计算 +- 现在相册URL修改功能的正则匹配增加了`u`修饰符 - 现在设置界面的弹出窗口支持拖拽调整位置 - 现在图片处理过程会记录错误日志 🐛 Bug Fixes +- 修复了设置了服务器鉴权密钥后,Typora上传失败的问题 - 修复了未设置水印文字时,图片水印功能无法生效的bug - 修复了server端口被占用时,端口探测功能没有正常发挥作用的问题 diff --git a/currentVersion_en.md b/currentVersion_en.md index 0e892b4..0f2b858 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,14 +1,22 @@ ✨ Features -- Now the browser will display the API document when accessing port 36677 (support `/` and `/upload` paths) -- Now the `heartbeat` interface supports `GET` requests +- Picture bed function optimization + - Optimized the processing of the path set to `/` for github and other picture beds + - Now it will automatically process the `/` at the beginning and end of the upload path, and most picture beds no longer require the path to end with `/` + - Optimized the processing of custom websites, now it will automatically remove the extra `/` at the end +- Upload server + - Now the upload requests from the local machine no longer require authentication + - Now browsing port 36677 will display the API document (support `/` and `/upload` paths) + - Now the `heartbeat` interface supports `GET` requests - Added built-in web service support, a simple web server is opened by default on port 37777, similar to `EasyWebSvr` - Now no longer watermark `svg` images -- Now in the advanced renaming, `{md5}` uses the file content instead of the file name string calculation -- Now the pop-up window of the setting interface supports dragging to adjust the position +- Now the `{md5}` in the advanced rename uses the file content instead of the file name string for calculation +- The regular expression matching of the album URL modification function now adds the `u` modifier +- Now the pop-up window in the settings interface supports dragging to adjust the position - Now the image processing process will record error logs 🐛 Bug Fixes -- Fixed the bug that the image watermark function cannot take effect when the watermark text is not set -- Fixed the problem that the port detection function did not work properly when the server port was occupied +- Fix the problem of Typora upload failure after setting the server authentication key +- Fixed the bug that the image watermark function does not take effect when the watermark text is not set +- Fixed the problem that the port detection function does not work properly when the server port is occupied diff --git a/package.json b/package.json index 115c74e..6fe7734 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.8.2", + "piclist": "^1.8.3", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.0", "proxy-agent": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 9927545..40af689 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12397,10 +12397,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.2.tgz#d63dfc45d83c9801a905227a1729a84016b18ea8" - integrity sha512-GwhzM1+t01o6BP2VOmwZlOg5oylsPHQ7V+eezVvmll0I7dAtq9xvoalpQ+pFvvnkDmZALjEyPsElR+JLbV5Rwg== +piclist@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.3.tgz#611cab63cdd65f3549a7c11cdfe809357f81b474" + integrity sha512-Gi7J/trUrGDy6ARL8N76eToFWvOxWRLPUl7c6Bf4xJBVGVt6nG7TMIjPh9qzlpT2y2Z9qTmjQ+UOQBsSN5qONg== dependencies: "@aws-sdk/client-s3" "3.421.0" "@aws-sdk/lib-storage" "3.421.0" From c35776201a7e04ec43ac186f4d35143071be5a96 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 9 Apr 2024 15:03:32 +0800 Subject: [PATCH 22/23] :hammer: Refactor(custom): add config file type --- src/main/apis/app/shortKey/shortKeyHandler.ts | 7 +- src/main/apis/app/system/index.ts | 53 ++-- src/main/apis/app/uploader/apis.ts | 21 +- src/main/apis/app/uploader/index.ts | 9 +- src/main/apis/app/window/windowList.ts | 7 +- src/main/apis/core/datastore/index.ts | 5 +- src/main/apis/gui/index.ts | 10 +- src/main/events/ipcList.ts | 17 +- src/main/events/picgoCoreIPC.ts | 5 +- src/main/events/remotes/menu.ts | 33 +-- src/main/lifeCycle/index.ts | 35 +-- src/main/manage/utils/logger.ts | 7 +- src/main/server/index.ts | 7 +- src/main/server/routerManager.ts | 3 +- src/main/server/webServer/index.ts | 9 +- src/main/utils/aesHelper.ts | 4 +- src/main/utils/common.ts | 22 +- src/main/utils/deleteFunc.ts | 4 +- src/main/utils/getPicBeds.ts | 3 +- src/main/utils/handleI18n.ts | 5 +- src/main/utils/handleUploaderConfig.ts | 5 +- src/main/utils/pasteTemplate.ts | 3 +- src/main/utils/syncSettings.ts | 29 +- src/main/utils/updateChecker.ts | 5 +- src/renderer/apis/alist.ts | 12 +- src/renderer/apis/aliyun.ts | 8 +- src/renderer/apis/github.ts | 7 +- src/renderer/apis/imgur.ts | 6 +- src/renderer/apis/qiniu.ts | 9 +- src/renderer/apis/smms.ts | 4 +- src/renderer/apis/tcyun.ts | 10 +- src/renderer/apis/upyun.ts | 7 +- src/renderer/apis/webdav.ts | 7 +- src/renderer/layouts/Main.vue | 12 +- src/renderer/manage/store/bucketFileDb.ts | 2 +- src/renderer/pages/Gallery.vue | 16 +- src/renderer/pages/PicGoSetting.vue | 138 +++++----- src/renderer/pages/Plugin.vue | 13 +- src/renderer/pages/ShortKey.vue | 3 +- src/renderer/pages/TrayPage.vue | 9 +- src/renderer/pages/Upload.vue | 25 +- src/renderer/pages/UploaderConfigPage.vue | 5 +- src/renderer/pages/picbeds/index.vue | 5 +- src/renderer/store/index.ts | 11 +- src/universal/types/electron.d.ts | 2 +- src/universal/types/enum.ts | 19 ++ src/universal/types/manage.d.ts | 72 ++--- src/universal/types/types.d.ts | 175 +++++++++--- src/universal/types/view.d.ts | 48 ++-- src/universal/utils/common.ts | 18 +- src/universal/utils/configPaths.ts | 257 ++++++++++++++++++ src/universal/utils/static.ts | 2 + 52 files changed, 803 insertions(+), 407 deletions(-) create mode 100644 src/universal/utils/configPaths.ts diff --git a/src/main/apis/app/shortKey/shortKeyHandler.ts b/src/main/apis/app/shortKey/shortKeyHandler.ts index c97aefc..69831fd 100644 --- a/src/main/apis/app/shortKey/shortKeyHandler.ts +++ b/src/main/apis/app/shortKey/shortKeyHandler.ts @@ -18,6 +18,7 @@ import shortKeyService from './shortKeyService' // External utility functions import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants' +import { configPaths } from '~/universal/utils/configPaths' class ShortKeyHandler { private isInModifiedMode: boolean = false @@ -33,7 +34,7 @@ class ShortKeyHandler { } private initBuiltInShortKey () { - const commands = db.get('settings.shortKey') as IShortKeyConfigs + const commands = db.get(configPaths.settings.shortKey._path) as IShortKeyConfigs Object.keys(commands) .filter(item => item.includes('picgo:')) .forEach(command => { @@ -175,7 +176,7 @@ class ShortKeyHandler { } unregisterPluginShortKey (pluginName: string) { - const commands = db.get('settings.shortKey') as IShortKeyConfigs + const commands = db.get(configPaths.settings.shortKey._path) as IShortKeyConfigs const keyList = Object.keys(commands) .filter(command => command.includes(pluginName)) .map(command => { @@ -187,7 +188,7 @@ class ShortKeyHandler { keyList.forEach(item => { globalShortcut.unregister(item.key) shortKeyService.unregisterCommand(item.command) - db.unset('settings.shortKey', item.command) + db.unset(configPaths.settings.shortKey._path, item.command) }) } } diff --git a/src/main/apis/app/system/index.ts b/src/main/apis/app/system/index.ts index e267e2e..5123c97 100644 --- a/src/main/apis/app/system/index.ts +++ b/src/main/apis/app/system/index.ts @@ -18,7 +18,7 @@ import { import uploader from 'apis/app/uploader' import db, { GalleryDB } from '~/main/apis/core/datastore' import windowManager from 'apis/app/window/windowManager' -import { IWindowList } from '#/types/enum' +import { IPasteStyle, IWindowList } from '#/types/enum' import pasteTemplate from '~/main/utils/pasteTemplate' import pkg from 'root/package.json' import { ensureFilePath, handleCopyUrl } from '~/main/utils/common' @@ -28,13 +28,14 @@ import { buildPicBedListMenu } from '~/main/events/remotes/menu' import clipboardPoll from '~/main/utils/clipboardPoll' import picgo from '../../core/picgo' import { uploadClipboardFiles } from '../uploader/apis' +import { configPaths } from '~/universal/utils/configPaths' let contextMenu: Menu | null let tray: Tray | null export function setDockMenu () { - const isListeningClipboard = db.get('settings.isListeningClipboard') || false - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false const dockMenu = Menu.buildFromTemplate([ { label: T('OPEN_MAIN_WINDOW'), @@ -50,7 +51,7 @@ export function setDockMenu () { { label: T('START_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', true) + db.set(configPaths.settings.isListeningClipboard, true) clipboardPoll.startListening() clipboardPoll.on('change', () => { picgo.log.info('clipboard changed') @@ -63,7 +64,7 @@ export function setDockMenu () { { label: T('STOP_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', false) + db.set(configPaths.settings.isListeningClipboard, false) clipboardPoll.stopListening() clipboardPoll.removeAllListeners() setDockMenu() @@ -84,7 +85,7 @@ export function createMenu () { label: T('OPEN_MAIN_WINDOW'), click () { const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false settingWindow!.show() settingWindow!.focus() if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { @@ -141,7 +142,7 @@ export function createMenu () { export function createContextMenu () { const ClipboardWatcher = clipboardPoll - const isListeningClipboard = db.get('settings.isListeningClipboard') || false + const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false if (process.platform === 'darwin' || process.platform === 'win32') { const submenu = buildPicBedListMenu() const template = [ @@ -149,7 +150,7 @@ export function createContextMenu () { label: T('OPEN_MAIN_WINDOW'), click () { const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false settingWindow!.show() settingWindow!.focus() if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { @@ -166,7 +167,7 @@ export function createContextMenu () { { label: T('START_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', true) + db.set(configPaths.settings.isListeningClipboard, true) ClipboardWatcher.startListening() ClipboardWatcher.on('change', () => { picgo.log.info('clipboard changed') @@ -179,7 +180,7 @@ export function createContextMenu () { { label: T('STOP_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', false) + db.set(configPaths.settings.isListeningClipboard, false) ClipboardWatcher.stopListening() ClipboardWatcher.removeAllListeners() createContextMenu() @@ -206,11 +207,11 @@ export function createContextMenu () { click () { const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! miniWindow.removeAllListeners() - if (db.get('settings.miniWindowOntop')) { + if (db.get(configPaths.settings.miniWindowOntop)) { miniWindow.setAlwaysOnTop(true) } const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get('settings.miniWindowPosition') + const lastPosition = db.get(configPaths.settings.miniWindowPosition) if (lastPosition) { miniWindow.setPosition(lastPosition[0], lastPosition[1]) } else { @@ -218,13 +219,13 @@ export function createContextMenu () { } const setPositionFunc = () => { const position = miniWindow.getPosition() - db.set('settings.miniWindowPosition', position) + db.set(configPaths.settings.miniWindowPosition, position) } miniWindow.on('close', setPositionFunc) miniWindow.on('move', setPositionFunc) miniWindow.show() miniWindow.focus() - const autoCloseMainWindow = db.get('settings.autoCloseMainWindow') || false + const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { windowManager.get(IWindowList.SETTING_WINDOW)!.hide() } @@ -246,7 +247,7 @@ export function createContextMenu () { label: T('OPEN_MAIN_WINDOW'), click () { const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false settingWindow!.show() settingWindow!.focus() if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { @@ -259,11 +260,11 @@ export function createContextMenu () { click () { const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! miniWindow.removeAllListeners() - if (db.get('settings.miniWindowOntop')) { + if (db.get(configPaths.settings.miniWindowOntop)) { miniWindow.setAlwaysOnTop(true) } const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get('settings.miniWindowPosition') + const lastPosition = db.get(configPaths.settings.miniWindowPosition) if (lastPosition) { miniWindow.setPosition(lastPosition[0], lastPosition[1]) } else { @@ -271,13 +272,13 @@ export function createContextMenu () { } const setPositionFunc = () => { const position = miniWindow.getPosition() - db.set('settings.miniWindowPosition', position) + db.set(configPaths.settings.miniWindowPosition, position) } miniWindow.on('close', setPositionFunc) miniWindow.on('move', setPositionFunc) miniWindow.show() miniWindow.focus() - const autoCloseMainWindow = db.get('settings.autoCloseMainWindow') || false + const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { windowManager.get(IWindowList.SETTING_WINDOW)!.hide() } @@ -286,7 +287,7 @@ export function createContextMenu () { { label: T('START_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', true) + db.set(configPaths.settings.isListeningClipboard, true) ClipboardWatcher.startListening() ClipboardWatcher.on('change', () => { picgo.log.info('clipboard changed') @@ -299,7 +300,7 @@ export function createContextMenu () { { label: T('STOP_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', false) + db.set(configPaths.settings.isListeningClipboard, false) ClipboardWatcher.stopListening() ClipboardWatcher.removeAllListeners() createContextMenu() @@ -388,7 +389,7 @@ export function createTray () { windowManager.get(IWindowList.TRAY_WINDOW)!.hide() } const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false settingWindow!.show() settingWindow!.focus() if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { @@ -412,21 +413,21 @@ export function createTray () { // drop-files only be supported in macOS // so the tray window must be available tray.on('drop-files', async (event: Event, files: string[]) => { - const pasteStyle = db.get('settings.pasteStyle') || 'markdown' + const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN const rawInput = cloneDeep(files) const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! const imgs = await uploader .setWebContents(trayWindow.webContents) .upload(files) - const deleteLocalFile = db.get('settings.deleteLocalFile') || false + 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]) } - pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))) - const isShowResultNotification = db.get('settings.uploadResultNotification') === undefined ? true : !!db.get('settings.uploadResultNotification') + pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))) + const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification) if (isShowResultNotification) { const notification = new Notification({ title: T('UPLOAD_SUCCEED'), diff --git a/src/main/apis/app/uploader/apis.ts b/src/main/apis/app/uploader/apis.ts index b33529e..fe8ad59 100644 --- a/src/main/apis/app/uploader/apis.ts +++ b/src/main/apis/app/uploader/apis.ts @@ -18,15 +18,16 @@ import ALLApi from '@/apis/allApi' import picgo from '@core/picgo' import GuiApi from '../../gui' import uploader from '.' -import { IWindowList } from '#/types/enum' +import { IPasteStyle, IWindowList } from '#/types/enum' import { picBedsCanbeDeleted } from '#/utils/static' import path from 'path' import SSHClient from '~/main/utils/sshClient' import { ISftpPlistConfig } from 'piclist' import { getRawData } from '~/renderer/utils/common' +import { configPaths } from '~/universal/utils/configPaths' const handleClipboardUploading = async (): Promise => { - const useBuiltinClipboard = db.get('settings.useBuiltinClipboard') === undefined ? true : !!db.get('settings.useBuiltinClipboard') + const useBuiltinClipboard = db.get(configPaths.settings.useBuiltinClipboard) === undefined ? true : !!db.get(configPaths.settings.useBuiltinClipboard) const win = windowManager.getAvailableWindow() if (useBuiltinClipboard) { return await uploader.setWebContents(win!.webContents).uploadWithBuildInClipboard() @@ -39,9 +40,9 @@ export const uploadClipboardFiles = async (): Promise => { if (img !== false) { if (img.length > 0) { const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) - const pasteStyle = db.get('settings.pasteStyle') || 'markdown' - handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))) - const isShowResultNotification = db.get('settings.uploadResultNotification') === undefined ? true : !!db.get('settings.uploadResultNotification') + const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))) + const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification) if (isShowResultNotification) { const notification = new Notification({ title: T('UPLOAD_SUCCEED'), @@ -88,8 +89,8 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW const imgs = await uploader.setWebContents(webContents).upload(input) const result = [] if (imgs !== false) { - const pasteStyle = db.get('settings.pasteStyle') || 'markdown' - const deleteLocalFile = db.get('settings.deleteLocalFile') || 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) { @@ -99,8 +100,8 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW picgo.log.error(err) }) } - pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))) - const isShowResultNotification = db.get('settings.uploadResultNotification') === undefined ? true : !!db.get('settings.uploadResultNotification') + pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))) + const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification) if (isShowResultNotification) { const notification = new Notification({ title: T('UPLOAD_SUCCEED'), @@ -152,7 +153,7 @@ export const deleteChoosedFiles = async (list: ImgInfo[]): Promise => const dbStore = GalleryDB.getInstance() const file = await dbStore.getById(item.id) await dbStore.removeById(item.id) - if (await picgo.getConfig('settings.deleteCloudFile')) { + if (await picgo.getConfig(configPaths.settings.deleteCloudFile)) { if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) { const noteFunc = (value: boolean) => { const notification = new Notification({ diff --git a/src/main/apis/app/uploader/index.ts b/src/main/apis/app/uploader/index.ts index bb767f0..c50d142 100644 --- a/src/main/apis/app/uploader/index.ts +++ b/src/main/apis/app/uploader/index.ts @@ -33,6 +33,7 @@ import { RENAME_FILE_NAME, TALKING_DATA_EVENT } from '~/universal/events/constants' +import { configPaths } from '~/universal/utils/configPaths' const waitForRename = (window: BrowserWindow, id: number): Promise => { return new Promise((resolve) => { @@ -80,7 +81,7 @@ class Uploader { this.webContents?.send('uploadProgress', progress) }) picgo.on('beforeTransform', () => { - if (db.get('settings.uploadNotification')) { + if (db.get(configPaths.settings.uploadNotification)) { const notification = new Notification({ title: T('UPLOAD_PROGRESS'), body: T('UPLOADING') @@ -90,8 +91,8 @@ class Uploader { }) picgo.helper.beforeUploadPlugins.register('renameFn', { handle: async (ctx: IPicGo) => { - const rename = db.get('settings.rename') - const autoRename = db.get('settings.autoRename') + const rename = db.get(configPaths.settings.rename) + const autoRename = db.get(configPaths.settings.autoRename) if (autoRename || rename) { await Promise.all(ctx.output.map(async (item, index) => { let name: undefined | string | null @@ -163,7 +164,7 @@ class Uploader { if (this.webContents) { handleTalkingData(this.webContents, { fromClipboard: !img, - type: db.get('picBed.uploader') || db.get('picBed.current') || 'smms', + type: db.get(configPaths.picBed.uploader) || db.get(configPaths.picBed.current) || 'smms', count: img ? img.length : 1, duration: Date.now() - startTime } as IAnalyticsData) diff --git a/src/main/apis/app/window/windowList.ts b/src/main/apis/app/window/windowList.ts index 0654be0..91d4e21 100644 --- a/src/main/apis/app/window/windowList.ts +++ b/src/main/apis/app/window/windowList.ts @@ -23,14 +23,15 @@ import { IWindowList } from '#/types/enum' // External utility functions import { CREATE_APP_MENU } from '@core/bus/constants' import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants' +import { configPaths } from '~/universal/utils/configPaths' const windowList = new Map() const handleWindowParams = (windowURL: string) => windowURL const getDefaultWindowSizes = (): { width: number, height: number } => { - const mainWindowWidth = picgo.getConfig('settings.mainWindowWidth') - const mainWindowHeight = picgo.getConfig('settings.mainWindowHeight') + const mainWindowWidth = picgo.getConfig(configPaths.settings.mainWindowWidth) + const mainWindowHeight = picgo.getConfig(configPaths.settings.mainWindowHeight) return { width: mainWindowWidth || 1200, height: mainWindowHeight || 800 @@ -127,7 +128,7 @@ const miniWindowOptions = { } } as IBrowserWindowOptions -if (db.get('settings.miniWindowOntop')) { +if (db.get(configPaths.settings.miniWindowOntop)) { miniWindowOptions.alwaysOnTop = true } diff --git a/src/main/apis/core/datastore/index.ts b/src/main/apis/core/datastore/index.ts index c2a69ff..868d3e8 100644 --- a/src/main/apis/core/datastore/index.ts +++ b/src/main/apis/core/datastore/index.ts @@ -13,6 +13,7 @@ import { DBStore, JSONStore } from '@picgo/store' // External utility functions import { T } from '~/main/i18n' +import { configPaths } from '~/universal/utils/configPaths' const STORE_PATH = dbPathDir() @@ -37,8 +38,8 @@ class ConfigStore { }) } - if (!this.db.has('settings.shortKey')) { - this.db.set('settings.shortKey[picgo:upload]', { + if (!this.db.has(configPaths.settings.shortKey._path)) { + this.db.set(configPaths.settings.shortKey['picgo:upload'], { enable: true, key: 'CommandOrControl+Shift+P', name: 'upload', diff --git a/src/main/apis/gui/index.ts b/src/main/apis/gui/index.ts index e094e7a..1a1eff4 100644 --- a/src/main/apis/gui/index.ts +++ b/src/main/apis/gui/index.ts @@ -24,6 +24,8 @@ import { SHOW_INPUT_BOX } from '~/universal/events/constants' // External utility functions import { DBStore } from '@picgo/store' import { T } from '~/main/i18n' +import { configPaths } from '~/universal/utils/configPaths' +import { IPasteStyle } from '~/universal/types/enum' // Cross-process support may be required in the future class GuiApi implements IGuiApi { @@ -85,15 +87,15 @@ class GuiApi implements IGuiApi { const rawInput = cloneDeep(input) const imgs = await uploader.setWebContents(webContents!).upload(input) if (imgs !== false) { - const pasteStyle = db.get('settings.pasteStyle') || 'markdown' - const deleteLocalFile = db.get('settings.deleteLocalFile') || 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) { await fs.remove(rawInput[i]) } - pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get('settings.customLink')))) - const isShowResultNotification = db.get('settings.uploadResultNotification') === undefined ? true : !!db.get('settings.uploadResultNotification') + pasteText.push(await (pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))) + const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification) if (isShowResultNotification) { const notification = new Notification({ title: T('UPLOAD_SUCCEED'), diff --git a/src/main/events/ipcList.ts b/src/main/events/ipcList.ts index 66f2c45..926493d 100644 --- a/src/main/events/ipcList.ts +++ b/src/main/events/ipcList.ts @@ -14,7 +14,7 @@ import { import windowManager from 'apis/app/window/windowManager' // 枚举类型声明 -import { IWindowList } from '#/types/enum' +import { IPasteStyle, IWindowList } from '#/types/enum' // 上传器 import uploader from 'apis/app/uploader' @@ -90,6 +90,7 @@ import { ISftpPlistConfig } from 'piclist' import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc' import webServer from '../server/webServer' +import { configPaths } from '~/universal/utils/configPaths' const STORE_PATH = app.getPath('userData') @@ -103,9 +104,9 @@ export default { // macOS use builtin clipboard is OK const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard() if (img !== false) { - const pasteStyle = db.get('settings.pasteStyle') || 'markdown' - handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get('settings.customLink')))) - const isShowResultNotification = db.get('settings.uploadResultNotification') === undefined ? true : !!db.get('settings.uploadResultNotification') + const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + handleCopyUrl(await (pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))) + const isShowResultNotification = db.get(configPaths.settings.uploadResultNotification) === undefined ? true : !!db.get(configPaths.settings.uploadResultNotification) if (isShowResultNotification) { const notification = new Notification({ title: T('UPLOAD_SUCCEED'), @@ -299,7 +300,7 @@ export default { ipcMain.on('openSettingWindow', () => { windowManager.get(IWindowList.SETTING_WINDOW)!.show() - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false if (autoCloseMiniWindow) { if (windowManager.has(IWindowList.MINI_WINDOW)) { windowManager.get(IWindowList.MINI_WINDOW)!.hide() @@ -315,11 +316,11 @@ export default { const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! miniWindow.removeAllListeners() - if (db.get('settings.miniWindowOntop')) { + if (db.get(configPaths.settings.miniWindowOntop)) { miniWindow.setAlwaysOnTop(true) } const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get('settings.miniWindowPosition') + const lastPosition = db.get(configPaths.settings.miniWindowPosition) if (lastPosition) { miniWindow.setPosition(lastPosition[0], lastPosition[1]) } else { @@ -327,7 +328,7 @@ export default { } const setPositionFunc = () => { const position = miniWindow.getPosition() - db.set('settings.miniWindowPosition', position) + db.set(configPaths.settings.miniWindowPosition, position) } miniWindow.on('close', setPositionFunc) miniWindow.on('move', setPositionFunc) diff --git a/src/main/events/picgoCoreIPC.ts b/src/main/events/picgoCoreIPC.ts index d3e894e..4117c18 100644 --- a/src/main/events/picgoCoreIPC.ts +++ b/src/main/events/picgoCoreIPC.ts @@ -47,6 +47,7 @@ import { SET_CURRENT_LANGUAGE, GET_CURRENT_LANGUAGE } from '#/events/constants' +import { configPaths } from '~/universal/utils/configPaths' // eslint-disable-next-line const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require @@ -360,8 +361,8 @@ const handlePicGoGalleryDB = () => { }) ipcMain.handle(PASTE_TEXT, async (_, item: ImgInfo, copy = true) => { - const pasteStyle = picgo.getConfig('settings.pasteStyle') || IPasteStyle.MARKDOWN - const customLink = picgo.getConfig('settings.customLink') + const pasteStyle = picgo.getConfig(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + const customLink = picgo.getConfig(configPaths.settings.customLink) const txt = await pasteTemplate(pasteStyle, item, customLink) if (copy) { clipboard.writeText(txt) diff --git a/src/main/events/remotes/menu.ts b/src/main/events/remotes/menu.ts index 589dbca..05cf1c0 100644 --- a/src/main/events/remotes/menu.ts +++ b/src/main/events/remotes/menu.ts @@ -30,6 +30,7 @@ import { } from '~/universal/events/constants' import { PicGo as PicGoCore } from 'piclist' import { T } from '~/main/i18n' +import { configPaths } from '~/universal/utils/configPaths' interface GuiMenuItem { label: string @@ -37,7 +38,7 @@ interface GuiMenuItem { } const buildMiniPageMenu = () => { - const isListeningClipboard = db.get('settings.isListeningClipboard') || false + const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false const ClipboardWatcher = clipboardPoll const submenu = buildPicBedListMenu() const template = [ @@ -45,7 +46,7 @@ const buildMiniPageMenu = () => { label: T('OPEN_MAIN_WINDOW'), click () { windowManager.get(IWindowList.SETTING_WINDOW)!.show() - const autoCloseMiniWindow = db.get('settings.autoCloseMiniWindow') || false + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false if (autoCloseMiniWindow) { if (windowManager.has(IWindowList.MINI_WINDOW)) { windowManager.get(IWindowList.MINI_WINDOW)!.hide() @@ -73,7 +74,7 @@ const buildMiniPageMenu = () => { { label: T('START_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', true) + db.set(configPaths.settings.isListeningClipboard, true) ClipboardWatcher.startListening() ClipboardWatcher.on('change', () => { picgo.log.info('clipboard changed') @@ -86,7 +87,7 @@ const buildMiniPageMenu = () => { { label: T('STOP_WATCH_CLIPBOARD'), click () { - db.set('settings.isListeningClipboard', false) + db.set(configPaths.settings.isListeningClipboard, false) ClipboardWatcher.stopListening() ClipboardWatcher.removeAllListeners() buildMiniPageMenu() @@ -147,7 +148,7 @@ const buildMainPageMenu = (win: BrowserWindow) => { const buildPicBedListMenu = () => { const picBeds = getPicBeds() - const currentPicBed = picgo.getConfig('picBed.uploader') + const currentPicBed = picgo.getConfig(configPaths.picBed.uploader) const currentPicBedName = picBeds.find(item => item.type === currentPicBed)?.name const picBedConfigList = picgo.getConfig('uploader') const currentPicBedMenuItem = [{ @@ -184,8 +185,8 @@ const buildPicBedListMenu = () => { click: !hasSubmenu ? function () { picgo.saveConfig({ - 'picBed.current': item.type, - 'picBed.uploader': item.type + [configPaths.picBed.current]: item.type, + [configPaths.picBed.uploader]: item.type }) if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed') @@ -204,19 +205,19 @@ const buildPicBedListMenu = () => { const handleRestoreState = (item: string, name: string): void => { if (item === 'uploader') { - const current = picgo.getConfig('picBed.current') + const current = picgo.getConfig(configPaths.picBed.current) if (current === name) { picgo.saveConfig({ - 'picBed.current': 'smms', - 'picBed.uploader': 'smms' + [configPaths.picBed.current]: 'smms', + [configPaths.picBed.uploader]: 'smms' }) } } if (item === 'transformer') { - const current = picgo.getConfig('picBed.transformer') + const current = picgo.getConfig(configPaths.picBed.transformer) if (current === name) { picgo.saveConfig({ - 'picBed.transformer': 'path' + [configPaths.picBed.transformer]: 'path' }) } } @@ -286,20 +287,20 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => { // handle transformer if (plugin.config.transformer.name) { - const currentTransformer = picgo.getConfig('picBed.transformer') || 'path' + const currentTransformer = picgo.getConfig(configPaths.picBed.transformer) || 'path' const pluginTransformer = plugin.config.transformer.name const obj = { label: `${currentTransformer === pluginTransformer ? T('DISABLE') : T('ENABLE')}transformer - ${plugin.config.transformer.name}`, click () { const transformer = plugin.config.transformer.name - const currentTransformer = picgo.getConfig('picBed.transformer') || 'path' + const currentTransformer = picgo.getConfig(configPaths.picBed.transformer) || 'path' if (currentTransformer === transformer) { picgo.saveConfig({ - 'picBed.transformer': 'path' + [configPaths.picBed.transformer]: 'path' }) } else { picgo.saveConfig({ - 'picBed.transformer': transformer + [configPaths.picBed.transformer]: transformer }) } } diff --git a/src/main/lifeCycle/index.ts b/src/main/lifeCycle/index.ts index 48a2919..92a55ad 100644 --- a/src/main/lifeCycle/index.ts +++ b/src/main/lifeCycle/index.ts @@ -14,7 +14,7 @@ import { import beforeOpen from '~/main/utils/beforeOpen' import ipcList from '~/main/events/ipcList' import busEventList from '~/main/events/busEventList' -import { IRemoteNoticeTriggerHook, IWindowList } from '#/types/enum' +import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum' import windowManager from 'apis/app/window/windowManager' import { uploadChoosedFiles, @@ -47,6 +47,7 @@ import fs from 'fs-extra' import { startFileServer } from '../fileServer' import webServer from '../server/webServer' import axios from 'axios' +import { configPaths } from '~/universal/utils/configPaths' const isDevelopment = process.env.NODE_ENV !== 'production' const handleStartUpFiles = (argv: string[], cwd: string) => { @@ -74,10 +75,10 @@ autoUpdater.setFeedURL({ autoUpdater.autoDownload = false autoUpdater.on('update-available', async (info: UpdateInfo) => { - const lang = db.get('settings.language') || 'zh-CN' + const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN let updateLog = '' try { - const url = lang === 'zh-CN' ? 'https://release.piclist.cn/currentVersion.md' : 'https://release.piclist.cn/currentVersion_en.md' + const url = lang === II18nLanguage.ZH_CN ? 'https://release.piclist.cn/currentVersion.md' : 'https://release.piclist.cn/currentVersion_en.md' const res = await axios.get(url) updateLog = res.data } catch (e: any) { @@ -98,7 +99,7 @@ autoUpdater.on('update-available', async (info: UpdateInfo) => { } else { shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest') } - db.set('settings.showUpdateTip', !result.checkboxChecked) + db.set(configPaths.settings.showUpdateTip, !result.checkboxChecked) }).catch((err) => { logger.error(err) }) @@ -152,27 +153,27 @@ class LifeCycle { createProtocol('picgo') windowManager.create(IWindowList.TRAY_WINDOW) windowManager.create(IWindowList.SETTING_WINDOW) - const isAutoListenClipboard = db.get('settings.isAutoListenClipboard') || false + const isAutoListenClipboard = db.get(configPaths.settings.isAutoListenClipboard) || false const ClipboardWatcher = clipboardPoll if (isAutoListenClipboard) { - db.set('settings.isListeningClipboard', true) + db.set(configPaths.settings.isListeningClipboard, true) ClipboardWatcher.startListening() ClipboardWatcher.on('change', () => { picgo.log.info('clipboard changed') uploadClipboardFiles() }) } else { - db.set('settings.isListeningClipboard', false) + db.set(configPaths.settings.isListeningClipboard, false) } - const isHideDock = db.get('settings.isHideDock') || false - const startMode = db.get('settings.startMode') || 'quiet' + const isHideDock = db.get(configPaths.settings.isHideDock) || false + const startMode = db.get(configPaths.settings.startMode) || ISartMode.QUIET if (process.platform === 'darwin') { isHideDock ? app.dock.hide() : setDockMenu() - startMode !== 'no-tray' && createTray() + startMode !== ISartMode.NO_TRAY && createTray() } else { createTray() } - db.set('needReload', false) + db.set(configPaths.needReload, false) updateChecker() // 不需要阻塞 process.nextTick(() => { @@ -194,15 +195,15 @@ class LifeCycle { } await remoteNoticeHandler.init() remoteNoticeHandler.triggerHook(IRemoteNoticeTriggerHook.APP_START) - if (startMode === 'mini') { + if (startMode === ISartMode.MINI) { windowManager.create(IWindowList.MINI_WINDOW) const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! miniWindow.removeAllListeners() - if (db.get('settings.miniWindowOntop')) { + if (db.get(configPaths.settings.miniWindowOntop)) { miniWindow.setAlwaysOnTop(true) } const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get('settings.miniWindowPosition') + const lastPosition = db.get(configPaths.settings.miniWindowPosition) if (lastPosition) { miniWindow.setPosition(lastPosition[0], lastPosition[1]) } else { @@ -210,13 +211,13 @@ class LifeCycle { } const setPositionFunc = () => { const position = miniWindow.getPosition() - db.set('settings.miniWindowPosition', position) + db.set(configPaths.settings.miniWindowPosition, position) } miniWindow.on('close', setPositionFunc) miniWindow.on('move', setPositionFunc) miniWindow.show() miniWindow.focus() - } else if (startMode === 'main') { + } else if (startMode === ISartMode.MAIN) { const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! settingWindow.show() settingWindow.focus() @@ -251,7 +252,7 @@ class LifeCycle { } }) app.setLoginItemSettings({ - openAtLogin: db.get('settings.autoStart') || false + openAtLogin: db.get(configPaths.settings.autoStart) || false }) if (process.platform === 'win32') { app.setAppUserModelId('com.kuingsmile.piclist') diff --git a/src/main/manage/utils/logger.ts b/src/main/manage/utils/logger.ts index 6e24f24..d05453e 100644 --- a/src/main/manage/utils/logger.ts +++ b/src/main/manage/utils/logger.ts @@ -7,6 +7,7 @@ import { ILogType } from '#/types/enum' import { ILogColor, ILogger } from 'piclist/dist/types' import { ManageApiType, Undefinable } from '~/universal/types/manage' import { enforceNumber, isDev } from '#/utils/common' +import { configPaths } from '~/universal/utils/configPaths' export class ManageLogger implements ILogger { private readonly level = { @@ -29,9 +30,9 @@ export class ManageLogger implements ILogger { `[PicList ${type.toUpperCase()}]` ) console.log(logHeader, ...msg) - this.logLevel = this.ctx.getConfig('settings.logLevel') + this.logLevel = this.ctx.getConfig(configPaths.settings.logLevel) this.logPath = - this.ctx.getConfig>('settings.logPath') || + this.ctx.getConfig>(configPaths.settings.logPath) || path.join(this.ctx.baseDir, './manage.log') setTimeout(() => { try { @@ -61,7 +62,7 @@ export class ManageLogger implements ILogger { const logFileSizeLimit = enforceNumber( this.ctx.getConfig>( - 'settings.logFileSizeLimit' + configPaths.settings.logFileSizeLimit ) || 10 ) * 1024 * diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 158a12b..655b129 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -11,6 +11,7 @@ import multer from 'multer' import { app } from 'electron' import path from 'path' import fs from 'fs-extra' +import { configPaths } from '~/universal/utils/configPaths' const DEFAULT_PORT = 36677 const DEFAULT_HOST = '0.0.0.0' @@ -49,10 +50,10 @@ class Server { } getConfigWithDefaults () { - let config = picgo.getConfig('settings.server') + let config = picgo.getConfig(configPaths.settings.server) if (!this.isValidConfig(config)) { config = { port: DEFAULT_PORT, host: DEFAULT_HOST, enable: true } - picgo.saveConfig({ 'settings.server': config }) + picgo.saveConfig({ [configPaths.settings.server]: config }) } config.host = config.host === '127.0.0.1' ? '0.0.0.0' : config.host return config @@ -96,7 +97,7 @@ class Server { logger.info('[PicList Server] get a POST request from IP:', remoteAddress) let urlSP = query ? new URLSearchParams(query) : undefined if (remoteAddress === '::1' || remoteAddress === '127.0.0.1') { - const serverKey = picgo.getConfig('settings.serverKey') || '' + const serverKey = picgo.getConfig(configPaths.settings.serverKey) || '' if (urlSP) { urlSP.set('key', serverKey) } else { diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index 43dba2c..0b0c44a 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -15,6 +15,7 @@ import { AESHelper } from '../utils/aesHelper' import { marked } from 'marked' import { markdownContent } from './apiDoc' import http from 'http' +import { configPaths } from '~/universal/utils/configPaths' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') @@ -46,7 +47,7 @@ router.post('/upload', async ({ }): Promise => { try { const passedKey = urlparams?.get('key') - const serverKey = picgo.getConfig('settings.serverKey') || '' + const serverKey = picgo.getConfig(configPaths.settings.serverKey) || '' if (serverKey && passedKey !== serverKey) { handleResponse({ response, diff --git a/src/main/server/webServer/index.ts b/src/main/server/webServer/index.ts index 7a6003d..22ab2bc 100644 --- a/src/main/server/webServer/index.ts +++ b/src/main/server/webServer/index.ts @@ -4,6 +4,7 @@ import path from 'path' import picgo from '@core/picgo' import logger from '../../apis/core/picgo/logger' import { encodeFilePath } from '~/universal/utils/common' +import { configPaths } from '~/universal/utils/configPaths' const defaultPath = process.platform === 'win32' ? 'C:\\Users' : '/' @@ -49,10 +50,10 @@ class WebServer { loadConfig (): void { this.config = { - enableWebServer: picgo.getConfig('settings.enableWebServer') || false, - webServerHost: picgo.getConfig('settings.webServerHost') || '0.0.0.0', - webServerPort: picgo.getConfig('settings.webServerPort') || 37777, - webServerPath: picgo.getConfig('settings.webServerPath') || defaultPath + enableWebServer: picgo.getConfig(configPaths.settings.enableWebServer) || false, + webServerHost: picgo.getConfig(configPaths.settings.webServerHost) || '0.0.0.0', + webServerPort: picgo.getConfig(configPaths.settings.webServerPort) || 37777, + webServerPath: picgo.getConfig(configPaths.settings.webServerPath) || defaultPath } } diff --git a/src/main/utils/aesHelper.ts b/src/main/utils/aesHelper.ts index ea99be4..6a1c4cd 100644 --- a/src/main/utils/aesHelper.ts +++ b/src/main/utils/aesHelper.ts @@ -1,8 +1,10 @@ import crypto from 'crypto' import picgo from '@core/picgo' +import { DEFAULT_AES_PASSWORD } from '~/universal/utils/static' +import { configPaths } from '~/universal/utils/configPaths' function getDerivedKey (): Buffer { - const userPassword = picgo.getConfig('settings.aesPassword') || 'PicList-aesPassword' + const userPassword = picgo.getConfig(configPaths.settings.aesPassword) || DEFAULT_AES_PASSWORD const fixedSalt = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex') const fixedIterations = 100000 const keyLength = 32 diff --git a/src/main/utils/common.ts b/src/main/utils/common.ts index 7b52876..2bbc19c 100644 --- a/src/main/utils/common.ts +++ b/src/main/utils/common.ts @@ -5,9 +5,11 @@ import { handleUrlEncode } from '~/universal/utils/common' import axios from 'axios' import FormData from 'form-data' import logger from '../apis/core/picgo/logger' +import { configPaths } from '~/universal/utils/configPaths' +import { IShortUrlServer } from '~/universal/types/enum' export const handleCopyUrl = (str: string): void => { - if (db.get('settings.autoCopy') !== false) { + if (db.get(configPaths.settings.autoCopy) !== false) { clipboard.writeText(str) } } @@ -122,16 +124,16 @@ export const getClipboardFilePath = (): string => { return '' } -export const handleUrlEncodeWithSetting = (url: string) => db.get('settings.encodeOutputURL') ? handleUrlEncode(url) : url +export const handleUrlEncodeWithSetting = (url: string) => db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url const c1nApi = 'https://c1n.cn/link/short' export const generateShortUrl = async (url: string) => { - const server = db.get('settings.shortUrlServer') || 'c1n' - if (server === 'c1n') { + const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N + if (server === IShortUrlServer.C1N) { const form = new FormData() form.append('url', url) - const c1nToken = db.get('settings.c1nToken') || '' + const c1nToken = db.get(configPaths.settings.c1nToken) || '' if (!c1nToken) { logger.warn('c1n token is not set') return url @@ -148,9 +150,9 @@ export const generateShortUrl = async (url: string) => { } catch (e: any) { logger.error(e) } - } else if (server === 'yourls') { - let domain = db.get('settings.yourlsDomain') || '' - const signature = db.get('settings.yourlsSignature') || '' + } else if (server === IShortUrlServer.YOURLS) { + let domain = db.get(configPaths.settings.yourlsDomain) || '' + const signature = db.get(configPaths.settings.yourlsSignature) || '' if (domain && signature) { if (!/^https?:\/\//.test(domain)) { domain = `http://${domain}` @@ -169,8 +171,8 @@ export const generateShortUrl = async (url: string) => { } else { logger.warn('Yourls server or signature is not set') } - } else if (server === 'cf_worker') { - let cfWorkerHost = db.get('settings.cfWorkerHost') || '' + } else if (server === IShortUrlServer.CFWORKER) { + let cfWorkerHost = db.get(configPaths.settings.cfWorkerHost) || '' cfWorkerHost = cfWorkerHost.replace(/\/$/, '') if (cfWorkerHost) { try { diff --git a/src/main/utils/deleteFunc.ts b/src/main/utils/deleteFunc.ts index 1697b39..c808ab9 100644 --- a/src/main/utils/deleteFunc.ts +++ b/src/main/utils/deleteFunc.ts @@ -12,8 +12,8 @@ interface DogecloudTokenFull { accessKeyId: string secretAccessKey: string sessionToken: string - }, - ExpiredAt: number, + } + ExpiredAt: number Buckets: { name: string s3Bucket: string diff --git a/src/main/utils/getPicBeds.ts b/src/main/utils/getPicBeds.ts index a5c744b..efa70aa 100644 --- a/src/main/utils/getPicBeds.ts +++ b/src/main/utils/getPicBeds.ts @@ -1,8 +1,9 @@ import picgo from '@core/picgo' +import { configPaths } from '~/universal/utils/configPaths' const getPicBeds = () => { const picBedTypes = picgo.helper.uploader.getIdList() - const picBedFromDB = picgo.getConfig('picBed.list') || [] + const picBedFromDB = picgo.getConfig(configPaths.picBed.list) || [] const picBeds = picBedTypes.map((item: string) => { const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined return { diff --git a/src/main/utils/handleI18n.ts b/src/main/utils/handleI18n.ts index abc6680..dfdc328 100644 --- a/src/main/utils/handleI18n.ts +++ b/src/main/utils/handleI18n.ts @@ -1,6 +1,9 @@ import db from '~/main/apis/core/datastore' import { i18nManager } from '~/main/i18n' +import { II18nLanguage } from '~/universal/types/enum' +import { configPaths } from '~/universal/utils/configPaths' + export const initI18n = () => { - const currentLanguage = db.get('settings.language') || 'zh-CN' + const currentLanguage = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN i18nManager.setCurrentLanguage(currentLanguage) } diff --git a/src/main/utils/handleUploaderConfig.ts b/src/main/utils/handleUploaderConfig.ts index 1fc63f7..eb24f7e 100644 --- a/src/main/utils/handleUploaderConfig.ts +++ b/src/main/utils/handleUploaderConfig.ts @@ -1,6 +1,7 @@ import { v4 as uuid } from 'uuid' import { trimValues } from '#/utils/common' import picgo from '@core/picgo' +import { configPaths } from '~/universal/utils/configPaths' export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IPicGoPluginConfig[] => { for (const i in config) { @@ -61,8 +62,8 @@ export const changeCurrentUploader = (type: string, config?: IStringKeyMap, id?: }) } picgo.saveConfig({ - 'picBed.current': type, - 'picBed.uploader': type + [configPaths.picBed.current]: type, + [configPaths.picBed.uploader]: type }) } diff --git a/src/main/utils/pasteTemplate.ts b/src/main/utils/pasteTemplate.ts index 0687d53..4f5391f 100644 --- a/src/main/utils/pasteTemplate.ts +++ b/src/main/utils/pasteTemplate.ts @@ -2,6 +2,7 @@ import { IPasteStyle } from '#/types/enum' import { generateShortUrl } from '~/main/utils/common' import db from '~/main/apis/core/datastore' import { handleUrlEncodeWithSetting } from './common' +import { configPaths } from '~/universal/utils/configPaths' export const formatCustomLink = (customLink: string, item: ImgInfo) => { const fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '') @@ -28,7 +29,7 @@ export default async (style: IPasteStyle, item: ImgInfo, customLink: string | un url = item.imgUrl || item.url || '' } url = handleUrlEncodeWithSetting(url) - const useShortUrl = db.get('settings.useShortUrl') || false + const useShortUrl = db.get(configPaths.settings.useShortUrl) || false if (useShortUrl) { url = await generateShortUrl(url) } diff --git a/src/main/utils/syncSettings.ts b/src/main/utils/syncSettings.ts index 2092568..0e90260 100644 --- a/src/main/utils/syncSettings.ts +++ b/src/main/utils/syncSettings.ts @@ -6,18 +6,7 @@ import db from '~/main/apis/core/datastore' import { HttpsProxyAgent } from 'hpagent' import { Octokit } from '@octokit/rest' import logger from 'apis/core/picgo/logger' - -interface SyncConfig { - type: string - file?: string - username: string - repo: string - branch: string - token: string - endpoint?: string - proxy?: string - interval?: number -} +import { configPaths } from '~/universal/utils/configPaths' const STORE_PATH = app.getPath('userData') @@ -28,7 +17,7 @@ const configFileNames = [ 'manage.bak.json' ] -function getOctokit (syncConfig: SyncConfig) { +function getOctokit (syncConfig: ISyncConfig) { const { token, proxy } = syncConfig return new Octokit({ auth: token, @@ -47,7 +36,7 @@ function getOctokit (syncConfig: SyncConfig) { } function getSyncConfig () { - return db.get('settings.sync') || { + return db.get(configPaths.settings.sync) || { type: 'github', username: '', repo: '', @@ -57,12 +46,12 @@ function getSyncConfig () { } } -function syncConfigValidator (syncConfig: SyncConfig) { +function syncConfigValidator (syncConfig: ISyncConfig) { const { type, username, repo, branch, token } = syncConfig return type && username && repo && branch && token } -async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) { +async function uploadLocalToRemote (syncConfig: ISyncConfig, fileName: string) { const localFilePath = path.join(STORE_PATH, fileName) if (!fs.existsSync(localFilePath)) { return false @@ -120,7 +109,7 @@ async function uploadLocalToRemote (syncConfig: SyncConfig, fileName: string) { } } -async function updateLocalToRemote (syncConfig: SyncConfig, fileName: string) { +async function updateLocalToRemote (syncConfig: ISyncConfig, fileName: string) { const localFilePath = path.join(STORE_PATH, fileName) if (!fs.existsSync(localFilePath)) { return false @@ -201,7 +190,7 @@ async function updateLocalToRemote (syncConfig: SyncConfig, fileName: string) { } } -async function downloadRemoteToLocal (syncConfig: SyncConfig, fileName: string) { +async function downloadRemoteToLocal (syncConfig: ISyncConfig, fileName: string) { const localFilePath = path.join(STORE_PATH, fileName) const { username, repo, branch, token, proxy, type } = syncConfig if (type === 'gitee') { @@ -304,7 +293,7 @@ async function uploadFile (fileName: string, all = false) { } } -async function uploadFunc (syncConfig: SyncConfig, fileName: string) { +async function uploadFunc (syncConfig: ISyncConfig, fileName: string) { let result = false try { result = await updateLocalToRemote(syncConfig, fileName) @@ -342,7 +331,7 @@ async function downloadFile (fileName: string, all = false) { } } -async function downloadFunc (syncConfig: SyncConfig, fileName: string) { +async function downloadFunc (syncConfig: ISyncConfig, fileName: string) { const result = await downloadRemoteToLocal(syncConfig, fileName) if (!result) { logger.error(`download ${fileName} failed`) diff --git a/src/main/utils/updateChecker.ts b/src/main/utils/updateChecker.ts index 3aa834f..341d2b0 100644 --- a/src/main/utils/updateChecker.ts +++ b/src/main/utils/updateChecker.ts @@ -1,10 +1,11 @@ import db from '~/main/apis/core/datastore' import { autoUpdater } from 'electron-updater' +import { configPaths } from '~/universal/utils/configPaths' const updateChecker = async () => { - let showTip = db.get('settings.showUpdateTip') + let showTip = db.get(configPaths.settings.showUpdateTip) if (showTip === undefined) { - db.set('settings.showUpdateTip', true) + db.set(configPaths.settings.showUpdateTip, true) showTip = true } if (showTip) { diff --git a/src/renderer/apis/alist.ts b/src/renderer/apis/alist.ts index 96b6570..594d937 100644 --- a/src/renderer/apis/alist.ts +++ b/src/renderer/apis/alist.ts @@ -1,8 +1,18 @@ import axios from 'axios' import path from 'path' +interface IConfigMap { + fileName: string + config: { + version: string + url: string + uploadPath: string + token: string + } +} + export default class AlistApi { - static async delete (configMap: IStringKeyMap): Promise { + static async delete (configMap: IConfigMap): Promise { const { fileName, config } = configMap try { const { version, url, uploadPath, token } = config diff --git a/src/renderer/apis/aliyun.ts b/src/renderer/apis/aliyun.ts index 3a210ff..2d255ad 100644 --- a/src/renderer/apis/aliyun.ts +++ b/src/renderer/apis/aliyun.ts @@ -2,13 +2,7 @@ import OSS from 'ali-oss' interface IConfigMap { fileName: string - config: { - accessKeyId: string - accessKeySecret: string - bucket: string - area: string - path?: string - } + config: PartialKeys } export default class AliyunApi { diff --git a/src/renderer/apis/github.ts b/src/renderer/apis/github.ts index 638dac4..f0073e7 100644 --- a/src/renderer/apis/github.ts +++ b/src/renderer/apis/github.ts @@ -3,12 +3,7 @@ import { Octokit } from '@octokit/rest' interface IConfigMap { fileName: string hash: string - config: { - repo: string - token: string - branch: string - path?: string - } + config: PartialKeys } export default class GithubApi { diff --git a/src/renderer/apis/imgur.ts b/src/renderer/apis/imgur.ts index 573df25..dff5b3a 100644 --- a/src/renderer/apis/imgur.ts +++ b/src/renderer/apis/imgur.ts @@ -1,11 +1,7 @@ import axios, { AxiosResponse } from 'axios' interface IConfigMap { - config?: { - clientId?: string - username?: string - accessToken?: string - } + config?: Partial hash?: string } diff --git a/src/renderer/apis/qiniu.ts b/src/renderer/apis/qiniu.ts index d8fb225..ad64f74 100644 --- a/src/renderer/apis/qiniu.ts +++ b/src/renderer/apis/qiniu.ts @@ -1,13 +1,8 @@ import Qiniu from 'qiniu' interface IConfigMap { - fileName: string; - config: { - accessKey: string; - secretKey: string; - bucket: string; - path?: string; - } + fileName: string + config: PartialKeys } export default class QiniuApi { diff --git a/src/renderer/apis/smms.ts b/src/renderer/apis/smms.ts index 4f3a565..1176343 100644 --- a/src/renderer/apis/smms.ts +++ b/src/renderer/apis/smms.ts @@ -2,9 +2,7 @@ import axios, { AxiosResponse } from 'axios' interface IConfigMap { hash?: string - config?: { - token?: string - } + config?: Partial } export default class SmmsApi { diff --git a/src/renderer/apis/tcyun.ts b/src/renderer/apis/tcyun.ts index ca18db3..19ce67c 100644 --- a/src/renderer/apis/tcyun.ts +++ b/src/renderer/apis/tcyun.ts @@ -1,14 +1,8 @@ import COS from 'cos-nodejs-sdk-v5' interface IConfigMap { - fileName: string; - config: { - secretId: string; - secretKey: string; - bucket: string; - area: string; - path?: string; - }; + fileName: string + config: PartialKeys } export default class TcyunApi { diff --git a/src/renderer/apis/upyun.ts b/src/renderer/apis/upyun.ts index 0decb37..41d9bf7 100644 --- a/src/renderer/apis/upyun.ts +++ b/src/renderer/apis/upyun.ts @@ -1,7 +1,12 @@ import Upyun from 'upyun' +interface IConfigMap { + fileName: string + config: PartialKeys +} + export default class UpyunApi { - static async delete (configMap: IStringKeyMap): Promise { + static async delete (configMap: IConfigMap): Promise { const { fileName, config: { bucket, operator, password, path } } = configMap try { const service = new Upyun.Service(bucket, operator, password) diff --git a/src/renderer/apis/webdav.ts b/src/renderer/apis/webdav.ts index 4ed3c25..7553b2d 100644 --- a/src/renderer/apis/webdav.ts +++ b/src/renderer/apis/webdav.ts @@ -1,8 +1,13 @@ import { AuthType, WebDAVClientOptions, createClient } from 'webdav' import { formatEndpoint } from '~/main/manage/utils/common' +interface IConfigMap { + fileName: string + config: PartialKeys +} + export default class WebdavApi { - static async delete (configMap: IStringKeyMap): Promise { + static async delete (configMap: IConfigMap): Promise { const { fileName, config: { host, username, password, path, sslEnabled, authType } } = configMap const endpoint = formatEndpoint(host, sslEnabled) const options: WebDAVClientOptions = { diff --git a/src/renderer/layouts/Main.vue b/src/renderer/layouts/Main.vue index 37b210c..075be33 100644 --- a/src/renderer/layouts/Main.vue +++ b/src/renderer/layouts/Main.vue @@ -279,6 +279,8 @@ import { // 数据发送工具函数 import { getConfig, saveConfig, sendToMain } from '@/utils/dataSender' import { openURL } from '@/utils/common' +import { configPaths, manualPageOpenType } from '~/universal/utils/configPaths' +import { II18nLanguage } from '~/universal/types/enum' const version = ref(process.env.NODE_ENV === 'production' ? pkg.version : 'Dev') const routerConfig = reactive(config) @@ -326,10 +328,10 @@ const handleGetPicPeds = () => { const handleSelect = async (index: string) => { defaultActive.value = index if (index === routerConfig.DocumentPage) { - const manualPageOpenSetting = await getConfig('settings.manualPageOpen') - const lang = await getConfig('settings.language') || 'zh-CN' + const manualPageOpenSetting = await getConfig(configPaths.settings.manualPageOpen) + const lang = await getConfig(configPaths.settings.language) || II18nLanguage.ZH_CN const openManual = () => ipcRenderer.send('openManualWindow') - const openExternal = () => openURL(lang === 'zh-CN' ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html') + const openExternal = () => openURL(lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html') if (!manualPageOpenSetting) { ElMessageBox.confirm($T('MANUAL_PAGE_OPEN_TIP'), $T('MANUAL_PAGE_OPEN_TIP_TITLE'), { @@ -338,10 +340,10 @@ const handleSelect = async (index: string) => { type: 'info', center: true }).then(() => { - saveConfig('settings.manualPageOpen', 'browser') + saveConfig(configPaths.settings.manualPageOpen, 'browser') openExternal() }).catch(() => { - saveConfig('settings.manualPageOpen', 'window') + saveConfig(configPaths.settings.manualPageOpen, 'window') openManual() }) } else { diff --git a/src/renderer/manage/store/bucketFileDb.ts b/src/renderer/manage/store/bucketFileDb.ts index 64fb24a..776e6f1 100644 --- a/src/renderer/manage/store/bucketFileDb.ts +++ b/src/renderer/manage/store/bucketFileDb.ts @@ -11,7 +11,7 @@ import Dexie, { Table } from 'dexie' */ export interface IFileCache { - key: string, + key: string value: any } diff --git a/src/renderer/pages/Gallery.vue b/src/renderer/pages/Gallery.vue index 7e4bbde..5564c48 100644 --- a/src/renderer/pages/Gallery.vue +++ b/src/renderer/pages/Gallery.vue @@ -499,6 +499,8 @@ import ALLApi from '@/apis/allApi' import { customRenameFormatTable, customStrMatch, customStrReplace } from '../manage/utils/common' import { picBedsCanbeDeleted } from '#/utils/static' import path from 'path' +import { configPaths } from '~/universal/utils/configPaths' +import { IPasteStyle } from '~/universal/types/enum' const images = ref([]) const dialogVisible = ref(false) @@ -731,7 +733,7 @@ function remove (item: ImgInfo) { type: 'warning' }).then(async () => { const file = await $$db.getById(item.id!) - if (await getConfig('settings.deleteCloudFile') && picBedsCanbeDeleted.includes(item?.type || 'placeholder')) { + if (await getConfig(configPaths.settings.deleteCloudFile) && picBedsCanbeDeleted.includes(item?.type || 'placeholder')) { const result = await ALLApi.delete(item) if (result) { ElNotification({ @@ -767,7 +769,7 @@ function remove (item: ImgInfo) { function handleDeleteCloudFile (val: ICheckBoxValueType) { saveConfig({ - 'settings.deleteCloudFile': val + [configPaths.settings.deleteCloudFile]: val }) } @@ -825,7 +827,7 @@ function multiRemove () { }).then(async () => { const files: IResult[] = [] const imageIDList = Object.keys(choosedList) - const isDeleteCloudFile = await getConfig('settings.deleteCloudFile') + const isDeleteCloudFile = await getConfig(configPaths.settings.deleteCloudFile) if (isDeleteCloudFile) { for (let i = 0; i < imageIDList.length; i++) { const key = imageIDList[i] @@ -919,12 +921,12 @@ function toggleHandleBar () { } async function handlePasteStyleChange (val: string) { - saveConfig('settings.pasteStyle', val) + saveConfig(configPaths.settings.pasteStyle, val) pasteStyle.value = val } function handleUseShortUrlChange (value: string) { - saveConfig('settings.useShortUrl', value === $T('UPLOAD_SHORT_URL')) + saveConfig(configPaths.settings.useShortUrl, value === $T('UPLOAD_SHORT_URL')) useShortUrl.value = value } @@ -1046,8 +1048,8 @@ onBeforeUnmount(() => { }) onActivated(async () => { - pasteStyle.value = (await getConfig('settings.pasteStyle')) || 'markdown' - useShortUrl.value = (await getConfig('settings.useShortUrl') ? $T('UPLOAD_SHORT_URL') : $T('UPLOAD_NORMAL_URL')) + pasteStyle.value = (await getConfig(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN + useShortUrl.value = (await getConfig(configPaths.settings.useShortUrl) ? $T('UPLOAD_SHORT_URL') : $T('UPLOAD_NORMAL_URL')) initDeleteCloud() }) diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 3ebb9fe..e011f6a 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -1735,7 +1735,7 @@ import pkg from 'root/package.json' // 事件常量 import { PICGO_OPEN_FILE, PICGO_OPEN_DIRECTORY, OPEN_URL, GET_PICBEDS, HIDE_DOCK } from '#/events/constants' -import { IRPCActionType } from '~/universal/types/enum' +import { IRPCActionType, ISartMode } from '~/universal/types/enum' // Electron 相关 import { @@ -1770,6 +1770,7 @@ import { invokeToMain } from '@/manage/utils/dataSender' // 内置重命名格式表 import { buildInRenameFormatTable } from '../manage/utils/common' +import { configPaths, ISartModeValues } from '~/universal/utils/configPaths' const imageProcessDialogVisible = ref(false) const activeName = ref<'system' | 'syncAndConfigure' | 'upload' | 'advanced' | 'upadte'>('system') @@ -1863,14 +1864,14 @@ function handleSaveConfig () { const formatConvertObjFilter = Object.fromEntries(formatConvertObjEntriesFilter) formatConvertObj.value = JSON.stringify(formatConvertObjFilter) compressForm.formatConvertObj = formatConvertObjFilter - saveConfig('buildIn.compress', toRaw(compressForm)) - saveConfig('buildIn.watermark', toRaw(waterMarkForm)) + saveConfig(configPaths.buildIn.compress, toRaw(compressForm)) + saveConfig(configPaths.buildIn.watermark, toRaw(waterMarkForm)) closeDialog() } async function initForm () { - const compress = await getConfig('buildIn.compress') - const watermark = await getConfig('buildIn.watermark') + const compress = await getConfig(configPaths.buildIn.compress) + const watermark = await getConfig(configPaths.buildIn.watermark) if (compress) { compressForm.quality = compress.quality ?? 100 compressForm.isConvert = compress.isConvert ?? false @@ -1986,10 +1987,6 @@ const customLink = reactive({ value: '![$fileName]($url)' }) -const shortKey = reactive({ - upload: '' -}) - const mainWindowWidth = ref(1200) const mainWindowHeight = ref(800) const rawPicGoSize = ref(false) @@ -2051,7 +2048,7 @@ const syncType = [ async function cancelSyncSetting () { syncVisible.value = false - sync.value = await getConfig('settings.sync') || { + sync.value = await getConfig(configPaths.settings.sync) || { type: 'github', username: '', repo: '', @@ -2065,7 +2062,7 @@ async function cancelSyncSetting () { function confirmSyncSetting () { saveConfig({ - 'settings.sync': sync.value + [configPaths.settings.sync]: sync.value }) syncVisible.value = false } @@ -2134,7 +2131,6 @@ async function initData () { currentLanguage.value = settings.language ?? 'zh-CN' currentStartMode.value = settings.startMode || 'quiet' customLink.value = settings.customLink || '![$fileName]($url)' - shortKey.upload = settings.shortKey.upload proxy.value = picBed.proxy || '' npmRegistry.value = settings.registry || '' npmProxy.value = settings.proxy || '' @@ -2152,7 +2148,7 @@ async function initData () { if (advancedRename.value.enable) { form.autoRename = false saveConfig({ - 'settings.autoRename': false + [configPaths.settings.autoRename]: false }) } sync.value = settings.sync || { @@ -2204,13 +2200,13 @@ function openLogSetting () { async function cancelCustomLink () { customLinkVisible.value = false - customLink.value = await getConfig('settings.customLink') || '![$fileName]($url)' + customLink.value = await getConfig(configPaths.settings.customLink) || '![$fileName]($url)' } function confirmCustomLink () { $customLink.value?.validate((valid: boolean) => { if (valid) { - saveConfig('settings.customLink', customLink.value) + saveConfig(configPaths.settings.customLink, customLink.value) customLinkVisible.value = false sendToMain('updateCustomLink') } else { @@ -2220,7 +2216,7 @@ function confirmCustomLink () { } function handleEncodeOutputURL (val: ICheckBoxValueType) { - saveConfig('settings.encodeOutputURL', val) + saveConfig(configPaths.settings.encodeOutputURL, val) const successNotification = new Notification($T('SETTINGS_ENCODE_OUTPUT_URL'), { body: $T('TIPS_SET_SUCCEED') }) @@ -2231,17 +2227,17 @@ function handleEncodeOutputURL (val: ICheckBoxValueType) { async function handleCancelAdvancedRename () { advancedRenameVisible.value = false - advancedRename.value = toRaw((await getConfig('buildIn.rename')) || { + advancedRename.value = toRaw((await getConfig(configPaths.buildIn.rename)) || { enable: false, format: '{filename}' }) } function handleSaveAdvancedRename () { - saveConfig('buildIn.rename', toRaw(advancedRename.value)) + saveConfig(configPaths.buildIn.rename, toRaw(advancedRename.value)) if (advancedRename.value.enable) { form.autoRename = false - saveConfig('settings.autoRename', false) + saveConfig(configPaths.settings.autoRename, false) } advancedRenameVisible.value = false const successNotification = new Notification($T('SETTINGS_ADVANCED_RENAME'), { @@ -2254,15 +2250,15 @@ function handleSaveAdvancedRename () { async function cancelProxy () { proxyVisible.value = false - proxy.value = await getConfig('picBed.proxy') || '' + proxy.value = await getConfig(configPaths.picBed.proxy) || '' } function confirmProxy () { proxyVisible.value = false saveConfig({ - 'picBed.proxy': proxy.value, - 'settings.proxy': npmProxy.value, - 'settings.registry': npmRegistry.value + [configPaths.picBed.proxy]: proxy.value, + [configPaths.settings.proxy]: npmProxy.value, + [configPaths.settings.registry]: npmRegistry.value }) const successNotification = new Notification($T('SETTINGS_SET_PROXY_AND_MIRROR'), { body: $T('TIPS_SET_SUCCEED') @@ -2290,15 +2286,15 @@ function handleMigrateFromPicGo () { } function updateHelperChange (val: ICheckBoxValueType) { - saveConfig('settings.showUpdateTip', val) + saveConfig(configPaths.settings.showUpdateTip, val) } function autoImportChange (val: ICheckBoxValueType) { - saveConfig('settings.autoImport', val) + saveConfig(configPaths.settings.autoImport, val) } function handleAutoImportPicBedChange (val: string[]) { - saveConfig('settings.autoImportPicBed', val) + saveConfig(configPaths.settings.autoImportPicBed, val) } function handleHideDockChange (val: ICheckBoxValueType) { @@ -2307,16 +2303,16 @@ function handleHideDockChange (val: ICheckBoxValueType) { form.isHideDock = false return } - saveConfig('settings.isHideDock', val) + saveConfig(configPaths.settings.isHideDock, val) sendToMain(HIDE_DOCK, val) } function useBuiltinClipboardChange (val: ICheckBoxValueType) { - saveConfig('settings.useBuiltinClipboard', val) + saveConfig(configPaths.settings.useBuiltinClipboard, val) } function handleIsAutoListenClipboard (val: ICheckBoxValueType) { - saveConfig('settings.isAutoListenClipboard', val) + saveConfig(configPaths.settings.isAutoListenClipboard, val) } function handleShowPicBedListChange (val: ICheckBoxValueType[]) { @@ -2329,37 +2325,37 @@ function handleShowPicBedListChange (val: ICheckBoxValueType[]) { return item }) saveConfig({ - 'picBed.list': list + [configPaths.picBed.list]: list }) sendToMain(GET_PICBEDS) } function handleAutoStartChange (val: ICheckBoxValueType) { - saveConfig('settings.autoStart', val) + saveConfig(configPaths.settings.autoStart, val) sendToMain('autoStart', val) } function handleDeleteCloudFile (val: ICheckBoxValueType) { saveConfig({ - 'settings.deleteCloudFile': val + [configPaths.settings.deleteCloudFile]: val }) } function handleDeleteLocalFile (val: ICheckBoxValueType) { saveConfig({ - 'settings.deleteLocalFile': val + [configPaths.settings.deleteLocalFile]: val }) } function handleRename (val: ICheckBoxValueType) { saveConfig({ - 'settings.rename': val + [configPaths.settings.rename]: val }) } function handleAutoRename (val: ICheckBoxValueType) { saveConfig({ - 'settings.autoRename': val + [configPaths.settings.autoRename]: val }) } @@ -2389,19 +2385,19 @@ function cancelCheckVersion () { } function handleEnableWebServerChange (val: ICheckBoxValueType) { - saveConfig('settings.enableWebServer', val) + saveConfig(configPaths.settings.enableWebServer, val) } function handleWebServerHostChange (val: string) { - saveConfig('settings.webServerHost', val) + saveConfig(configPaths.settings.webServerHost, val) } function handleWebServerPortChange (val?: number, oldVal?: number) { - saveConfig('settings.webServerPort', Number(val) || 37777) + saveConfig(configPaths.settings.webServerPort, Number(val) || 37777) } function handleWebServerPathChange (val: string) { - saveConfig('settings.webServerPath', val) + saveConfig(configPaths.settings.webServerPath, val) } function confirmWebServerSetting () { @@ -2413,25 +2409,25 @@ function confirmWebServerSetting () { } function handleServerKeyChange (val: string) { - saveConfig('settings.serverKey', val) + saveConfig(configPaths.settings.serverKey, val) } function handleUploadNotification (val: ICheckBoxValueType) { saveConfig({ - 'settings.uploadNotification': val + [configPaths.settings.uploadNotification]: val }) } function handleUploadResultNotification (val: ICheckBoxValueType) { saveConfig({ - 'settings.uploadResultNotification': val + [configPaths.settings.uploadResultNotification]: val }) } async function cancelWindowSize () { mainWindowSizeVisible.value = false - mainWindowWidth.value = await getConfig('settings.mainWindowWidth') || 1200 - mainWindowHeight.value = await getConfig('settings.mainWindowHeight') || 800 + mainWindowWidth.value = await getConfig(configPaths.settings.mainWindowWidth) || 1200 + mainWindowHeight.value = await getConfig(configPaths.settings.mainWindowHeight) || 800 } async function confirmWindowSize () { @@ -2439,8 +2435,8 @@ async function confirmWindowSize () { const width = enforceNumber(mainWindowWidth.value) const height = enforceNumber(mainWindowHeight.value) saveConfig({ - 'settings.mainWindowWidth': rawPicGoSize.value ? 800 : width < 100 ? 100 : width, - 'settings.mainWindowHeight': rawPicGoSize.value ? 450 : height < 100 ? 100 : height + [configPaths.settings.mainWindowWidth]: rawPicGoSize.value ? 800 : width < 100 ? 100 : width, + [configPaths.settings.mainWindowHeight]: rawPicGoSize.value ? 450 : height < 100 ? 100 : height }) const successNotification = new Notification($T('SETTINGS_MAIN_WINDOW_SIZE'), { @@ -2452,15 +2448,15 @@ async function confirmWindowSize () { } function handleAutoCloseMainWindowChange (val: ICheckBoxValueType) { - saveConfig('settings.autoCloseMainWindow', val) + saveConfig(configPaths.settings.autoCloseMainWindow, val) } function handleAutoCloseMiniWindowChange (val: ICheckBoxValueType) { - saveConfig('settings.autoCloseMiniWindow', val) + saveConfig(configPaths.settings.autoCloseMiniWindow, val) } function handleMiniWindowOntop (val: ICheckBoxValueType) { - saveConfig('settings.miniWindowOntop', val) + saveConfig(configPaths.settings.miniWindowOntop, val) $message.info($T('TIPS_NEED_RELOAD')) } @@ -2468,18 +2464,18 @@ async function handleMiniIconPath (evt: Event) { const result = await invokeToMain('openFileSelectDialog') if (result && result[0]) { form.customMiniIcon = result[0] - saveConfig('settings.customMiniIcon', form.customMiniIcon) + saveConfig(configPaths.settings.customMiniIcon, form.customMiniIcon) $message.info($T('TIPS_NEED_RELOAD')) } } function handleIsCustomMiniIcon (val: ICheckBoxValueType) { - saveConfig('settings.isCustomMiniIcon', val) + saveConfig(configPaths.settings.isCustomMiniIcon, val) $message.info($T('TIPS_NEED_RELOAD')) } function handleAutoCopyUrl (val: ICheckBoxValueType) { - saveConfig('settings.autoCopy', val) + saveConfig(configPaths.settings.autoCopy, val) const successNotification = new Notification($T('SETTINGS_AUTO_COPY_URL_AFTER_UPLOAD'), { body: $T('TIPS_SET_SUCCEED') }) @@ -2489,7 +2485,7 @@ function handleAutoCopyUrl (val: ICheckBoxValueType) { } function handleUseShortUrl (val: ICheckBoxValueType) { - saveConfig('settings.useShortUrl', val) + saveConfig(configPaths.settings.useShortUrl, val) const successNotification = new Notification($T('SETTINGS_SHORT_URL'), { body: $T('TIPS_SET_SUCCEED') }) @@ -2499,27 +2495,27 @@ function handleUseShortUrl (val: ICheckBoxValueType) { } function handleShortUrlServerChange (val: string) { - saveConfig('settings.shortUrlServer', val) + saveConfig(configPaths.settings.shortUrlServer, val) } function handleC1nTokenChange (val: string) { - saveConfig('settings.c1nToken', val) + saveConfig(configPaths.settings.c1nToken, val) } function handleYourlsDomainChange (val: string) { - saveConfig('settings.yourlsDomain', val) + saveConfig(configPaths.settings.yourlsDomain, val) } function handleYourlsSignatureChange (val: string) { - saveConfig('settings.yourlsSignature', val) + saveConfig(configPaths.settings.yourlsSignature, val) } function handleCfWorkerHostChange (val: string) { - saveConfig('settings.cfWorkerHost', val) + saveConfig(configPaths.settings.cfWorkerHost, val) } function handleAesPasswordChange (val: string) { - saveConfig('settings.aesPassword', val || 'PicList-aesPassword') + saveConfig(configPaths.settings.aesPassword, val || 'PicList-aesPassword') } function confirmLogLevelSetting () { @@ -2527,8 +2523,8 @@ function confirmLogLevelSetting () { return $message.error($T('TIPS_PLEASE_CHOOSE_LOG_LEVEL')) } saveConfig({ - 'settings.logLevel': form.logLevel, - 'settings.logFileSizeLimit': form.logFileSizeLimit + [configPaths.settings.logLevel]: form.logLevel, + [configPaths.settings.logFileSizeLimit]: form.logFileSizeLimit }) const successNotification = new Notification($T('SETTINGS_SET_LOG_FILE'), { body: $T('TIPS_SET_SUCCEED') @@ -2541,8 +2537,8 @@ function confirmLogLevelSetting () { async function cancelLogLevelSetting () { logFileVisible.value = false - let logLevel = await getConfig('settings.logLevel') - const logFileSizeLimit = await getConfig('settings.logFileSizeLimit') || 10 + let logLevel = await getConfig(configPaths.settings.logLevel) + const logFileSizeLimit = await getConfig(configPaths.settings.logFileSizeLimit) || 10 if (!Array.isArray(logLevel)) { if (logLevel && logLevel.length > 0) { logLevel = [logLevel] @@ -2617,7 +2613,7 @@ async function downloadAll () { function confirmServerSetting () { server.value.port = parseInt(server.value.port as unknown as string, 10) saveConfig({ - 'settings.server': server.value + [configPaths.settings.server]: server.value }) const successNotification = new Notification($T('SETTINGS_SET_PICGO_SERVER'), { body: $T('TIPS_SET_SUCCEED') @@ -2631,7 +2627,7 @@ function confirmServerSetting () { async function cancelServerSetting () { serverVisible.value = false - server.value = await getConfig('settings.server') || { + server.value = await getConfig(configPaths.settings.server) || { port: 36677, host: '0.0.0.0', enable: true @@ -2662,28 +2658,28 @@ function handleLevelDisabled (val: string) { function handleLanguageChange (val: string) { i18nManager.setCurrentLanguage(val) saveConfig({ - 'settings.language': val + [configPaths.settings.language]: val }) sendToMain(GET_PICBEDS) } -function handleStartModeChange (val: 'quiet' | 'mini' | 'main' | 'no-tray') { - if (val === 'no-tray') { +function handleStartModeChange (val: ISartModeValues) { + if (val === ISartMode.NO_TRAY) { if (form.isHideDock) { ElMessage.warning($T('SETTINGS_ISHIDEDOCK_TIPS')) - currentStartMode.value = 'quiet' + currentStartMode.value = ISartMode.QUIET return } $message.info($T('TIPS_NEED_RELOAD')) } saveConfig({ - 'settings.startMode': val + [configPaths.settings.startMode]: val }) } function handleManualPageOpenChange (val: string) { saveConfig({ - 'settings.manualPageOpen': val + [configPaths.settings.manualPageOpen]: val }) } diff --git a/src/renderer/pages/Plugin.vue b/src/renderer/pages/Plugin.vue index 413e349..b3a7345 100644 --- a/src/renderer/pages/Plugin.vue +++ b/src/renderer/pages/Plugin.vue @@ -274,6 +274,7 @@ import axios from 'axios' // 枚举类型声明 import { IRPCActionType } from '~/universal/types/enum' +import { configPaths } from '~/universal/utils/configPaths' const $confirm = ElMessageBox.confirm const searchText = ref('') @@ -416,7 +417,7 @@ onBeforeMount(async () => { }) getPluginList() getSearchResult = debounce(_getSearchResult, 50) - needReload.value = await getConfig('needReload') || false + needReload.value = await getConfig(configPaths.needReload) || false }) async function buildContextMenu (plugin: IPicGoPlugin) { @@ -577,19 +578,19 @@ function handleSearchResult (item: INPMSearchResultObject) { // restore Uploader & Transformer async function handleRestoreState (item: string, name: string) { if (item === 'uploader') { - const current = await getConfig('picBed.current') + const current = await getConfig(configPaths.picBed.current) if (current === name) { saveConfig({ - 'picBed.current': 'smms', - 'picBed.uploader': 'smms' + [configPaths.picBed.current]: 'smms', + [configPaths.picBed.uploader]: 'smms' }) } } if (item === 'transformer') { - const current = await getConfig('picBed.transformer') + const current = await getConfig(configPaths.picBed.transformer) if (current === name) { saveConfig({ - 'picBed.transformer': 'path' + [configPaths.picBed.transformer]: 'path' }) } } diff --git a/src/renderer/pages/ShortKey.vue b/src/renderer/pages/ShortKey.vue index 793592a..f928a8c 100644 --- a/src/renderer/pages/ShortKey.vue +++ b/src/renderer/pages/ShortKey.vue @@ -132,6 +132,7 @@ import { getConfig, sendToMain } from '@/utils/dataSender' // 国际化函数 import { T as $T } from '@/i18n' +import { configPaths } from '~/universal/utils/configPaths' const list = ref([]) const keyBindingVisible = ref(false) @@ -140,7 +141,7 @@ const shortKey = ref('') const currentIndex = ref(0) onBeforeMount(async () => { - const shortKeyConfig = (await getConfig('settings.shortKey'))! + const shortKeyConfig = (await getConfig(configPaths.settings.shortKey._path))! list.value = Object.keys(shortKeyConfig).map(item => { return { ...shortKeyConfig[item], diff --git a/src/renderer/pages/TrayPage.vue b/src/renderer/pages/TrayPage.vue index 24a1bcf..c2e14ca 100644 --- a/src/renderer/pages/TrayPage.vue +++ b/src/renderer/pages/TrayPage.vue @@ -88,6 +88,7 @@ import { getConfig, sendToMain } from '@/utils/dataSender' // 工具函数 import { handleUrlEncode } from '#/utils/common' +import { configPaths } from '~/universal/utils/configPaths' const files = ref[]>([]) const notification = reactive({ @@ -128,8 +129,8 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => { } async function copyTheLink (item: ImgInfo) { - const pasteStyle = await getConfig('settings.pasteStyle') || IPasteStyle.MARKDOWN - const customLink = await getConfig('settings.customLink') + const pasteStyle = await getConfig(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + const customLink = await getConfig(configPaths.settings.customLink) const txt = await pasteTemplate(pasteStyle, item, customLink) clipboard.writeText(txt) const myNotification = new Notification(notification.title, notification) @@ -143,10 +144,10 @@ async function pasteTemplate (style: IPasteStyle, item: ImgInfo, customLink: str if (item.type === 'aws-s3' || item.type === 'aws-s3-plist') { url = item.imgUrl || item.url || '' } - if ((await getConfig('settings.encodeOutputURL')) === true) { + if ((await getConfig(configPaths.settings.encodeOutputURL)) === true) { url = handleUrlEncode(url) } - const useShortUrl = await getConfig('settings.useShortUrl') || false + const useShortUrl = await getConfig(configPaths.settings.useShortUrl) || false if (useShortUrl) { url = await ipcRenderer.invoke('getShortUrl', url) } diff --git a/src/renderer/pages/Upload.vue b/src/renderer/pages/Upload.vue index 09ef506..06060a0 100644 --- a/src/renderer/pages/Upload.vue +++ b/src/renderer/pages/Upload.vue @@ -466,7 +466,8 @@ import { useRouter } from 'vue-router' // 路由配置常量 import { PICBEDS_PAGE } from '@/router/config' -import { IRPCActionType } from '~/universal/types/enum' +import { IPasteStyle, IRPCActionType } from '~/universal/types/enum' +import { configPaths } from '~/universal/utils/configPaths' const $router = useRouter() @@ -538,14 +539,14 @@ function handleSaveConfig () { const formatConvertObjFilter = Object.fromEntries(formatConvertObjEntriesFilter) formatConvertObj.value = JSON.stringify(formatConvertObjFilter) compressForm.formatConvertObj = formatConvertObjFilter - saveConfig('buildIn.compress', toRaw(compressForm)) - saveConfig('buildIn.watermark', toRaw(waterMarkForm)) + saveConfig(configPaths.buildIn.compress, toRaw(compressForm)) + saveConfig(configPaths.buildIn.watermark, toRaw(waterMarkForm)) closeDialog() } async function initData () { - const compress = await getConfig('buildIn.compress') - const watermark = await getConfig('buildIn.watermark') + const compress = await getConfig(configPaths.buildIn.compress) + const watermark = await getConfig(configPaths.buildIn.watermark) if (compress) { compressForm.quality = compress.quality ?? 100 compressForm.isConvert = compress.isConvert ?? false @@ -637,7 +638,7 @@ function onProgressChange (val: number) { async function handlePicBedNameClick (_picBedName: string, picBedConfigName: string | undefined) { const formatedpicBedConfigName = picBedConfigName || 'Default' - const currentPicBed = await getConfig('picBed.current') + const currentPicBed = await getConfig(configPaths.picBed.current) const currentPicBedConfig = await getConfig(`uploader.${currentPicBed}`) as any || {} const configList = await triggerRPC(IRPCActionType.GET_PICBED_CONFIG_LIST, currentPicBed) const currentConfigList = configList?.configList ?? [] @@ -721,23 +722,23 @@ function ipcSendFiles (files: FileList) { } async function getPasteStyle () { - pasteStyle.value = await getConfig('settings.pasteStyle') || 'markdown' - customLink.value = await getConfig('settings.customLink') || '![$fileName]($url)' + pasteStyle.value = await getConfig(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + customLink.value = await getConfig(configPaths.settings.customLink) || '![$fileName]($url)' } async function getUseShortUrl () { - useShortUrl.value = await getConfig('settings.useShortUrl') || false + useShortUrl.value = await getConfig(configPaths.settings.useShortUrl) || false } async function handleUseShortUrlChange () { saveConfig({ - 'settings.useShortUrl': useShortUrl.value + [configPaths.settings.useShortUrl]: useShortUrl.value }) } function handlePasteStyleChange (val: string | number | boolean) { saveConfig({ - 'settings.pasteStyle': val + [configPaths.settings.pasteStyle]: val }) } @@ -766,7 +767,7 @@ function handleInputBoxValue (val: string) { } async function getDefaultPicBed () { - const currentPicBed = await getConfig('picBed.current') + const currentPicBed = await getConfig(configPaths.picBed.current) picBed.value.forEach(item => { if (item.type === currentPicBed) { picBedName.value = item.name diff --git a/src/renderer/pages/UploaderConfigPage.vue b/src/renderer/pages/UploaderConfigPage.vue index c8036ad..95040dd 100644 --- a/src/renderer/pages/UploaderConfigPage.vue +++ b/src/renderer/pages/UploaderConfigPage.vue @@ -118,6 +118,7 @@ import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config' // 状态管理 import { useStore } from '@/hooks/useStore' +import { configPaths } from '~/universal/utils/configPaths' const $router = useRouter() const $route = useRoute() @@ -187,8 +188,8 @@ function addNewConfig () { function setDefaultPicBed (type: string) { saveConfig({ - 'picBed.current': type, - 'picBed.uploader': type + [configPaths.picBed.current]: type, + [configPaths.picBed.uploader]: type }) store?.setDefaultPicBed(type) diff --git a/src/renderer/pages/picbeds/index.vue b/src/renderer/pages/picbeds/index.vue index bf614cc..7583695 100644 --- a/src/renderer/pages/picbeds/index.vue +++ b/src/renderer/pages/picbeds/index.vue @@ -132,6 +132,7 @@ import dayjs from 'dayjs' // Element Plus 下拉菜单组件 import { ElDropdown, ElMessage } from 'element-plus' +import { configPaths } from '~/universal/utils/configPaths' const type = ref('') const config = ref([]) @@ -227,8 +228,8 @@ function handleNameClick () { async function handleCopyApi () { try { - const { port = 36677, host = '127.0.0.1' } = await getConfig('settings.server') || {} - const serverKey = await getConfig('settings.serverKey') || '' + const { port = 36677, host = '127.0.0.1' } = await getConfig(configPaths.settings.server) || {} + const serverKey = await getConfig(configPaths.settings.serverKey) || '' const uploader = await getConfig('uploader') as IStringKeyMap || {} const picBedConfigList = uploader[$route.params.type as string].configList || [] const picBedConfig = picBedConfigList.find((item: IUploaderConfigListItem) => item._id === $route.params.configId) diff --git a/src/renderer/store/index.ts b/src/renderer/store/index.ts index 015392e..ae12f9d 100644 --- a/src/renderer/store/index.ts +++ b/src/renderer/store/index.ts @@ -1,14 +1,15 @@ import { reactive, InjectionKey, readonly, App, UnwrapRef, ref } from 'vue' import { saveConfig } from '@/utils/dataSender' +import { configPaths } from '~/universal/utils/configPaths' export interface IState { - defaultPicBed: string; + defaultPicBed: string } export interface IStore { state: UnwrapRef - setDefaultPicBed: (type: string) => void; - updateForceUpdateTime: () => void; + setDefaultPicBed: (type: string) => void + updateForceUpdateTime: () => void } export const storeKey: InjectionKey = Symbol('store') @@ -23,8 +24,8 @@ const forceUpdateTime = ref(Date.now()) // methods const setDefaultPicBed = (type: string) => { saveConfig({ - 'picBed.current': type, - 'picBed.uploader': type + [configPaths.picBed.current]: type, + [configPaths.picBed.uploader]: type }) state.defaultPicBed = type } diff --git a/src/universal/types/electron.d.ts b/src/universal/types/electron.d.ts index fb2c1ea..d04caeb 100644 --- a/src/universal/types/electron.d.ts +++ b/src/universal/types/electron.d.ts @@ -5,7 +5,7 @@ declare type IWindowList = import('./enum').IWindowList declare interface IWindowListItem { isValid: boolean multiple: boolean - options: () => IBrowserWindowOptions, + options: () => IBrowserWindowOptions callback: (window: BrowserWindow, windowManager: IWindowManager) => void } diff --git a/src/universal/types/enum.ts b/src/universal/types/enum.ts index 8ed21c7..d2e8cbb 100644 --- a/src/universal/types/enum.ts +++ b/src/universal/types/enum.ts @@ -96,3 +96,22 @@ export enum IToolboxItemCheckStatus { SUCCESS = 'success', ERROR = 'error', } + +export enum ISartMode { + QUIET = 'quiet', + MINI = 'mini', + MAIN = 'main', + NO_TRAY = 'no-tray' +} + +export enum II18nLanguage { + ZH_CN = 'zh-CN', + ZH_TW = 'zh-TW', + EN = 'en' +} + +export enum IShortUrlServer { + C1N = 'c1n', + YOURLS = 'yourls', + CFWORKER = 'cf_worker' +} diff --git a/src/universal/types/manage.d.ts b/src/universal/types/manage.d.ts index 461ed30..a818374 100644 --- a/src/universal/types/manage.d.ts +++ b/src/universal/types/manage.d.ts @@ -4,39 +4,39 @@ import { ILogger } from "piclist/dist/types"; type Undefinable = T | undefined; declare interface ManageError extends Error { - code?: number; - param?: string; - stack?: string; - picbed?: string; + code?: number + param?: string + stack?: string + picbed?: string } type PicBedMangeConfig = IStringKeyMap; interface PicBedManageConfigMap { - [key: string]: PicBedMangeConfig; + [key: string]: PicBedMangeConfig } interface ManageApiType { /** * logger */ - logger: ILogger; + logger: ILogger /** * congif path */ - configPath: string; + configPath: string /** * basedir */ - baseDir: string; + baseDir: string /** * current picBed name */ - currentPicBed: string; + currentPicBed: string /** * current picBed config */ - currentPicBedConfig: PicBedMangeConfig; + currentPicBedConfig: PicBedMangeConfig /** * get manage config */ @@ -62,136 +62,136 @@ interface ManageApiType { */ getBucketListRecursively: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get bucket list */ getBucketListBackstage: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get bucket list */ getBucketList: ( param?: IStringKeyMap - ) => Promise; + ) => Promise getBucketDomain: ( param: IStringKeyMap - ) => Promise; + ) => Promise /** * get bucket info */ getBucketInfo: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * create bucket */ createBucket: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * delete bucket */ deleteBucket: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get Operator list * specific for upyun */ getOperatorList: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * add Operator * specific for upyun */ addOperator: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * delete Operator * specific for upyun */ deleteOperator: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get bucket ACL policy */ getBucketAclPolicy: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * set bucket ACL policy */ setBucketAclPolicy: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get bucket file list */ getBucketFileList: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * delete bucket file */ deleteBucketFile: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * delete folder */ deleteBucketFolder: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * rename bucket file */ renameBucketFile: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * download bucket file */ downloadBucketFile: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * copy or move between buckets */ copyMoveBucketFile: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * create folder */ createBucketFolder: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * upload file */ uploadBucketFile: ( param?: IStringKeyMap - ) => Promise; + ) => Promise /** * get presigned url */ getPreSignedUrl: ( param?: IStringKeyMap - ) => Promise; + ) => Promise } /** PicList 存储管理功能配置文件类型定义 */ interface ManageConfigType { picBed: { - [others: string]: any; - }; + [others: string]: any + } settings: { - [others: string]: any; - }; - [others: string]: any; + [others: string]: any + } + [others: string]: any } diff --git a/src/universal/types/types.d.ts b/src/universal/types/types.d.ts index 62fe170..070fa6c 100644 --- a/src/universal/types/types.d.ts +++ b/src/universal/types/types.d.ts @@ -35,6 +35,18 @@ interface IServerConfig { enable: boolean } +interface ISyncConfig { + type: string + file?: string + username: string + repo: string + branch: string + token: string + endpoint?: string + proxy?: string + interval?: number +} + // Image && PicBed interface ImgInfo { buffer?: Buffer @@ -86,25 +98,25 @@ interface IOldShortKeyConfigs { } interface IKeyCommandType { - key: string, + key: string command: string } // Main process interface IBrowserWindowOptions { - height: number, - width: number, - show: boolean, - fullscreenable: boolean, - resizable: boolean, + height: number + width: number + show: boolean + fullscreenable: boolean + resizable: boolean webPreferences: { - nodeIntegration: boolean, - nodeIntegrationInWorker: boolean, - contextIsolation: boolean, + nodeIntegration: boolean + nodeIntegrationInWorker: boolean + contextIsolation: boolean backgroundThrottling: boolean webSecurity?: boolean - }, - vibrancy?: string | any, + } + vibrancy?: string | any frame?: boolean center?: boolean title?: string @@ -275,67 +287,148 @@ interface IShortKeyHandlerObj { type IShortKeyHandler = (ctx: ICtx, guiApi?: IGuiApi) => Promise +type PartialKeys = Omit & Partial> + interface shortKeyHandlerMap { from: string handle: IShortKeyHandler } // PicBeds +interface ITelegraphConfig { + proxy?: string +} + +interface ILocalConfig { + path: string + customUrl?: string + webPath?: string +} + interface IAliYunConfig { accessKeyId: string - accessKeySecret: string, - bucket: string, - area: string, - path: string, + accessKeySecret: string + bucket: string + area: string + path: string customUrl: string options: string } interface IGitHubConfig { - repo: string, - token: string, - path: string, - customUrl: string, + repo: string + token: string + path: string + customUrl: string branch: string } interface IImgurConfig { - clientId: string, + clientId: string proxy: string + username: string + accessToken: string + album: string } interface IQiniuConfig { - accessKey: string, - secretKey: string, - bucket: string, - url: string, - area: string, - options: string, + accessKey: string + secretKey: string + bucket: string + url: string + area: 'z0' | 'z1' | 'z2' | 'na0' | 'as0' | string + options: string path: string } interface ISMMSConfig { token: string + backupDomain?: string } interface ITcYunConfig { - secretId: string, - secretKey: string, - bucket: string, - appId: string, - area: string, - path: string, - customUrl: string, - version: 'v4' | 'v5', + secretId: string + secretKey: string + bucket: string + appId: string + endpoint: string + area: string + path: string + customUrl: string + version: 'v4' | 'v5' options: string + slim: boolean } interface IUpYunConfig { - bucket: string, - operator: string, - password: string, - options: string, + bucket: string + operator: string + password: string + options: string path: string + url: string + antiLeechToken: string + expireTime: number + endpoint: string +} + +interface IWebdavPlistConfig { + host: string + sslEnabled: boolean + username: string + password: string + path: string + webpath: string + customUrl: string + authType: string + options: string +} + +interface ISftpPlistConfig { + host: string + port?: number + username: string + password?: string + privateKey?: string + passphrase?: string + uploadPath?: string + customUrl?: string + webPath?: string + fileUser?: string + fileMode?: string + dirMode?: string +} + +interface IPicListConfig { + host: string + port?: number + picbed?: string + configName?: string + serverKey?: string +} + +interface ILskyConfig { + version: string + host: string + token: string + strategyId: string + albumId: string + permission: IStringKeyMap +} + +interface IAwsS3PListUserConfig { + accessKeyID: string + secretAccessKey: string + bucketName: string + uploadPath: string + region?: string + endpoint?: string + proxy?: string + urlPrefix?: string + pathStyleAccess?: boolean + rejectUnauthorized?: boolean + acl?: string + disableBucketPrefixToURL?: boolean | string } type ILoggerType = string | Error | boolean | number | undefined @@ -362,9 +455,9 @@ type ILogArgvType = string | number type ILogArgvTypeWithError = ILogArgvType | Error interface IMiniWindowPos { - x: number, - y: number, - height: number, + x: number + y: number + height: number width: number } diff --git a/src/universal/types/view.d.ts b/src/universal/types/view.d.ts index a4d875d..050b787 100644 --- a/src/universal/types/view.d.ts +++ b/src/universal/types/view.d.ts @@ -13,30 +13,30 @@ interface ISettingForm { autoCopyUrl: boolean checkBetaUpdate: boolean useBuiltinClipboard: boolean - language: string - logFileSizeLimit: number, - deleteCloudFile: boolean, - isCustomMiniIcon: boolean, - customMiniIcon: string, - isHideDock: boolean, - autoImport: boolean, - autoImportPicBed: string[], - encodeOutputURL: boolean, - isAutoListenClipboard: boolean, - useShortUrl: boolean, - c1nToken: string, - shortUrlServer: string, - yourlsDomain: string, - yourlsSignature: string, - cfWorkerHost: string, - deleteLocalFile: boolean, - serverKey: string, - aesPassword: string, - manualPageOpen: string, - enableWebServer: boolean, - webServerHost: string, - webServerPort: number, - webServerPath: string, + language: 'zh-CN' | 'zh-TW' | 'en' + logFileSizeLimit: number + deleteCloudFile: boolean + isCustomMiniIcon: boolean + customMiniIcon: string + isHideDock: boolean + autoImport: boolean + autoImportPicBed: string[] + encodeOutputURL: boolean + isAutoListenClipboard: boolean + useShortUrl: boolean + c1nToken: string + shortUrlServer: string + yourlsDomain: string + yourlsSignature: string + cfWorkerHost: string + deleteLocalFile: boolean + serverKey: string + aesPassword: string + manualPageOpen: 'window' | 'browser' + enableWebServer: boolean + webServerHost: string + webServerPort: number + webServerPath: string } interface IShortKeyMap { diff --git a/src/universal/utils/common.ts b/src/universal/utils/common.ts index 94f516b..3d75060 100644 --- a/src/universal/utils/common.ts +++ b/src/universal/utils/common.ts @@ -33,7 +33,15 @@ export const enforceNumber = (num: number | string) => isNaN(+num) ? 0 : +num export const isDev = process.env.NODE_ENV === 'development' export const trimValues = (obj: T): {[K in keyof T]: T[K] extends string ? string : T[K]} => { - return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])) as {[K in keyof T]: T[K] extends string ? string : T[K]} + return Object + .fromEntries( + Object + .entries(obj) + .map(([key, value]) => + [key, typeof value === 'string' + ? value.trim() + : value]) + ) as {[K in keyof T]: T[K] extends string ? string : T[K]} } export function isNeedToShorten (alias: string, cutOff = 20) { @@ -54,7 +62,9 @@ export function safeSliceF (str:string, total: number) { } export function encodeFilePath (filePath: string) { - filePath = filePath.replace(/\\/g, '/') - const parts = filePath.split('/') - return parts.map(encodeURIComponent).join('/') + return filePath + .replace(/\\/g, '/') + .split('/') + .map(encodeURIComponent) + .join('/') } diff --git a/src/universal/utils/configPaths.ts b/src/universal/utils/configPaths.ts new file mode 100644 index 0000000..c6cb624 --- /dev/null +++ b/src/universal/utils/configPaths.ts @@ -0,0 +1,257 @@ + +import { II18nLanguage, IPasteStyle, ISartMode, IShortUrlServer } from '../types/enum' +import { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist' + +export type ISartModeValues = typeof ISartMode[keyof typeof ISartMode] +export type IPasteStyleValues = typeof IPasteStyle[keyof typeof IPasteStyle] +export type II18nLanguageValues = typeof II18nLanguage[keyof typeof II18nLanguage] +export type IShortUrlServerValues = typeof IShortUrlServer[keyof typeof IShortUrlServer] +export type manualPageOpenType = 'window' | 'browser' + +interface IPicGoPlugins { + [key: `picgo-plugin-${string}`]: boolean +} + +export interface IConfigStruct { + picBed: { + uploader: string, + current?: string, + smms?: ISMMSConfig, + qiniu?: IQiniuConfig, + upyun?: IUpYunConfig + tcyun?: ITcYunConfig + github?: IGitHubConfig + aliyun?: IAliYunConfig + imgur?: IImgurConfig + webdavplist?: IWebdavPlistConfig + local?: ILocalConfig + sftpplist?: ISftpPlistConfig + telegraphplist?: ITelegraphConfig + lskyplist?: ILskyConfig + 'aws-s3-plist': IAwsS3PListUserConfig + proxy?: string + transformer?: string + list: IPicBedType[] + [others: string]: any + }, + settings: { + shortKey: { + [key: string]: IShortKeyConfig + } + logLevel: string[] + logPath: string + logFileSizeLimit: number + isAutoListenClipboard: boolean + isListeningClipboard: boolean + showUpdateTip: boolean + miniWindowPosition: [number, number] + miniWindowOntop: boolean + mainWindowWidth: number + mainWindowHeight: number + isHideDock: boolean + autoCloseMiniWindow: boolean + autoCloseMainWindow: boolean + isCustomMiniIcon: boolean + customMiniIcon: string + startMode: ISartModeValues + autoRename: boolean + deleteCloudFile: boolean + server: IServerConfig + serverKey: string + pasteStyle: IPasteStyleValues + aesPassword: string + rename: boolean + sync: ISyncConfig + tempDirPath: string + language: II18nLanguageValues + customLink: string + manualPageOpen: manualPageOpenType + encodeOutputURL: boolean + useShortUrl: boolean + shortUrlServer: IShortUrlServerValues + c1nToken: string + cfWorkerHost: string + yourlsDomain: string + yourlsSignature: string + isSilentNotice: boolean + proxy: string + registry: string + autoCopy: boolean + enableWebServer: boolean + webServerHost: string + webServerPort: number + webServerPath: string + deleteLocalFile: boolean + uploadResultNotification: boolean + uploadNotification: boolean + useBuiltinClipboard: boolean + autoStart: boolean + autoImport: boolean + autoImportPicBed: string[] + } + needReload: boolean + picgoPlugins: IPicGoPlugins + uploader: IUploaderConfig + buildIn: { + compress: IBuildInCompressOptions + waterMark: IBuildInWaterMarkOptions + rename: { + enable: boolean + format: string + } + } + debug: boolean + PICGO_ENV: string +} + +interface IConfigPaths { + picBed: { + current: string + uploader: string + proxy: string + transformer: string + list: string + }, + settings: { + shortKey: { + _path: string + 'picgo:upload': string + } + logLevel: string + logPath: string + logFileSizeLimit: string + isAutoListenClipboard: string + isListeningClipboard: string + showUpdateTip: string + miniWindowPosition: string + miniWindowOntop: string + isHideDock: string + mainWindowWidth: string + mainWindowHeight: string + autoCloseMiniWindow: string + autoCloseMainWindow: string + isCustomMiniIcon: string + customMiniIcon: string + startMode: string + autoRename: string + deleteCloudFile: string + server: string + serverKey: string + pasteStyle: string + aesPassword: string + rename: string + sync: string + tempDirPath: string + language: string + customLink: string + manualPageOpen: string + encodeOutputURL: string + useShortUrl: string + shortUrlServer: string + c1nToken: string + cfWorkerHost: string + yourlsDomain: string + yourlsSignature: string + isSilentNotice: string + proxy: string + registry: string + autoCopy: string + enableWebServer: string + webServerHost: string + webServerPort: string + webServerPath: string + deleteLocalFile: string + uploadResultNotification: string + uploadNotification: string + useBuiltinClipboard: string + autoStart: string + autoImport: string + autoImportPicBed: string + } + needReload: string + picgoPlugins: string + uploader: string + buildIn: { + compress: string + watermark: string + rename: string + } + debug: string + PICGO_ENV: string +} + +export const configPaths: IConfigPaths = { + picBed: { + current: 'picBed.current', + uploader: 'picBed.uploader', + proxy: 'picBed.proxy', + transformer: 'picBed.transformer', + list: 'picBed.list' + }, + settings: { + shortKey: { + _path: 'settings.shortKey', + 'picgo:upload': 'settings.shortKey[picgo:upload]' + }, + logLevel: 'settings.logLevel', + logPath: 'settings.logPath', + logFileSizeLimit: 'settings.logFileSizeLimit', + isAutoListenClipboard: 'settings.isAutoListenClipboard', + isListeningClipboard: 'settings.isListeningClipboard', + showUpdateTip: 'settings.showUpdateTip', + miniWindowPosition: 'settings.miniWindowPosition', + miniWindowOntop: 'settings.miniWindowOntop', + isHideDock: 'settings.isHideDock', + mainWindowWidth: 'settings.mainWindowWidth', + mainWindowHeight: 'settings.mainWindowHeight', + autoCloseMiniWindow: 'settings.autoCloseMiniWindow', + autoCloseMainWindow: 'settings.autoCloseMainWindow', + isCustomMiniIcon: 'settings.isCustomMiniIcon', + customMiniIcon: 'settings.customMiniIcon', + startMode: 'settings.startMode', + autoRename: 'settings.autoRename', + deleteCloudFile: 'settings.deleteCloudFile', + server: 'settings.server', + serverKey: 'settings.serverKey', + pasteStyle: 'settings.pasteStyle', + aesPassword: 'settings.aesPassword', + rename: 'settings.rename', + sync: 'settings.sync', + tempDirPath: 'settings.tempDirPath', + language: 'settings.language', + customLink: 'settings.customLink', + manualPageOpen: 'settings.manualPageOpen', + encodeOutputURL: 'settings.encodeOutputURL', + useShortUrl: 'settings.useShortUrl', + shortUrlServer: 'settings.shortUrlServer', + c1nToken: 'settings.c1nToken', + cfWorkerHost: 'settings.cfWorkerHost', + yourlsDomain: 'settings.yourlsDomain', + yourlsSignature: 'settings.yourlsSignature', + isSilentNotice: 'settings.isSilentNotice', + proxy: 'settings.proxy', + registry: 'settings.registry', + autoCopy: 'settings.autoCopy', + enableWebServer: 'settings.enableWebServer', + webServerHost: 'settings.webServerHost', + webServerPort: 'settings.webServerPort', + webServerPath: 'settings.webServerPath', + deleteLocalFile: 'settings.deleteLocalFile', + uploadResultNotification: 'settings.uploadResultNotification', + uploadNotification: 'settings.uploadNotification', + useBuiltinClipboard: 'settings.useBuiltinClipboard', + autoStart: 'settings.autoStart', + autoImport: 'settings.autoImport', + autoImportPicBed: 'settings.autoImportPicBed' + }, + needReload: 'needReload', + picgoPlugins: 'picgoPlugins', + uploader: 'uploader', + buildIn: { + compress: 'buildIn.compress', + watermark: 'buildIn.waterMark', + rename: 'buildIn.rename' + }, + debug: 'debug', + PICGO_ENV: 'PICGO_ENV' +} diff --git a/src/universal/utils/static.ts b/src/universal/utils/static.ts index 424655b..c888c97 100644 --- a/src/universal/utils/static.ts +++ b/src/universal/utils/static.ts @@ -22,3 +22,5 @@ export const picBedsCanbeDeleted = [ 'upyun', 'webdavplist' ] + +export const DEFAULT_AES_PASSWORD = 'aesPassword' From d66f4809f1670a9cdd2ed03605da41eba7d44c85 Mon Sep 17 00:00:00 2001 From: Kuingsmile Date: Tue, 9 Apr 2024 16:17:52 +0800 Subject: [PATCH 23/23] :tada: Release: v2.8.1 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc6a671..dd18294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## :tada: 2.8.1 (2024-04-09) + + +### :sparkles: Features + +* **custom:** add api doc for 36677 port ([872a1a7](https://github.com/Kuingsmile/piclist/commit/872a1a7)) +* **custom:** add buildin web server ([ab8b62e](https://github.com/Kuingsmile/piclist/commit/ab8b62e)), closes [#180](https://github.com/Kuingsmile/piclist/issues/180) +* **custom:** add log for image processing ([e4c1c7f](https://github.com/Kuingsmile/piclist/commit/e4c1c7f)) +* **custom:** add u flag for gallery regexp match ([aa9e535](https://github.com/Kuingsmile/piclist/commit/aa9e535)) +* **custom:** change buildin rename md5 calculate ([bf8d7b4](https://github.com/Kuingsmile/piclist/commit/bf8d7b4)), closes [#178](https://github.com/Kuingsmile/piclist/issues/178) +* **custom:** exclude svg from watermarker ([75b0c7f](https://github.com/Kuingsmile/piclist/commit/75b0c7f)), closes [#182](https://github.com/Kuingsmile/piclist/issues/182) +* **custom:** optimize the processing of upload path and custom url ([ae40a16](https://github.com/Kuingsmile/piclist/commit/ae40a16)) +* **custom:** optimize webServer ([e6e9472](https://github.com/Kuingsmile/piclist/commit/e6e9472)) +* **custom:** refactor upload and delete api ([475b85e](https://github.com/Kuingsmile/piclist/commit/475b85e)) +* **custom:** update api doc ([922f04f](https://github.com/Kuingsmile/piclist/commit/922f04f)) + + +### :bug: Bug Fixes + +* **custom:** fix compatiblity with typora ([762913c](https://github.com/Kuingsmile/piclist/commit/762913c)) +* **custom:** fix error handling in server startup ([cf0450f](https://github.com/Kuingsmile/piclist/commit/cf0450f)) +* **custom:** fix image watermark bug ([8e5e6d4](https://github.com/Kuingsmile/piclist/commit/8e5e6d4)) + + +### :pencil: Documentation + +* **custom:** add tg group qr code ([5cd34a6](https://github.com/Kuingsmile/piclist/commit/5cd34a6)) +* **custom:** prepare for 2.8.1 ([f73badd](https://github.com/Kuingsmile/piclist/commit/f73badd)) +* **custom:** prepare for 2.81 ([0476346](https://github.com/Kuingsmile/piclist/commit/0476346)) +* **custom:** update tg group link ([20f6acc](https://github.com/Kuingsmile/piclist/commit/20f6acc)) + + + # :tada: 2.8.0 (2024-03-14) diff --git a/package.json b/package.json index 6fe7734..5d128d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "piclist", - "version": "2.8.0", + "version": "2.8.1", "author": { "name": "Kuingsmile", "email": "pkukuing@gmail.com"