diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b58478b..592b3f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## :tada: 2.6.3 (2023-10-10) + + +### :sparkles: Features + +* add authoration key for built-in server ([06c518d](https://github.com/Kuingsmile/piclist/commit/06c518d)) +* build-in server support formdata now ([615cbc5](https://github.com/Kuingsmile/piclist/commit/615cbc5)), closes [#103](https://github.com/Kuingsmile/piclist/issues/103) +* built-in server will listen to 0.0.0.0 other than 127.0.0.1 ([54d289c](https://github.com/Kuingsmile/piclist/commit/54d289c)) + + +### :bug: Bug Fixes + +* fix an issue of watermark ([64952e1](https://github.com/Kuingsmile/piclist/commit/64952e1)), closes [#102](https://github.com/Kuingsmile/piclist/issues/102) +* fix local path picbed upload error ([392e178](https://github.com/Kuingsmile/piclist/commit/392e178)) +* fix upload api copy error after changing to 0.0.0.0 ([7e40808](https://github.com/Kuingsmile/piclist/commit/7e40808)) + + +### :pencil: Documentation + +* update docs ([573265c](https://github.com/Kuingsmile/piclist/commit/573265c)) + + + ## :tada: 2.6.2 (2023-09-30) diff --git a/README.md b/README.md index 4fab5a52..51460e83 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ PicList `V1.5.0` and above versions provide the `one-click migration` function. ## PicList-Core -The core of PicList is based on the original PicGo-Core [PicList-core](https://github.com/Kuingsmile/PicList-Core), which is adapted for cloud deletion and other functions, and adds watermark addition and image compression / scaling / rotation / format conversion functions, which can be called through CLI command line, as well as some other function changes. +The core of PicList is based on the original PicGo-Core [PicList-core](https://github.com/Kuingsmile/PicList-Core), which is adapted for cloud deletion and other functions, and adds watermark addition and image compression / scaling / rotation / format conversion functions, which can be called through CLI command line, support starting the built-in upload server through `picgo-server`,as well as some other function changes. if you want to use PicList-core, please go to [https://github.com/Kuingsmile/PicList-Core](https://github.com/Kuingsmile/PicList-Core), or go to [npm official address](https://www.npmjs.com/package/piclist) to view the installation instructions. @@ -38,6 +38,7 @@ if you want to use PicList-core, please go to [https://github.com/Kuingsmile/Pic - Within the album, you can synchronize the deletion of cloud images. This is supported across all built-in image hosts and multiple plugins. - The album now offers advanced search and sorting features, as well as batch URL modification. - Built-in tools for adding watermarks, compressing images, scaling images, rotating images, and converting image formats are now available. Advanced renaming is also supported. +- Upload interface supports form upload files, can be shared by multiple computers - Configuration can be synchronized to Github or Gitee repositories. - Manages over ten types of image hosting platforms, allowing online viewing of cloud directories, file searching, batch uploading, batch downloading, file deletion, and more. - Support previewing multiple formats of files, including pictures, videos, plain text files and markdown files, etc. For the specific formats supported, please refer to [Supported file format list](https://github.com/Kuingsmile/PicList/blob/dev/supported_format.md) @@ -79,7 +80,7 @@ The verification of the upload option may have problems, you can ignore it, and ### How to use in Obsidian -In the community plugins, search for and install the Image auto upload Plugin. Next, go to the plugin settings page and change the default uploader to PicGo(app). Set the PicGo server to http://127.0.0.1:36677/upload as shown in the image below. Additionally, this plugin also supports cloud-based deletion through PicList. To use this feature, enter http://127.0.0.1:36677/delete in the deletion interface. +In the community plugins, search for and install the Image auto upload Plugin. Next, go to the plugin settings page and change the default uploader to PicGo(app). Set the PicGo server to `http://127.0.0.1:36677/upload` as shown in the image below. Additionally, this plugin also supports cloud-based deletion through PicList. To use this feature, enter `http://127.0.0.1:36677/delete` in the deletion interface. ![image](https://user-images.githubusercontent.com/96409857/226522718-8378c480-9fb4-4785-87e1-d59808862016.png) diff --git a/README_cn.md b/README_cn.md index e3dadc14..3f65d774 100644 --- a/README_cn.md +++ b/README_cn.md @@ -27,7 +27,7 @@ PicList `V1.5.0`以上版本提供 `一键迁移`功能,进入 `设置`页面 ## PicList-Core -PicList的内核使用的是原版PicGo-Core基础上修改的[PicList-core](https://github.com/Kuingsmile/PicList-Core),为云端删除等功能做了适配,同时,新增了水印添加和图片压缩/缩放/旋转/格式转换等功能,可以通过CLI命令行调用,还有一些其他的功能改动。 +PicList的内核使用的是原版PicGo-Core基础上修改的[PicList-core](https://github.com/Kuingsmile/PicList-Core),为云端删除等功能做了适配,同时,新增了水印添加和图片压缩/缩放/旋转/格式转换等功能,可以通过CLI命令行调用,支持通过`picgo-server`命令开启上传服务器,还有一些其他的功能改动。 如果您希望使用PicList-core,请前往[https://github.com/Kuingsmile/PicList-Core](https://github.com/Kuingsmile/PicList-Core),或者前往[npm官方地址](https://www.npmjs.com/package/piclist)查看安装说明。 @@ -38,6 +38,7 @@ PicList的内核使用的是原版PicGo-Core基础上修改的[PicList-core](htt - 相册中可同步删除云端图片,支持所有内置图床和多个插件 - 相册新增了高级搜索和排序,批量修改URL等功能 - 内置水印添加、图片压缩、图片缩放、图片旋转和图片格式转换等功能,同时支持高级重命名 +- 上传接口支持表单上传文件,可多电脑共用 - 支持配置同步至Github或Gitee仓库 - 支持管理十余种图床,可以在线进行云端目录查看、文件搜索、批量上传、批量下载、删除文件等 - 支持预览多种格式的文件,包括图片、视频、纯文本文件和markdown文件等,具体支持的格式请参考[支持的文件格式列表](https://github.com/Kuingsmile/PicList/blob/dev/supported_format.md) diff --git a/currentVersion.md b/currentVersion.md index 60bda470..62f78c1b 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,11 +1,11 @@ ✨ Features -- 相册页面只显示最后一级文件名,不包含额外的路径 -- 现在更新提示窗口会显示更新日志,方便用户查看 +- 现在当已经设置了水印字体文件路径或使用图片水印时,不再检查内置字体文件是否存在 +- PicList和PicList-Core的内置服务器现在均支持通过`formData`的方式上传图片文件 +- 内置服务器现在默认监听`0.0.0.0`,而不是`127.0.0.1`,以配合多电脑间使用 +- 内置服务器现在支持设置鉴权密钥`key`参数,通过url参数形式传递`?key=xxx`,避免被恶意利用 +- PicList-Core现在支持通过`picgo-server`启动内置上传服务器,使用方式与PicList的内置服务器相同 🐛 Bug Fixes -- 修复了从Obsidian插件删除图片时,webdav图床和SFTP图床无法删除的问题 -- 修复了用户无ssh权限时,sftp图床无法进行云端删除的问题 -- 修复了sftp图床上传时,即使出错仍然会判断为上传成功的问题 -- 修复了sftp图床上传时,会额外创建空文件夹的问题 \ No newline at end of file +- 修复了没有新建临时文件夹导致本地图床上传失败的问题 diff --git a/currentVersion_en.md b/currentVersion_en.md index dac0d316..44522917 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,11 +1,11 @@ ✨ Features -- Now the album page only displays the last level file name, without additional path -- Now the update prompt window will display the update log, which is convenient for users to view +- Now that the watermark font file path has been set or the image watermark is used, the built-in font file is no longer checked +- The built-in server of PicList and PicList-Core now support uploading image files through `formData` +- PicList-Core now supports starting the built-in upload server through `picgo-server`, and the usage is the same as the built-in server of PicList +- The built-in server now supports setting the authentication key `key` parameter, which is passed in the form of url parameters `?key=xxx` to avoid being maliciously used +- PicList-Core now supports starting the built-in upload server through `picgo-server`, and the usage is the same as the built-in server of PicList 🐛 Bug Fixes -- Fixed the problem that webdav and SFTP cannot be deleted when deleting pictures from Obsidian plugin -- Fixed the problem that sftp image bed cannot be deleted in the cloud when the user has no ssh permission -- Fixed the problem that when uploading to sftp image bed, it will still be judged as upload success even if there is an error -- Fixed the problem that when uploading to sftp image bed, an empty folder will be created additionally +- Fix the problem that the local image bed upload fails due to not creating a new temporary folder diff --git a/package.json b/package.json index 56486915..5a65237d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "piclist", - "version": "2.6.2", + "version": "2.6.3", "author": { "name": "Kuingsmile", "email": "pkukuing@gmail.com" @@ -54,7 +54,7 @@ "electron-updater": "^6.1.4", "element-plus": "2.3.14", "epipebomb": "^1.0.0", - "fast-xml-parser": "^4.3.1", + "fast-xml-parser": "^4.3.2", "form-data": "^4.0.0", "fs-extra": "^11.1.1", "got": "^12.6.0", @@ -65,9 +65,10 @@ "marked": "^8.0.0", "mime-types": "^2.1.35", "mitt": "^3.0.1", + "multer": "^1.4.5-lts.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.1.4", + "piclist": "^1.3.4", "pinia": "^2.1.6", "pinia-plugin-persistedstate": "^3.2.0", "qiniu": "^7.9.0", @@ -96,15 +97,16 @@ "@types/js-yaml": "^4.0.6", "@types/lowdb": "^1.0.12", "@types/mime-types": "^2.1.2", + "@types/multer": "^1.4.8", "@types/node": "^16.10.2", - "@types/request-promise-native": "^1.0.18", - "@types/semver": "^7.5.2", - "@types/tunnel": "^0.0.3", + "@types/request-promise-native": "^1.0.19", + "@types/semver": "^7.5.3", + "@types/tunnel": "^0.0.4", "@types/upyun": "^3.4.1", "@types/uuid": "^9.0.3", - "@types/write-file-atomic": "^4.0.0", - "@typescript-eslint/eslint-plugin": "^6.7.3", - "@typescript-eslint/parser": "^6.7.3", + "@types/write-file-atomic": "^4.0.1", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", "@vue/cli-plugin-babel": "^5.0.8", "@vue/cli-plugin-eslint": "^5.0.8", "@vue/cli-plugin-router": "^5.0.8", @@ -119,7 +121,7 @@ "dpdm": "^3.14.0", "electron": "^22.0.2", "electron-devtools-installer": "^3.2.0", - "eslint": "^8.50.0", + "eslint": "^8.51.0", "eslint-config-standard": ">=16.0.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-node": "^11.1.0", @@ -127,7 +129,7 @@ "eslint-plugin-vue": "^9.17.0", "husky": "^3.1.0", "node-loader": "^2.0.0", - "npm-check-updates": "^16.14.4", + "npm-check-updates": "^16.14.5", "stylus": "^0.59.0", "stylus-loader": "^7.1.3", "typescript": "^4.9.5", diff --git a/public/i18n/en.yml b/public/i18n/en.yml index 720cb2bf..de5e1352 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -200,8 +200,10 @@ SETTINGS_TIPS_SERVER_NOTICE: If you don't know what is the server's function, pl 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_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 SETTINGS_LOG_LEVEL_ALL: All SETTINGS_LOG_LEVEL_SUCCESS: Success SETTINGS_LOG_LEVEL_ERROR: Error diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 73e0e708..c6d459a0 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -202,8 +202,10 @@ SETTINGS_TIPS_SERVER_NOTICE: 如果你不知道Server的作用,请阅读文档 SETTINGS_ENABLE_SERVER: 是否开启Server SETTINGS_SET_SERVER_HOST: 设置监听地址 SETTINGS_SET_SERVER_PORT: 设置监听端口 +SETTINGS_SET_SERVER_KEY: 设置鉴权密钥 SETTINGS_TIP_PLACEHOLDER_HOST: 推荐默认地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推荐默认端口:36677 +SETTINGS_TIP_PLACEHOLDER_KEY: 用于接口鉴权, 通过url参数添加'?key=xxx' SETTINGS_LOG_LEVEL_ALL: 全部-All SETTINGS_LOG_LEVEL_SUCCESS: 成功-Success SETTINGS_LOG_LEVEL_ERROR: 错误-Error diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index 848410df..e122d883 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -200,8 +200,10 @@ SETTINGS_TIPS_SERVER_NOTICE: 如果你不知道Server的作用,請閱讀文檔 SETTINGS_ENABLE_SERVER: 是否開啟Server SETTINGS_SET_SERVER_HOST: 設定監聽地址 SETTINGS_SET_SERVER_PORT: 設定監聽端口 +SETTINGS_SET_SERVER_KEY: 設定鑒權密鑰 SETTINGS_TIP_PLACEHOLDER_HOST: 推薦預設地址:127.0.0.1 SETTINGS_TIP_PLACEHOLDER_PORT: 推薦預設端口:36677 +SETTINGS_TIP_PLACEHOLDER_KEY: 用於接口鑒權, 通過url參數添加'?key=xxx' SETTINGS_LOG_LEVEL_ALL: 全部-All SETTINGS_LOG_LEVEL_SUCCESS: 成功-Success SETTINGS_LOG_LEVEL_ERROR: 錯誤-Error diff --git a/src/main/server/index.ts b/src/main/server/index.ts index a59b2a43..1e14681f 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -7,6 +7,29 @@ import { import picgo from '@core/picgo' import logger from '@core/picgo/logger' import axios from 'axios' +import multer from 'multer' +import { app } from 'electron' +import path from 'path' +import fs from 'fs-extra' + +const appPath = app.getPath('userData') +const serverTempDir = path.join(appPath, 'serverTemp') + +fs.ensureDirSync(serverTempDir) + +const multerStorage = multer.diskStorage({ + destination: function (_req: any, _file: any, cb: (arg0: null, arg1: any) => void) { + fs.ensureDirSync(serverTempDir) + cb(null, serverTempDir) + }, + filename: function (_req: any, file: { originalname: any }, cb: (arg0: null, arg1: any) => void) { + cb(null, file.originalname) + } +}) + +const uploadMulter = multer({ + storage: multerStorage +}) class Server { private httpServer: http.Server @@ -17,10 +40,13 @@ class 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: '127.0.0.1', + host: '0.0.0.0', enable: true } this.config = config @@ -55,32 +81,59 @@ class Server { } }) } 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 + 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) + + 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 { logger.warn(`[PicList Server] don't support [${request.method}] method`) @@ -91,7 +144,7 @@ class Server { // port as string is a bug private listen = (port: number | string) => { - logger.info(`[PicList Server] is listening at ${port}`) + logger.info(`[PicList Server] is listening at ${port} of ${this.config.host}`) if (typeof port === 'string') { port = parseInt(port, 10) } @@ -127,6 +180,9 @@ 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.startup() } diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index f391dd72..e714dfd3 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -9,6 +9,11 @@ import path from 'path' import { dbPathDir } from 'apis/core/datastore/dbChecker' import picgo from '@core/picgo' import { changeCurrentUploader } from '../utils/handleUploaderConfig' +import { app } from 'electron' +import fs from 'fs-extra' + +const appPath = app.getPath('userData') +const serverTempDir = path.join(appPath, 'serverTemp') const STORE_PATH = dbPathDir() const LOG_PATH = path.join(STORE_PATH, 'piclist.log') @@ -27,6 +32,18 @@ router.post('/upload', async ({ }): Promise => { try { const picbed = urlparams?.get('picbed') + const passedKey = urlparams?.get('key') + const serverKey = picgo.getConfig('settings.serverKey') || '' + if (serverKey && passedKey !== serverKey) { + handleResponse({ + response, + body: { + success: false, + message: 'server key is not correct' + } + }) + return + } let currentPicBedType = '' let currentPicBedConfig = {} as IStringKeyMap let currentPicBedConfigId = '' @@ -112,6 +129,7 @@ router.post('/upload', async ({ }) } } + fs.emptyDirSync(serverTempDir) if (needRestore) { changeCurrentUploader(currentPicBedType, currentPicBedConfig, currentPicBedConfigId) } diff --git a/src/renderer/pages/PicGoSetting.vue b/src/renderer/pages/PicGoSetting.vue index 39237bf3..cc6d0c66 100644 --- a/src/renderer/pages/PicGoSetting.vue +++ b/src/renderer/pages/PicGoSetting.vue @@ -1055,6 +1055,16 @@ :placeholder="$T('SETTINGS_TIP_PLACEHOLDER_PORT')" /> + + +