mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-09-09 09:26:58 +08:00
Merge branch 'dev' into release
This commit is contained in:
@@ -3,3 +3,7 @@ test/unit/*.js
|
||||
test/e2e/*.js
|
||||
dist/
|
||||
src/**/*.js
|
||||
node_modules
|
||||
dist
|
||||
vue.config.js
|
||||
babel.config.js
|
||||
|
||||
+20
-3
@@ -10,7 +10,8 @@ module.exports = {
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'@vue/standard',
|
||||
'@vue/typescript'
|
||||
'@vue/typescript/recommended',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
@@ -20,8 +21,24 @@ module.exports = {
|
||||
'no-async-promise-executor': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'@typescript-eslint/indent': ['error', 2],
|
||||
'vue/no-v-html': 'off'
|
||||
'@typescript-eslint/indent': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description'
|
||||
}
|
||||
],
|
||||
'vue/no-v-html': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{},
|
||||
{
|
||||
usePrettierrc: true
|
||||
}
|
||||
]
|
||||
},
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxBracketSameLine": false,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 120,
|
||||
"proseWrap": "always",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false,
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
Vendored
-1
@@ -5,7 +5,6 @@
|
||||
"name": "Electron: Main",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"protocol": "inspector",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
## :tada: 2.9.1 (2024-07-07)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* **custom:** add alist support ([bead8a2](https://github.com/Kuingsmile/piclist/commit/bead8a2))
|
||||
* **custom:** add build in alist support and fix gallery bug ([875d034](https://github.com/Kuingsmile/piclist/commit/875d034)), closes [#218](https://github.com/Kuingsmile/piclist/issues/218)
|
||||
* **custom:** optimize upload progress ([0039399](https://github.com/Kuingsmile/piclist/commit/0039399))
|
||||
* **custom:** upgrade core ([6aa7311](https://github.com/Kuingsmile/piclist/commit/6aa7311))
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* **custom:** fix picbed config page css ([d7e0f56](https://github.com/Kuingsmile/piclist/commit/d7e0f56)), closes [#213](https://github.com/Kuingsmile/piclist/issues/213)
|
||||
* **custom:** fix rename window bug ([0b670a8](https://github.com/Kuingsmile/piclist/commit/0b670a8)), closes [#58](https://github.com/Kuingsmile/piclist/issues/58)
|
||||
* **custom:** fix s3 delete bug ([b30dc4d](https://github.com/Kuingsmile/piclist/commit/b30dc4d)), closes [#212](https://github.com/Kuingsmile/piclist/issues/212)
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* **custom:** add scoop-lemon as install method ([09bf847](https://github.com/Kuingsmile/piclist/commit/09bf847))
|
||||
* **custom:** fix docs typo ([dc70648](https://github.com/Kuingsmile/piclist/commit/dc70648))
|
||||
* **custom:** update 2.9.1 changelog ([2bdb79f](https://github.com/Kuingsmile/piclist/commit/2bdb79f))
|
||||
|
||||
|
||||
|
||||
# :tada: 2.9.0 (2024-06-14)
|
||||
|
||||
|
||||
|
||||
+7
-7
@@ -5,7 +5,7 @@
|
||||
1. 使用 [yarn](https://yarnpkg.com/) 安装依赖
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn
|
||||
```
|
||||
|
||||
然后通过
|
||||
@@ -16,18 +16,18 @@ yarn dev
|
||||
|
||||
启动项目。
|
||||
|
||||
2. 只跟 Electron 主进程相关的代码请在 `src/main` 目录下添加。只跟渲染进程相关的代码请在 `src/renderer` 目录下添加。两个进程都能使用的代码请在 `src/universal` 目录下添加。 **注意**:渲染进程没有 `Node.js` 能力,所有渲染进程需要用到 `Node.js` 模块相关的代码请在 `src/main/events/picgoCoreIPC.ts` 下添加事件进行处理。
|
||||
3. 所有的跨进程事件名请在 `src/universal/events/constants.ts` 里添加。
|
||||
4. 所有的全局类型定义请在 `src/universal/types/` 里添加,如果是 `enum`,请在 `src/universal/types/enum.ts` 里添加。
|
||||
5. 与图床管理功能相关的代码请在 `src/main/manage`和 `src/renderer/manage`目录下添加。
|
||||
1. 只跟 Electron 主进程相关的代码请在 `src/main` 目录下添加。只跟渲染进程相关的代码请在 `src/renderer` 目录下添加。两个进程都能使用的代码请在 `src/universal` 目录下添加。
|
||||
2. 所有的跨进程事件名请在 `src/universal/events/constants.ts` 里添加。
|
||||
3. 所有的全局类型定义请在 `src/universal/types/` 里添加,如果是 `enum`,请在 `src/universal/types/enum.ts` 里添加。
|
||||
4. 与图床管理功能相关的代码请在 `src/main/manage`和 `src/renderer/manage`目录下添加。
|
||||
|
||||
## i18n
|
||||
|
||||
1. 在 `public/i18n/` 下面创建一种语言的 `yml` 文件,例如 `zh-Hans.yml`。然后参考 `zh-CN.yml` 或者 `en.yml` 编写语言文件。并注意,PicList 会通过语言文件中的 `LANG_DISPLAY_LABEL` 向用户展示该语言的名称。
|
||||
2. 在 `src/universal/i18n/index.ts` 里添加一种默认语言。其中 `label` 就是语言文件中 `LANG_DISPLAY_LABEL` 的值,`value` 是语言文件名。
|
||||
3. 如果是对已有语言文件进行更新,请在更新完,务必运行一遍 `yarn gen-i18n`,确保能生成正确的语言定义文件。
|
||||
3. 如果是对已有语言文件进行更新,请在更新完,务必运行一遍 `yarn i18n`,确保能生成正确的语言定义文件。
|
||||
|
||||
## 提交代码
|
||||
|
||||
1. 请检查代码没有多余的注释、`console.log` 等调试代码。
|
||||
2. 提交代码前,请执行命令 `git add . && yarn cz`,唤起 [代码提交规范工具](https://github.com/PicGo/bump-version)。通过该工具提交代码。
|
||||
2. 提交代码前,请执行命令 `git add . && yarn cz`,唤起 [代码提交规范工具](https://github.com/Kuingsmile/node-bump-version)。通过该工具提交代码。
|
||||
|
||||
+7
-7
@@ -5,7 +5,7 @@
|
||||
1. Use [yarn](https://yarnpkg.com/) to install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn
|
||||
```
|
||||
|
||||
then pass
|
||||
@@ -16,18 +16,18 @@ yarn dev
|
||||
|
||||
Startup project.
|
||||
|
||||
2. Please add code only related to the main process of Electron in the `src/main` directory. Code only related to the rendering process should be added in the `src/renderer` directory. Add code that can be used by both processes in the `src/universal` directory. **Note**: The rendering process does not have the `Node.js` capability. All rendering processes need to use `Node.js modules` related code, please add events under `src/main/events/picgoCoreIPC.ts` for processing.
|
||||
3. Please add all cross-process event names in `src/universal/events/constants.ts`.
|
||||
4. Please add all global type definitions in `src/universal/types/`, if it is `enum`, please add it in `src/universal/types/enum.ts`.
|
||||
5. Code related to the management function of the picture bed should be added in the `src/main/manage` and `src/renderer/manage` directory.
|
||||
1. Please add code only related to the main process of Electron in the `src/main` directory. Code only related to the rendering process should be added in the `src/renderer` directory. Add code that can be used by both processes in the `src/universal` directory.
|
||||
2. Please add all cross-process event names in `src/universal/events/constants.ts`.
|
||||
3. Please add all global type definitions in `src/universal/types/`, if it is `enum`, please add it in `src/universal/types/enum.ts`.
|
||||
4. Code related to the management function of the picture bed should be added in the `src/main/manage` and `src/renderer/manage` directory.
|
||||
|
||||
## i18n
|
||||
|
||||
1. Create a language `yml` file under `public/i18n/`, for example `zh-Hans.yml`. Then refer to `zh-CN.yml` or `en.yml` to write language files. Also note that PicList will display the name of the language to the user via `LANG_DISPLAY_LABEL` in the language file.
|
||||
2. Add a default language to `src/universal/i18n/index.ts`. where `label` is the value of `LANG_DISPLAY_LABEL` in the language file, and `value` is the name of the language file.
|
||||
3. If you are updating an existing language file, be sure to run `yarn gen-i18n` after the update to ensure that the correct language definition file can be generated.
|
||||
3. If you are updating an existing language file, be sure to run `yarn i18n` after the update to ensure that the correct language definition file can be generated.
|
||||
|
||||
## Submit code
|
||||
|
||||
1. Please check that the code has no extra comments, `console.log` and other debugging code.
|
||||
2. Before submitting the code, please execute the command `git add . && yarn cz` to invoke [Code Submission Specification Tool](https://github.com/PicGo/bump-version). Submit code through this tool.
|
||||
2. Before submitting the code, please execute the command `git add . && yarn cz` to invoke [Code Submission Specification Tool](https://github.com/Kuingsmile/node-bump-version). Submit code through this tool.
|
||||
|
||||
@@ -75,22 +75,15 @@ To open the main window, right-click or two-finger tap the PicList icon in the t
|
||||
|
||||
Or right-click the PicList icon in the dock and select "Open Main Window."
|
||||
|
||||
## 8. 上传失败,或者是服务器出错
|
||||
## 8. Upload failed, or server error
|
||||
|
||||
1. PicList 自带的图床都经过测试,上传出错一般都不是 PicList 自身的原因。如果你用的是 GitHub 图床请参考上面的第 7 点。
|
||||
2. 检查 PicList 的日志(报错日志可以在 PicList 设置 -> 设置日志文件 -> 点击打开 后找到),看看 `[PicList Error]` 的报错信息里有什么关键信息
|
||||
|
||||
1. 先自行搜索 error 里的报错信息,往往你能百度或者谷歌出问题原因,不必开 issue。
|
||||
2. 如果有带有 `401` 、`403` 等 `40X` 状态码字样的,不用怀疑,就是你配置写错了,仔细检查配置,看看是否多了空格之类的。
|
||||
3. 如果带有 `HttpError`、`RequestError` 、 `socket hang up` 等字样的说明这是网络问题,我无法帮你解决网络问题,请检查你自己的网络,是否有代理,DNS 设置是否正常等。
|
||||
3. 通常网络问题引起的上传失败都是因为代理设置不当导致的。如果开启了系统代理,建议同时也在 PicList 的代理设置中设置对应的HTTP代理。
|
||||
4. PicList's built-in image hosting platforms have been tested, and upload errors are generally not caused by PicList itself. If you are using the GitHub image hosting platform, please refer to the 7th point above.
|
||||
5. Check PicList's log (the error log can be found by clicking "Open" in PicList Settings -> Set Log File ->), and see if there is any key information in the `[PicList Error]` error message
|
||||
1. PicList's built-in image hosting platforms have been tested, and upload errors are generally not caused by PicList itself. If you are using the GitHub image hosting platform, please refer to the 7th point above.
|
||||
2. Check PicList's log (the error log can be found by clicking "Open" in PicList Settings -> Set Log File ->), and see if there is any key information in the `[PicList Error]` error message
|
||||
|
||||
1. First search for the error message in the error message by yourself, and you can often Baidu or Google the cause of the problem, so you don't need to open an issue.
|
||||
2. If there are `401`, `403` and other `40X` status code words, don't doubt it, it means that your configuration is wrong, check the configuration carefully to see if there are extra spaces, etc.
|
||||
3. If there are words such as `HttpError`, `RequestError`, `socket hang up`, etc., it means that this is a network problem, and I cannot help you solve the network problem. Please check your own network, whether there is a proxy, whether the DNS setting is normal, etc.
|
||||
6. Usually, upload failures caused by network problems are caused by improper proxy settings. If the system proxy is turned on, it is recommended to set the corresponding HTTP proxy in the proxy settings of PicList at the same time.
|
||||
3. Usually, upload failures caused by network problems are caused by improper proxy settings. If the system proxy is turned on, it is recommended to set the corresponding HTTP proxy in the proxy settings of PicList at the same time.
|
||||
|
||||
## 10. After installing the macOS version, there is no main interface
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ docker-compose up -d
|
||||
|
||||
| Platform | Album cloud deletion | Cloud storage management |
|
||||
| :------------------------: | :------------------: | :----------------------: |
|
||||
| Built-in AList | ✔️ | ✔️ |
|
||||
| SM.MS | ✔️ | ✔️ |
|
||||
| Github | ✔️ | ✔️ |
|
||||
| Imgur | ✔️ | ✔️ |
|
||||
@@ -162,6 +163,15 @@ docker-compose up -d
|
||||
|
||||
[https://github.com/Kuingsmile/PicList/releases/latest](https://github.com/Kuingsmile/PicList/releases/latest)
|
||||
|
||||
### Scoop
|
||||
|
||||
Thanks to [scoop-lemon](https://github.com/hoilc/scoop-lemon), you can use Scoop to install PicList, just execute the following command:
|
||||
|
||||
```bash
|
||||
scoop bucket add lemon https://github.com/hoilc/scoop-lemon
|
||||
scoop install lemon/piclist
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
MacOS users can now use Homebrew to install PicList, just execute the following command:
|
||||
@@ -182,16 +192,6 @@ If the macOS system installs PicList and displays "file is damaged" or installs
|
||||
|
||||
After V1.4.1, all mac installation packages have been signed by my developer certificate, will not be recognized by the macOS system as "malicious software", will not appear "file is damaged" prompt.
|
||||
|
||||
### Mac App Store
|
||||
|
||||
Due to the sandbox mechanism of Mac App Store, many functions cannot be used normally, so the installation method of Mac App Store is no longer supported.
|
||||
|
||||
If you have purchased PicList through Mac App Store, please add my WeChat `pku_sq_ma`, I will refund you.
|
||||
|
||||
Thank you again for your support for PicList.
|
||||
|
||||
**Of course, if you don't want to pay, you can also download the free installation package through Github to install.**
|
||||
|
||||
## Application screenshot
|
||||
|
||||

|
||||
|
||||
@@ -135,6 +135,7 @@ docker-compose up -d
|
||||
|
||||
| 平台 | 相册云删除 | 云存储管理 |
|
||||
| :------------: | :--------: | :--------: |
|
||||
| 内置AList | ✔️ | ✔️ |
|
||||
| SM.MS | ✔️ | ✔️ |
|
||||
| Github | ✔️ | ✔️ |
|
||||
| Imgur | ✔️ | ✔️ |
|
||||
@@ -162,6 +163,15 @@ docker-compose up -d
|
||||
|
||||
[https://github.com/Kuingsmile/PicList/releases/latest](https://github.com/Kuingsmile/PicList/releases/latest)
|
||||
|
||||
### Scoop
|
||||
|
||||
感谢[scoop-lemon](https://github.com/hoilc/scoop-lemon),你可以使用Scoop来安装PicList,只需要执行以下命令即可:
|
||||
|
||||
```bash
|
||||
scoop bucket add lemon https://github.com/hoilc/scoop-lemon
|
||||
scoop install lemon/piclist
|
||||
```
|
||||
|
||||
### Homebrew
|
||||
|
||||
MacOS用户现在可以使用Homebrew来安装PicList了,只需要执行以下命令即可:
|
||||
@@ -176,6 +186,12 @@ brew install piclist --cask
|
||||
brew uninstall piclist
|
||||
```
|
||||
|
||||
### Mac特殊说明
|
||||
|
||||
如果macOS系统安装PicList后显示“文件已损坏”或者安装后打开无响应,请升级到PicList V1.4.1或以上版本。
|
||||
|
||||
V1.4.1之后,所有mac安装包都已经使用我的开发者证书签名,不会被macOS系统识别为“恶意软件”,不会出现“文件已损坏”提示。
|
||||
|
||||
## 应用截图
|
||||
|
||||

|
||||
|
||||
+3
-5
@@ -1,6 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
plugins: ['@babel/plugin-proposal-optional-chaining']
|
||||
}
|
||||
presets: ["@vue/cli-plugin-babel/preset"],
|
||||
plugins: ["@babel/plugin-proposal-optional-chaining"],
|
||||
};
|
||||
|
||||
+9
-27
@@ -1,32 +1,14 @@
|
||||
### ✨ Features
|
||||
|
||||
- 图床
|
||||
- 现在`sm.ms`图床上传重复图片时,后上传的图片也支持云端删除了
|
||||
- 管理
|
||||
- 现在s3图床支持新建存储通
|
||||
- 调整了管理页面的部分布局
|
||||
- 代码高亮样式调整为stackoverflow-light
|
||||
- 界面
|
||||
- 现在托盘菜单会根据当前状态显示开启/关闭剪贴板监听,而不是始终显示全部菜单
|
||||
- 现在托盘菜单会根据当前状态显示开启/关闭mini窗口
|
||||
- 现在鼠标悬停于托盘图标时,会显示当前图床和配置名
|
||||
- 移除了设置页面部分不必要的全局通知
|
||||
- 优化了英文下的设置页面布局
|
||||
- 性能
|
||||
- 现在启动软件时会自动清空剪贴板图片缓存文件夹
|
||||
- 优化了读取配置的性能表现
|
||||
- 优化了多个页面的加载速度
|
||||
- 升级vue等依赖至最新版本
|
||||
- 新增内置AList上传支持
|
||||
- 优化了设置了图片处理操作时的上传进度通知
|
||||
- 优化了部分图床的i18n设置
|
||||
- 修改内置兰空图床显示名,由`lsky`修改为`兰空图床`
|
||||
- core版本现在使用piclist桌面版配置文件时支持设置`configName`参数
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- 修复了从obsidan发送删除请求时,会导致软件闪退的问题
|
||||
- 修复了使用`webp`插件时,上传剪贴板图片会导致软件闪退的问题
|
||||
- 修复了设置了图片处理操作后,上传URL会报错的问题
|
||||
- 修复了设置原始PicGo窗口大小后,重新打开设置界面时窗口大小数值没有更新的问题
|
||||
- 修复了主界面菜单加载和点击时反复触发获取图床配置导致的卡顿问题
|
||||
- 修复了切换语言时部分下拉框没有更新的问题
|
||||
- 修复了部分翻译错误
|
||||
- 管理页面
|
||||
- 修复了部分图床的文件夹复制链接错误的问题
|
||||
- 修复了文件浏览页面的css错误
|
||||
- 修复了s3图床文件名含有特殊字符时无法删除的问题
|
||||
- 修复了特定情况下开启手动重命名后会导致软件闪退的问题
|
||||
- 修复了图床设置页面按钮的样式问题
|
||||
- 优化了多个图床存在的重复记录错误日志的问题
|
||||
+9
-27
@@ -1,32 +1,14 @@
|
||||
### ✨ Features
|
||||
|
||||
- PicBed
|
||||
- Now when uploading duplicate images to `sm.ms` image bed, the later uploaded image also supports cloud deletion
|
||||
- Management
|
||||
- Now s3 image bed supports creating new storage channels
|
||||
- Adjusted some layouts of the management page
|
||||
- The code highlighting style is adjusted to stackoverflow-light
|
||||
- Interface
|
||||
- Now the tray menu will display the open/close clipboard monitoring according to the current status, instead of always displaying all menus
|
||||
- Now the tray menu will display the open/close mini window according to the current status
|
||||
- Now when the mouse hovers over the tray icon, the current image bed and configuration name will be displayed
|
||||
- Removed some unnecessary global notifications on the settings page
|
||||
- Optimized the layout of the settings page in English
|
||||
- Performance
|
||||
- Now the software will automatically clear the clipboard image cache folder when starting
|
||||
- Optimized the performance of reading configuration
|
||||
- Optimized the loading speed of multiple pages
|
||||
- Upgrade dependencies such as vue to the latest version
|
||||
- Add built-in AList upload support
|
||||
- Optimize the upload progress notification when image processing operations are set
|
||||
- Optimize the i18n settings of some image beds
|
||||
- Modify the display name of the built-in Lan Kong image bed from `lsky` to `Lan Kong image bed`
|
||||
- The core version now supports setting the `configName` parameter when using the piclist desktop version configuration file
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Fix the problem that the software crashes when sending a delete request from obsidan
|
||||
- Fix the problem that the software crashes when uploading clipboard images using the `webp` plugin
|
||||
- Fix the problem that an error occurs when uploading a URL after setting image processing operations
|
||||
- Fix the problem that the window size value is not updated when the original PicGo window size is set and the setting interface is reopened
|
||||
- Fix the problem that the main interface menu repeatedly triggers the lag caused by repeatedly triggering the acquisition of the image bed configuration when loading and clicking
|
||||
- Fix the problem that some drop-down boxes are not updated when switching languages
|
||||
- Fix some translation errors
|
||||
- Management page
|
||||
- Fix the problem that the folder copy link of some image beds is incorrect
|
||||
- Fix the css error of the file browsing page
|
||||
- Fix the problem that the s3 image bed cannot be deleted when the file name contains special characters
|
||||
- Fix the problem that the software crashes under specific conditions after enabling manual renaming
|
||||
- Fix the style problem of the button on the image bed setting page
|
||||
- Optimize the problem of repeated recording error logs for multiple image beds
|
||||
|
||||
+16
-20
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "piclist",
|
||||
"version": "2.9.0",
|
||||
"version": "2.9.1",
|
||||
"author": {
|
||||
"name": "Kuingsmile",
|
||||
"email": "pkukuing@gmail.com"
|
||||
@@ -15,23 +15,23 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vue-cli-service electron:build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"bump": "bump-version",
|
||||
"cz": "git-cz",
|
||||
"dev": "vue-cli-service electron:serve",
|
||||
"electron:build": "vue-cli-service electron:build",
|
||||
"electron:serve": "vue-cli-service electron:serve",
|
||||
"i18n": "node ./scripts/gen-i18n-types.js",
|
||||
"link": "node ./scripts/link.js",
|
||||
"lint": "vue-cli-service lint",
|
||||
"lint:dpdm": "dpdm -T --tsconfig ./tsconfig.json --no-tree --no-warning --exit-code circular:1 src/background.ts",
|
||||
"lint:fix": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue src/",
|
||||
"ncu": "node ./scripts/check-dep.js",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"postuninstall": "electron-builder install-app-deps",
|
||||
"release": "vue-cli-service electron:build --publish always",
|
||||
"upload-dist": "node ./scripts/upload-dist-to-r2.js",
|
||||
"upload-beta": "node ./scripts/upload-beta.js",
|
||||
"link": "node ./scripts/link.js",
|
||||
"sha256": "node ./scripts/gen-sha256.js",
|
||||
"ncu": "node ./scripts/check-dep.js",
|
||||
"lint:dpdm": "dpdm -T --tsconfig ./tsconfig.json --no-tree --no-warning --exit-code circular:1 src/background.ts"
|
||||
"upload-beta": "node ./scripts/upload-beta.js",
|
||||
"upload-dist": "node ./scripts/upload-dist-to-r2.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.421.0",
|
||||
@@ -52,7 +52,7 @@
|
||||
"cos-nodejs-sdk-v5": "^2.12.5",
|
||||
"dexie": "^3.2.4",
|
||||
"electron-updater": "^6.1.4",
|
||||
"element-plus": "2.7.4",
|
||||
"element-plus": "2.7.6",
|
||||
"epipebomb": "^1.0.0",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -67,7 +67,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-ssh-no-cpu-features": "^2.0.0",
|
||||
"nodejs-file-downloader": "^4.12.1",
|
||||
"piclist": "^1.8.10",
|
||||
"piclist": "^1.9.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"proxy-agent": "^5.0.0",
|
||||
@@ -79,15 +79,14 @@
|
||||
"upyun": "^3.4.6",
|
||||
"uuid": "^9.0.1",
|
||||
"video.js": "^8.6.1",
|
||||
"vue": "^3.4.27",
|
||||
"vue-router": "^4.3.2",
|
||||
"vue": "^3.4.30",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue3-lazyload": "^0.3.8",
|
||||
"vue3-photo-preview": "^0.3.0",
|
||||
"webdav": "^5.3.1",
|
||||
"write-file-atomic": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/video.js": "^7.3.58",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@electron/notarize": "^2.1.0",
|
||||
"@types/ali-oss": "^6.16.11",
|
||||
@@ -103,32 +102,29 @@
|
||||
"@types/tunnel": "^0.0.7",
|
||||
"@types/upyun": "^3.4.3",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/video.js": "^7.3.58",
|
||||
"@types/write-file-atomic": "^4.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||
"@typescript-eslint/parser": "^6.12.0",
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||
"@vue/cli-plugin-router": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"@vue/eslint-config-standard": "^8.0.1",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/runtime-dom": "^3.4.27",
|
||||
"@vue/runtime-dom": "^3.4.30",
|
||||
"conventional-changelog": "^5.1.0",
|
||||
"cz-customizable": "^7.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"dpdm": "^3.14.0",
|
||||
"electron": "^22.3.27",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-promise": "^6.2.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"husky": "^3.1.0",
|
||||
"node-bump-version": "^1.0.2",
|
||||
"node-loader": "^2.0.0",
|
||||
"npm-check-updates": "^16.14.20",
|
||||
"prettier": "^3.3.2",
|
||||
"stylus": "^0.59.0",
|
||||
"stylus-loader": "^7.1.3",
|
||||
"typescript": "^4.9.5",
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ SETTINGS_PLUGIN_INSTALL_MIRROR: Mirror for Plugin Install
|
||||
SETTINGS_CURRENT_VERSION: Current Version
|
||||
SETTINGS_NEWEST_VERSION: Newest Version
|
||||
SETTINGS_GETING: Getting...
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList has a new version, please click confirm to open download page
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList has a new version, please click confirm to restart
|
||||
SETTINGS_LOG_FILE_PATH: Log File Path
|
||||
SETTINGS_CONFIG_FILE_PATH: Config File Path
|
||||
SETTINGS_LOG_FILE: Log File
|
||||
|
||||
@@ -199,7 +199,7 @@ SETTINGS_PLUGIN_INSTALL_MIRROR: 插件安装镜像
|
||||
SETTINGS_CURRENT_VERSION: 当前版本
|
||||
SETTINGS_NEWEST_VERSION: 最新版本
|
||||
SETTINGS_GETING: 正在获取中
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList更新啦,请点击确定打开下载页面
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList更新啦,请点击确定重启触发更新
|
||||
SETTINGS_LOG_FILE_PATH: 日志文件路径
|
||||
SETTINGS_CONFIG_FILE_PATH: 配置文件路径
|
||||
SETTINGS_LOG_FILE: 常规日志文件
|
||||
|
||||
@@ -197,7 +197,7 @@ SETTINGS_PLUGIN_INSTALL_MIRROR: 插件安裝鏡像
|
||||
SETTINGS_CURRENT_VERSION: 當前版本
|
||||
SETTINGS_NEWEST_VERSION: 最新版本
|
||||
SETTINGS_GETING: 正在取得中
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList更新啦,請點擊確定開啟下載頁面
|
||||
SETTINGS_TIPS_HAS_NEW_VERSION: PicList更新啦,請點擊確定重启應用
|
||||
SETTINGS_LOG_FILE_PATH: 記錄檔案路徑
|
||||
SETTINGS_CONFIG_FILE_PATH: 設定檔案路徑
|
||||
SETTINGS_LOG_FILE: 記錄檔案
|
||||
|
||||
@@ -51,4 +51,5 @@ app.use(pinia)
|
||||
app.use(hljsVuePlugin)
|
||||
app.use(VueVideoPlayer)
|
||||
app.mount('#app')
|
||||
|
||||
initTalkingData()
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import axios from 'axios'
|
||||
import {
|
||||
app,
|
||||
clipboard,
|
||||
dialog,
|
||||
shell
|
||||
} from 'electron'
|
||||
import { app, clipboard, dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import { gte, lte } from 'semver'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import { showNotification } from '~/utils/common'
|
||||
import {
|
||||
IRemoteNoticeActionType,
|
||||
IRemoteNoticeTriggerCount,
|
||||
IRemoteNoticeTriggerHook
|
||||
} from '#/types/enum'
|
||||
import { IRemoteNoticeActionType, IRemoteNoticeTriggerCount, IRemoteNoticeTriggerHook } from '#/types/enum'
|
||||
|
||||
// for test
|
||||
const REMOTE_NOTICE_URL = 'https://release.piclist.cn/remote-notice.json'
|
||||
@@ -41,7 +32,10 @@ class RemoteNoticeHandler {
|
||||
fs.writeFileSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, JSON.stringify({}))
|
||||
}
|
||||
try {
|
||||
const localCountStorage: IRemoteNoticeLocalCountStorage = fs.readJSONSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, 'utf8')
|
||||
const localCountStorage: IRemoteNoticeLocalCountStorage = fs.readJSONSync(
|
||||
REMOTE_NOTICE_LOCAL_STORAGE_PATH,
|
||||
'utf8'
|
||||
)
|
||||
this.remoteNoticeLocalCountStorage = localCountStorage
|
||||
} catch (e) {
|
||||
this.remoteNoticeLocalCountStorage = localCountStorage
|
||||
@@ -57,11 +51,11 @@ class RemoteNoticeHandler {
|
||||
|
||||
private async getRemoteNoticeInfo(): Promise<IRemoteNotice | null> {
|
||||
try {
|
||||
const noticeInfo = await axios({
|
||||
const noticeInfo = (await axios({
|
||||
method: 'get',
|
||||
url: REMOTE_NOTICE_URL,
|
||||
responseType: 'json'
|
||||
}).then(res => res.data) as IRemoteNotice
|
||||
}).then(res => res.data)) as IRemoteNotice
|
||||
return noticeInfo
|
||||
} catch {
|
||||
return null
|
||||
@@ -143,12 +137,14 @@ class RemoteNoticeHandler {
|
||||
break
|
||||
case IRemoteNoticeActionType.SHOW_MESSAGE_BOX: {
|
||||
const currentWindow = windowManager.getAvailableWindow()
|
||||
dialog.showMessageBox(currentWindow, {
|
||||
dialog
|
||||
.showMessageBox(currentWindow, {
|
||||
title: action.data?.title || '',
|
||||
message: action.data?.content || '',
|
||||
type: 'info',
|
||||
buttons: action.data?.buttons?.map(item => item.label) || ['Yes']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
const button = action.data?.buttons?.[res.response]
|
||||
if (button?.type === 'cancel') {
|
||||
// do nothing
|
||||
@@ -198,6 +194,4 @@ class RemoteNoticeHandler {
|
||||
|
||||
const remoteNoticeHandler = new RemoteNoticeHandler()
|
||||
|
||||
export {
|
||||
remoteNoticeHandler
|
||||
}
|
||||
export { remoteNoticeHandler }
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
import {
|
||||
globalShortcut
|
||||
} from 'electron'
|
||||
import { globalShortcut } from 'electron'
|
||||
|
||||
import shortKeyService from 'apis/app/shortKey/shortKeyService'
|
||||
import GuiApi from 'apis/gui'
|
||||
@@ -72,7 +69,12 @@ class ShortKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private registerShortKey (config: IShortKeyConfig | IPluginShortKeyConfig, command: string, handler: IShortKeyHandler, writeFlag: boolean) {
|
||||
private registerShortKey(
|
||||
config: IShortKeyConfig | IPluginShortKeyConfig,
|
||||
command: string,
|
||||
handler: IShortKeyHandler,
|
||||
writeFlag: boolean
|
||||
) {
|
||||
shortKeyService.registerCommand(command, handler)
|
||||
if (config.key) {
|
||||
globalShortcut.register(config.key, () => {
|
||||
|
||||
@@ -75,7 +75,13 @@ export function createMenu () {
|
||||
label: 'PicList',
|
||||
submenu: [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } }
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
@@ -93,9 +99,7 @@ export function createMenu () {
|
||||
},
|
||||
{
|
||||
label: T('QUIT'),
|
||||
submenu: [
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
submenu: [{ label: T('QUIT'), role: 'quit' }]
|
||||
}
|
||||
])
|
||||
Menu.setApplicationMenu(appMenu)
|
||||
@@ -104,7 +108,8 @@ export function createMenu () {
|
||||
export function createContextMenu() {
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const isMiniWindowVisible = windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
|
||||
const isMiniWindowVisible =
|
||||
windowManager.has(IWindowList.MINI_WINDOW) && windowManager.get(IWindowList.MINI_WINDOW)!.isVisible()
|
||||
|
||||
const startWatchClipboard = () => {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
@@ -125,18 +130,44 @@ export function createContextMenu () {
|
||||
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{ label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } },
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click: startWatchClipboard,
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click: stopWatchClipboard,
|
||||
visible: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('RELOAD_APP'),
|
||||
click() {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
},
|
||||
{ label: T('QUIT'), role: 'quit' }
|
||||
]
|
||||
if (process.platform === 'win32') {
|
||||
template.splice(2, 0,
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible }
|
||||
template.splice(
|
||||
2,
|
||||
0,
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click: hideMiniWindow,
|
||||
visible: isMiniWindowVisible
|
||||
}
|
||||
)
|
||||
}
|
||||
contextMenu = Menu.buildFromTemplate(template)
|
||||
@@ -150,10 +181,28 @@ export function createContextMenu () {
|
||||
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{ label: T('OPEN_MAIN_WINDOW'), click: openMainWindow },
|
||||
{ label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible },
|
||||
{ label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible },
|
||||
{ label: T('START_WATCH_CLIPBOARD'), click: startWatchClipboard, visible: !isListeningClipboard },
|
||||
{ label: T('STOP_WATCH_CLIPBOARD'), click: stopWatchClipboard, visible: isListeningClipboard },
|
||||
{
|
||||
label: T('OPEN_MINI_WINDOW'),
|
||||
click() {
|
||||
openMiniWindow(false)
|
||||
},
|
||||
visible: !isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('HIDE_MINI_WINDOW'),
|
||||
click: hideMiniWindow,
|
||||
visible: isMiniWindowVisible
|
||||
},
|
||||
{
|
||||
label: T('START_WATCH_CLIPBOARD'),
|
||||
click: startWatchClipboard,
|
||||
visible: !isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('STOP_WATCH_CLIPBOARD'),
|
||||
click: stopWatchClipboard,
|
||||
visible: isListeningClipboard
|
||||
},
|
||||
{
|
||||
label: T('ABOUT'),
|
||||
click() {
|
||||
@@ -260,9 +309,7 @@ export function createTray (tooltip: string) {
|
||||
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 imgs = await uploader.setWebContents(trayWindow.webContents).upload(files)
|
||||
const deleteLocalFile = db.get(configPaths.settings.deleteLocalFile) || false
|
||||
if (imgs !== false) {
|
||||
const pasteText: string[] = []
|
||||
@@ -270,8 +317,11 @@ export function createTray (tooltip: string) {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
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)
|
||||
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'),
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import { Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
@@ -19,7 +16,10 @@ import { IPasteStyle, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {
|
||||
const useBuiltinClipboard = db.get(configPaths.settings.useBuiltinClipboard) === undefined ? true : !!db.get(configPaths.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()
|
||||
@@ -33,8 +33,11 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
||||
if (img.length > 0) {
|
||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
||||
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)
|
||||
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'),
|
||||
@@ -75,7 +78,10 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadChoosedFiles = async (webContents: WebContents, files: IFileWithPath[]): Promise<IStringKeyMap[]> => {
|
||||
export const uploadChoosedFiles = async (
|
||||
webContents: WebContents,
|
||||
files: IFileWithPath[]
|
||||
): Promise<IStringKeyMap[]> => {
|
||||
const input = files.map(item => item.path)
|
||||
const rawInput = cloneDeep(input)
|
||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||
@@ -86,14 +92,19 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
|
||||
const pasteText: string[] = []
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (deleteLocalFile) {
|
||||
fs.remove(rawInput[i]).then(() => {
|
||||
fs.remove(rawInput[i])
|
||||
.then(() => {
|
||||
picgo.log.info(`delete local file: ${rawInput[i]}`)
|
||||
}).catch((err: Error) => {
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
picgo.log.error(err)
|
||||
})
|
||||
}
|
||||
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)
|
||||
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'),
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
BrowserWindow,
|
||||
clipboard,
|
||||
ipcMain,
|
||||
Notification,
|
||||
WebContents
|
||||
} from 'electron'
|
||||
import { BrowserWindow, clipboard, ipcMain, Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import util from 'util'
|
||||
import path from 'path'
|
||||
@@ -21,17 +15,13 @@ import logger from '@core/picgo/logger'
|
||||
import { T } from '~/i18n'
|
||||
import { showNotification, getClipboardFilePath, calcDurationRange } from '~/utils/common'
|
||||
|
||||
import {
|
||||
GET_RENAME_FILE_NAME,
|
||||
RENAME_FILE_NAME,
|
||||
TALKING_DATA_EVENT
|
||||
} from '#/events/constants'
|
||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME, TALKING_DATA_EVENT } from '#/events/constants'
|
||||
import { ICOREBuildInEvent, IWindowList } from '#/types/enum'
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
||||
|
||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const windowId = window.id
|
||||
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: Event, newName: string) => {
|
||||
resolve(newName)
|
||||
@@ -91,7 +81,8 @@ class Uploader {
|
||||
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) => {
|
||||
await Promise.all(
|
||||
ctx.output.map(async (item, index) => {
|
||||
let name: undefined | string | null
|
||||
let fileName: string | undefined
|
||||
if (autoRename) {
|
||||
@@ -102,7 +93,7 @@ class Uploader {
|
||||
if (rename) {
|
||||
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
||||
logger.info('create rename window')
|
||||
ipcMain.on(GET_RENAME_FILE_NAME, (evt) => {
|
||||
ipcMain.on(GET_RENAME_FILE_NAME, evt => {
|
||||
try {
|
||||
if (evt.sender.id === window.webContents.id) {
|
||||
logger.info('rename window ready, wait for rename...')
|
||||
@@ -115,7 +106,8 @@ class Uploader {
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
item.fileName = name || fileName
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
export const MANUAL_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#documents`
|
||||
export const MANUAL_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#documents`
|
||||
: 'picgo://./index.html#documents'
|
||||
|
||||
export const MINI_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#mini-page`
|
||||
: 'picgo://./index.html#mini-page'
|
||||
|
||||
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
|
||||
export const RENAME_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#rename-page`
|
||||
: 'picgo://./index.html#rename-page'
|
||||
|
||||
export const SETTING_WINDOW_URL = isDevelopment
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload`
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#main-page/upload`
|
||||
: 'picgo://./index.html#main-page/upload'
|
||||
|
||||
export const TRAY_WINDOW_URL = isDevelopment
|
||||
? (process.env.WEBPACK_DEV_SERVER_URL as string)
|
||||
: 'picgo://./index.html'
|
||||
export const TRAY_WINDOW_URL = isDevelopment ? (process.env.WEBPACK_DEV_SERVER_URL as string) : 'picgo://./index.html'
|
||||
|
||||
export const TOOLBOX_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#toolbox-page`
|
||||
export const TOOLBOX_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `${process.env.WEBPACK_DEV_SERVER_URL as string}#toolbox-page`
|
||||
: 'picgo://./index.html#toolbox-page'
|
||||
|
||||
@@ -23,8 +23,11 @@ const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
|
||||
const handleWindowParams = (windowURL: string) => windowURL
|
||||
|
||||
const getDefaultWindowSizes = (): { width: number, height: number } => {
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([configPaths.settings.mainWindowWidth, configPaths.settings.mainWindowHeight])
|
||||
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([
|
||||
configPaths.settings.mainWindowWidth,
|
||||
configPaths.settings.mainWindowHeight
|
||||
])
|
||||
return {
|
||||
width: mainWindowWidth || 1200,
|
||||
height: mainWindowHeight || 800
|
||||
@@ -232,14 +235,17 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
// bounds: { x: 821, y: 75, width: 800, height: 450 }
|
||||
const bounds = currentWindow.getBounds()
|
||||
const positionX = bounds.x + bounds.width / 2 - 150
|
||||
let positionX = bounds.x + bounds.width / 2 - 150
|
||||
let positionY
|
||||
// if is the settingWindow
|
||||
if (bounds.height > 400) {
|
||||
positionY = bounds.y + bounds.height / 2 - 88
|
||||
} else { // if is the miniWindow
|
||||
} else {
|
||||
// if is the miniWindow
|
||||
positionY = bounds.y + bounds.height / 2
|
||||
}
|
||||
positionX = Math.floor(positionX)
|
||||
positionY = Math.floor(positionY)
|
||||
window.setPosition(positionX, positionY, false)
|
||||
}
|
||||
}
|
||||
@@ -254,13 +260,15 @@ windowList.set(IWindowList.TOOLBOX_WINDOW, {
|
||||
const currentWindow = windowManager.getAvailableWindow(true)
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
const bounds = currentWindow.getBounds()
|
||||
const positionX = bounds.x + bounds.width / 2 - 400
|
||||
let positionX = bounds.x + bounds.width / 2 - 400
|
||||
let positionY
|
||||
if (bounds.height > 400) {
|
||||
positionY = bounds.y + bounds.height / 2 - 225
|
||||
} else {
|
||||
positionY = bounds.y + bounds.height / 2
|
||||
}
|
||||
positionX = Math.floor(positionX)
|
||||
positionY = Math.floor(positionY)
|
||||
window.setPosition(positionX, positionY, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
} from '@core/bus/constants'
|
||||
|
||||
export const uploadWithClipboardFiles = (): Promise<{
|
||||
success: boolean,
|
||||
success: boolean
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, (result: string) => {
|
||||
if (result) {
|
||||
return resolve({
|
||||
@@ -31,11 +31,13 @@ export const uploadWithClipboardFiles = (): Promise<{
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
success: boolean,
|
||||
export const uploadWithFiles = (
|
||||
pathList: IFileWithPath[]
|
||||
): Promise<{
|
||||
success: boolean
|
||||
result?: string[]
|
||||
}> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(UPLOAD_WITH_FILES_RESPONSE, (result: string[]) => {
|
||||
if (result.length) {
|
||||
return resolve({
|
||||
@@ -55,7 +57,7 @@ export const uploadWithFiles = (pathList: IFileWithPath[]): Promise<{
|
||||
// get available window id:
|
||||
// miniWindow or settingWindow or trayWindow
|
||||
export const getWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(GET_WINDOW_ID_REPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
@@ -65,7 +67,7 @@ export const getWindowId = (): Promise<number> => {
|
||||
|
||||
// get settingWindow id:
|
||||
export const getSettingWindowId = (): Promise<number> => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
bus.once(GET_SETTING_WINDOW_ID_RESPONSE, (id: number) => {
|
||||
resolve(id)
|
||||
})
|
||||
|
||||
@@ -54,7 +54,9 @@ function dbChecker () {
|
||||
fs.unlinkSync(configFilePath)
|
||||
if (fs.existsSync(configFileBackupPath)) {
|
||||
try {
|
||||
configFile = fs.readFileSync(configFileBackupPath, { encoding: 'utf-8' })
|
||||
configFile = fs.readFileSync(configFileBackupPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
JSON.parse(configFile)
|
||||
writeFile.sync(configFilePath, configFile, { encoding: 'utf-8' })
|
||||
const stats = fs.statSync(configFileBackupPath)
|
||||
@@ -91,7 +93,9 @@ function dbPathChecker (): string {
|
||||
return _configFilePath
|
||||
}
|
||||
try {
|
||||
const configString = fs.readFileSync(defaultConfigPath, { encoding: 'utf-8' })
|
||||
const configString = fs.readFileSync(defaultConfigPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const config = JSON.parse(configString)
|
||||
const userConfigPath: string = config.configPath || ''
|
||||
if (userConfigPath) {
|
||||
@@ -135,9 +139,4 @@ function getGalleryDBPath (): {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
dbChecker,
|
||||
dbPathChecker,
|
||||
dbPathDir,
|
||||
getGalleryDBPath
|
||||
}
|
||||
export { dbChecker, dbPathChecker, dbPathDir, getGalleryDBPath }
|
||||
|
||||
@@ -115,6 +115,4 @@ class GalleryDB {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
GalleryDB
|
||||
}
|
||||
export { GalleryDB }
|
||||
|
||||
@@ -94,6 +94,4 @@ const getLogger = (logPath: string, logType: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getLogger
|
||||
}
|
||||
export { getLogger }
|
||||
|
||||
+32
-27
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
BrowserWindow,
|
||||
dialog,
|
||||
ipcMain,
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { BrowserWindow, dialog, ipcMain, Notification } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { DBStore } from '@picgo/store'
|
||||
@@ -47,7 +42,7 @@ class GuiApi implements IGuiApi {
|
||||
return true
|
||||
}
|
||||
settingWindow?.show()
|
||||
return new Promise<void>((resolve) => {
|
||||
return new Promise<void>(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve()
|
||||
}, 1000) // TODO: a better way to wait page loaded.
|
||||
@@ -58,13 +53,15 @@ class GuiApi implements IGuiApi {
|
||||
return BrowserWindow.fromId(id)?.webContents
|
||||
}
|
||||
|
||||
async showInputBox (options: IShowInputBoxOption = {
|
||||
async showInputBox(
|
||||
options: IShowInputBoxOption = {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
await this.showSettingWindow()
|
||||
this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options)
|
||||
return new Promise<string>((resolve) => {
|
||||
return new Promise<string>(resolve => {
|
||||
ipcMain.once(SHOW_INPUT_BOX, (_: Event, value: string) => {
|
||||
resolve(value)
|
||||
})
|
||||
@@ -90,8 +87,11 @@ class GuiApi implements IGuiApi {
|
||||
if (deleteLocalFile) {
|
||||
await fs.remove(rawInput[i])
|
||||
}
|
||||
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)
|
||||
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'),
|
||||
@@ -112,10 +112,12 @@ class GuiApi implements IGuiApi {
|
||||
return []
|
||||
}
|
||||
|
||||
showNotification (options: IShowNotificationOption = {
|
||||
showNotification(
|
||||
options: IShowNotificationOption = {
|
||||
title: '',
|
||||
body: ''
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body
|
||||
@@ -123,18 +125,17 @@ class GuiApi implements IGuiApi {
|
||||
notification.show()
|
||||
}
|
||||
|
||||
showMessageBox (options: IShowMessageBoxOption = {
|
||||
showMessageBox(
|
||||
options: IShowMessageBoxOption = {
|
||||
title: '',
|
||||
message: '',
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}) {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve) => {
|
||||
}
|
||||
) {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
this.windowId = await getWindowId()
|
||||
dialog.showMessageBox(
|
||||
BrowserWindow.fromId(this.windowId)!,
|
||||
options
|
||||
).then((res) => {
|
||||
dialog.showMessageBox(BrowserWindow.fromId(this.windowId)!, options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
@@ -162,14 +163,16 @@ class GuiApi implements IGuiApi {
|
||||
if (prop === 'overwrite') {
|
||||
return new Proxy(GalleryDB.getInstance().overwrite, {
|
||||
apply(target, ctx, args) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi.showMessageBox({
|
||||
guiApi
|
||||
.showMessageBox({
|
||||
title: T('TIPS_WARNING'),
|
||||
message: T('TIPS_PLUGIN_REMOVE_GALLERY_ITEM'),
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.result === 0) {
|
||||
resolve(Reflect.apply(target, ctx, args))
|
||||
} else {
|
||||
@@ -183,14 +186,16 @@ class GuiApi implements IGuiApi {
|
||||
if (prop === 'removeById') {
|
||||
return new Proxy(GalleryDB.getInstance().removeById, {
|
||||
apply(target, ctx, args) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const guiApi = GuiApi.getInstance()
|
||||
guiApi.showMessageBox({
|
||||
guiApi
|
||||
.showMessageBox({
|
||||
title: T('TIPS_WARNING'),
|
||||
message: T('TIPS_PLUGIN_REMOVE_GALLERY_ITEM'),
|
||||
type: 'info',
|
||||
buttons: ['Yes', 'No']
|
||||
}).then(res => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.result === 0) {
|
||||
resolve(Reflect.apply(target, ctx, args))
|
||||
} else {
|
||||
|
||||
@@ -11,13 +11,8 @@ import {
|
||||
UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE
|
||||
} from '@core/bus/constants'
|
||||
|
||||
import {
|
||||
createMenu
|
||||
} from 'apis/app/system'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { createMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
BrowserWindow,
|
||||
Menu,
|
||||
shell,
|
||||
MenuItemConstructorOptions,
|
||||
MenuItem
|
||||
} from 'electron'
|
||||
import { app, dialog, BrowserWindow, Menu, shell, MenuItemConstructorOptions, MenuItem } from 'electron'
|
||||
import { PicGo as PicGoCore } from 'piclist'
|
||||
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import {
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
@@ -47,7 +37,7 @@ const buildMiniPageMenu = () => {
|
||||
const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
const submenu = buildPicBedListMenu()
|
||||
const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [
|
||||
const template: Array<MenuItemConstructorOptions | MenuItem> = [
|
||||
{
|
||||
label: T('OPEN_MAIN_WINDOW'),
|
||||
click: openMainWindow
|
||||
@@ -145,8 +135,7 @@ const buildMainPageMenu = (win: BrowserWindow) => {
|
||||
shell.openExternal(url)
|
||||
}
|
||||
}
|
||||
]
|
||||
// @ts-ignore
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
return Menu.buildFromTemplate(template)
|
||||
}
|
||||
|
||||
@@ -155,28 +144,33 @@ const buildPicBedListMenu = () => {
|
||||
const currentPicBed = picgo.getConfig(configPaths.picBed.uploader)
|
||||
const currentPicBedName = picBeds.find(item => item.type === currentPicBed)?.name
|
||||
const picBedConfigList = picgo.getConfig<IUploaderConfig>('uploader')
|
||||
const currentPicBedMenuItem = [{
|
||||
const currentPicBedMenuItem = [
|
||||
{
|
||||
label: `${T('CURRENT_PICBED')} - ${currentPicBedName}`,
|
||||
enabled: false
|
||||
}, {
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
}]
|
||||
let submenu = picBeds.filter(item => item.visible).map(item => {
|
||||
}
|
||||
]
|
||||
let submenu = picBeds
|
||||
.filter(item => item.visible)
|
||||
.map(item => {
|
||||
const configList = picBedConfigList?.[item.type]?.configList
|
||||
const defaultId = picBedConfigList?.[item.type]?.defaultId
|
||||
const hasSubmenu = !!configList
|
||||
return {
|
||||
label: item.name,
|
||||
type: !hasSubmenu ? 'checkbox' : undefined,
|
||||
checked: !hasSubmenu ? (currentPicBed === item.type) : undefined,
|
||||
checked: !hasSubmenu ? currentPicBed === item.type : undefined,
|
||||
submenu: hasSubmenu
|
||||
? configList.map((config) => {
|
||||
? configList.map(config => {
|
||||
return {
|
||||
label: config._configName || 'Default',
|
||||
// if only one config, use checkbox, or radio will checked as default
|
||||
// see: https://github.com/electron/electron/issues/21292
|
||||
type: 'checkbox',
|
||||
checked: config._id === defaultId && (item.type === currentPicBed),
|
||||
checked: config._id === defaultId && item.type === currentPicBed,
|
||||
click: function () {
|
||||
changeCurrentUploader(item.type, config, config._id)
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
@@ -201,9 +195,9 @@ const buildPicBedListMenu = () => {
|
||||
: undefined
|
||||
}
|
||||
})
|
||||
// @ts-ignore
|
||||
// @ts-expect-error submenu type
|
||||
submenu = currentPicBedMenuItem.concat(submenu)
|
||||
// @ts-ignore
|
||||
// @ts-expect-error submenu type
|
||||
return Menu.buildFromTemplate(submenu)
|
||||
}
|
||||
|
||||
@@ -230,7 +224,8 @@ const handleRestoreState = (item: string, name: string): void => {
|
||||
}
|
||||
|
||||
const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
const menu = [{
|
||||
const menu = [
|
||||
{
|
||||
label: T('ENABLE_PLUGIN'),
|
||||
enabled: !plugin.enabled,
|
||||
click() {
|
||||
@@ -240,7 +235,8 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_TOGGLE_PLUGIN, plugin.fullName, true)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('DISABLE_PLUGIN'),
|
||||
enabled: plugin.enabled,
|
||||
click() {
|
||||
@@ -258,21 +254,24 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
handleRestoreState('uploader', plugin.config.uploader.name)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('UNINSTALL_PLUGIN'),
|
||||
click() {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUninstall(plugin.fullName)
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
label: T('UPDATE_PLUGIN'),
|
||||
click() {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName)
|
||||
handlePluginUpdate(plugin.fullName)
|
||||
}
|
||||
}]
|
||||
}
|
||||
] as Array<MenuItemConstructorOptions | MenuItem>
|
||||
for (const i in plugin.config) {
|
||||
if (plugin.config[i].config.length > 0) {
|
||||
const obj = {
|
||||
@@ -317,7 +316,6 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
// plugin custom menus
|
||||
if (plugin.guiMenu) {
|
||||
menu.push({
|
||||
// @ts-ignore
|
||||
type: 'separator'
|
||||
})
|
||||
for (const i of plugin.guiMenu) {
|
||||
@@ -338,13 +336,7 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => {
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return Menu.buildFromTemplate(menu)
|
||||
}
|
||||
|
||||
export {
|
||||
buildMiniPageMenu,
|
||||
buildMainPageMenu,
|
||||
buildPicBedListMenu,
|
||||
buildPluginPageMenu
|
||||
}
|
||||
export { buildMiniPageMenu, buildMainPageMenu, buildPicBedListMenu, buildPluginPageMenu }
|
||||
|
||||
@@ -10,6 +10,7 @@ import { systemRouter } from '~/events/rpc/routes/system'
|
||||
import { toolboxRouter } from '~/events/rpc/routes/toolbox'
|
||||
import { trayRouter } from '~/events/rpc/routes/tray'
|
||||
import { uploadRouter } from '~/events/rpc/routes/upload'
|
||||
import { manageRouter } from '~/events/rpc/routes/manage'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import { RPC_ACTIONS, RPC_ACTIONS_INVOKE } from '#/events/constants'
|
||||
@@ -75,13 +76,12 @@ const routes = [
|
||||
systemRouter.routes(),
|
||||
toolboxRouter.routes(),
|
||||
trayRouter.routes(),
|
||||
uploadRouter.routes()
|
||||
uploadRouter.routes(),
|
||||
manageRouter.routes()
|
||||
]
|
||||
|
||||
for (const route of routes) {
|
||||
rpcServer.use(route)
|
||||
}
|
||||
|
||||
export {
|
||||
rpcServer
|
||||
}
|
||||
export { rpcServer }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { clipboard } from 'electron'
|
||||
|
||||
import { GalleryDB } from '@core/datastore'
|
||||
@@ -8,7 +7,12 @@ import { IFilter, IObject } from '@picgo/store/dist/types'
|
||||
import GuiApi from 'apis/gui'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { removeFileFromDogeInMain, removeFileFromHuaweiInMain, removeFileFromS3InMain, removeFileFromSFTPInMain } from '~/utils/deleteFunc'
|
||||
import {
|
||||
removeFileFromDogeInMain,
|
||||
removeFileFromHuaweiInMain,
|
||||
removeFileFromS3InMain,
|
||||
removeFileFromSFTPInMain
|
||||
} from '~/utils/deleteFunc'
|
||||
import pasteTemplate from '~/utils/pasteTemplate'
|
||||
|
||||
import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum'
|
||||
@@ -128,6 +132,4 @@ const galleryRoutes = [
|
||||
|
||||
galleryRouter.addBatch(galleryRoutes)
|
||||
|
||||
export {
|
||||
galleryRouter
|
||||
}
|
||||
export { galleryRouter }
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { ManageApi } from '~/manage/manageApi'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_BUCKET_LIST,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string]) => {
|
||||
return new ManageApi(args[0]).getBucketList()
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_BUCKET_LIST_BACKSTAGE,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).getBucketListBackstage(args[1])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_BUCKET_LIST_RECURSIVELY,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).getBucketListRecursively(args[1])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_CREATE_BUCKET,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).createBucket(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_BUCKET_FILE_LIST,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).getBucketFileList(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_BUCKET_DOMAIN,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).getBucketDomain(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_SET_BUCKET_ACL_POLICY,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).setBucketAclPolicy(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_RENAME_BUCKET_FILE,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).renameBucketFile(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_BUCKET_FILE,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).deleteBucketFile(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_BUCKET_FOLDER,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).deleteBucketFolder(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_PRE_SIGNED_URL,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).getPreSignedUrl(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_UPLOAD_BUCKET_FILE,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).uploadBucketFile(args[1])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DOWNLOAD_BUCKET_FILE,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).downloadBucketFile(args[1])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_CREATE_BUCKET_FOLDER,
|
||||
handler: async (_: IIPCEvent, args: [currentPicBed: string, param: IStringKeyMap]) => {
|
||||
return new ManageApi(args[0]).createBucketFolder(args[1])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
import getManageApi from '~/manage/Main'
|
||||
|
||||
const manageApi = getManageApi()
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [key?: string]) => {
|
||||
return manageApi.getConfig(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_SAVE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [data: IObj]) => {
|
||||
manageApi.saveConfig(args[0])
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_REMOVE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [key: string, propName: string]) => {
|
||||
manageApi.removeConfig(args[0], args[1])
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
|
||||
import configRoutes from '~/events/rpc/routes/manage/config'
|
||||
import bucketRoutes from '~/events/rpc/routes/manage/bucket'
|
||||
import upDownLoadRoutes from '~/events/rpc/routes/manage/upDownload'
|
||||
|
||||
const manageRouter = new RPCRouter()
|
||||
|
||||
const manageRoutes = [...configRoutes, ...bucketRoutes, ...upDownLoadRoutes]
|
||||
|
||||
manageRouter.addBatch(manageRoutes)
|
||||
|
||||
export { manageRouter }
|
||||
@@ -0,0 +1,108 @@
|
||||
import { app, dialog, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import { IRPCActionType, IRPCType } from '#/types/enum'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { downloadFileFromUrl } from '~/manage/utils/common'
|
||||
|
||||
export default [
|
||||
{
|
||||
action: IRPCActionType.MANAGE_OPEN_FILE_SELECT_DIALOG,
|
||||
handler: async () => {
|
||||
const res = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections']
|
||||
})
|
||||
return res.canceled ? [] : res.filePaths
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_UPLOAD_TASK_LIST,
|
||||
handler: async () => {
|
||||
return UpDownTaskQueue.getInstance().getAllUploadTask()
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_DOWNLOAD_TASK_LIST,
|
||||
handler: async () => {
|
||||
return UpDownTaskQueue.getInstance().getAllDownloadTask()
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_UPLOADED_TASK,
|
||||
handler: async () => {
|
||||
UpDownTaskQueue.getInstance().removeUploadedTask()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_ALL_UPLOADED_TASK,
|
||||
handler: async () => {
|
||||
UpDownTaskQueue.getInstance().clearUploadTaskQueue()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_DOWNLOADED_TASK,
|
||||
handler: async () => {
|
||||
UpDownTaskQueue.getInstance().removeDownloadedTask()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DELETE_ALL_DOWNLOADED_TASK,
|
||||
handler: async () => {
|
||||
UpDownTaskQueue.getInstance().clearDownloadTaskQueue()
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_SELECT_DOWNLOAD_FOLDER,
|
||||
handler: async () => {
|
||||
const res = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
return res.filePaths[0]
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_GET_DEFAULT_DOWNLOAD_FOLDER,
|
||||
handler: async () => {
|
||||
return app.getPath('downloads')
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_OPEN_DOWNLOADED_FOLDER,
|
||||
handler: async (_: IIPCEvent, args: [path?: string]) => {
|
||||
const path = args[0]
|
||||
if (path) {
|
||||
shell.showItemInFolder(path)
|
||||
} else {
|
||||
shell.openPath(app.getPath('downloads'))
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_OPEN_LOCAL_FILE,
|
||||
handler: async (_: IIPCEvent, args: [fullPath: string]) => {
|
||||
const fullPath = args[0]
|
||||
fs.existsSync(fullPath) ? shell.showItemInFolder(fullPath) : shell.openPath(path.dirname(fullPath))
|
||||
}
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_DOWNLOAD_FILE_FROM_URL,
|
||||
handler: async (_: IIPCEvent, args: [urls: string[]]) => {
|
||||
return await downloadFileFromUrl(args[0])
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.MANAGE_CONVERT_PATH_TO_BASE64,
|
||||
handler: async (_: IIPCEvent, args: [filePath: string]) => {
|
||||
return fs.readFileSync(args[0], 'base64')
|
||||
},
|
||||
type: IRPCType.INVOKE
|
||||
}
|
||||
]
|
||||
@@ -94,6 +94,4 @@ const picbedRoutes = [
|
||||
|
||||
picbedRouter.addBatch(picbedRoutes)
|
||||
|
||||
export {
|
||||
picbedRouter
|
||||
}
|
||||
export { picbedRouter }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import {
|
||||
pluginImportLocalFunc,
|
||||
@@ -32,6 +31,4 @@ const pluginRoutes = [
|
||||
|
||||
pluginRouter.addBatch(pluginRoutes)
|
||||
|
||||
export {
|
||||
pluginRouter
|
||||
}
|
||||
export { pluginRouter }
|
||||
|
||||
@@ -104,11 +104,13 @@ const getPluginList = (): IPicGoPlugin[] => {
|
||||
const handleNPMError = (): IDispose => {
|
||||
const handler = (msg: string) => {
|
||||
if (msg === 'NPM is not installed') {
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
title: T('TIPS_ERROR'),
|
||||
message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'),
|
||||
buttons: ['Yes']
|
||||
}).then((res) => {
|
||||
})
|
||||
.then(res => {
|
||||
if (res.response === 0) {
|
||||
shell.openExternal('https://nodejs.org/')
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { IRPCActionType } from '#/types/enum'
|
||||
import server from '~/server'
|
||||
import webServer from '~/server/webServer'
|
||||
|
||||
@@ -16,19 +16,15 @@ export default [
|
||||
action: IRPCActionType.CONFIGURE_MIGRATE_FROM_PICGO,
|
||||
handler: async () => {
|
||||
const picGoConfigPath = STORE_PATH.replace('piclist', 'picgo')
|
||||
const files = [
|
||||
'data.json',
|
||||
'data.bak.json',
|
||||
'picgo.db',
|
||||
'picgo.bak.db'
|
||||
]
|
||||
const files = ['data.json', 'data.bak.json', 'picgo.db', 'picgo.bak.db']
|
||||
try {
|
||||
await Promise.all(files.map(async file => {
|
||||
await Promise.all(
|
||||
files.map(async file => {
|
||||
const sourcePath = path.join(picGoConfigPath, file)
|
||||
const targetPath = path.join(STORE_PATH, file.replace('picgo', 'piclist'))
|
||||
await fs.copy(sourcePath, targetPath, { overwrite: true })
|
||||
}
|
||||
))
|
||||
})
|
||||
)
|
||||
return true
|
||||
} catch (err: any) {
|
||||
logger.error(err)
|
||||
|
||||
@@ -7,15 +7,8 @@ import shortKeyRoutes from '~/events/rpc/routes/setting/shortKey'
|
||||
|
||||
const settingRouter = new RPCRouter()
|
||||
|
||||
const settingRoutes = [
|
||||
...advancedRoutes,
|
||||
...configureRoutes,
|
||||
...mainAppRoutes,
|
||||
...shortKeyRoutes
|
||||
]
|
||||
const settingRoutes = [...advancedRoutes, ...configureRoutes, ...mainAppRoutes, ...shortKeyRoutes]
|
||||
|
||||
settingRouter.addBatch(settingRoutes)
|
||||
|
||||
export {
|
||||
settingRouter
|
||||
}
|
||||
export { settingRouter }
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
|
||||
@@ -4,13 +4,8 @@ import windowRoutes from '~/events/rpc/routes/system/window'
|
||||
|
||||
const systemRouter = new RPCRouter()
|
||||
|
||||
const systemRoutes = [
|
||||
...appRoutes,
|
||||
...windowRoutes
|
||||
]
|
||||
const systemRoutes = [...appRoutes, ...windowRoutes]
|
||||
|
||||
systemRouter.addBatch(systemRoutes)
|
||||
|
||||
export {
|
||||
systemRouter
|
||||
}
|
||||
export { systemRouter }
|
||||
|
||||
@@ -2,12 +2,7 @@ import { app, BrowserWindow } from 'electron'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import {
|
||||
buildMainPageMenu,
|
||||
buildMiniPageMenu,
|
||||
buildPicBedListMenu,
|
||||
buildPluginPageMenu
|
||||
} from '~/events/remotes/menu'
|
||||
import { buildMainPageMenu, buildMiniPageMenu, buildPicBedListMenu, buildPluginPageMenu } from '~/events/remotes/menu'
|
||||
import { openMiniWindow } from '~/utils/windowHelper'
|
||||
|
||||
import { IRPCActionType, IWindowList } from '#/types/enum'
|
||||
|
||||
@@ -14,10 +14,8 @@ const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_
|
||||
|
||||
const defaultClipboardImagePath = path.join(defaultConfigPath, CLIPBOARD_IMAGE_FOLDER)
|
||||
|
||||
export const checkClipboardUploadMap: IToolboxCheckerMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async (event) => {
|
||||
export const checkClipboardUploadMap: IToolboxCheckerMap<IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async event => {
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
})
|
||||
@@ -54,9 +52,7 @@ IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
}
|
||||
}
|
||||
|
||||
export const fixClipboardUploadMap: IToolboxFixMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD
|
||||
> = {
|
||||
export const fixClipboardUploadMap: IToolboxFixMap<IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: async () => {
|
||||
const configFilePath = dbPathChecker()
|
||||
const dirPath = path.dirname(configFilePath)
|
||||
|
||||
@@ -38,7 +38,7 @@ IToolboxItemType.IS_CONFIG_FILE_BROKEN | IToolboxItemType.IS_GALLERY_FILE_BROKEN
|
||||
})
|
||||
}
|
||||
},
|
||||
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: async (event) => {
|
||||
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: async event => {
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.IS_GALLERY_FILE_BROKEN)
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
import { IConfig } from 'piclist'
|
||||
@@ -27,10 +26,8 @@ function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null {
|
||||
|
||||
const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY)
|
||||
|
||||
export const checkProxyMap: IToolboxCheckerMap<
|
||||
IToolboxItemType.HAS_PROBLEM_WITH_PROXY
|
||||
> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async (event) => {
|
||||
export const checkProxyMap: IToolboxCheckerMap<IToolboxItemType.HAS_PROBLEM_WITH_PROXY> = {
|
||||
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async event => {
|
||||
sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.LOADING
|
||||
})
|
||||
@@ -38,9 +35,8 @@ IToolboxItemType.HAS_PROBLEM_WITH_PROXY
|
||||
if (fs.existsSync(configFilePath)) {
|
||||
let config: IConfig | undefined
|
||||
try {
|
||||
config = await fs.readJSON(configFilePath) as IConfig
|
||||
} catch (e) {
|
||||
}
|
||||
config = (await fs.readJSON(configFilePath)) as IConfig
|
||||
} catch (e) {}
|
||||
if (!config) {
|
||||
return sendToolboxRes(event, {
|
||||
status: IToolboxItemCheckStatus.SUCCESS,
|
||||
|
||||
@@ -42,7 +42,8 @@ toolboxRouter
|
||||
IRPCType.SEND
|
||||
)
|
||||
.add(
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX, async (event, args) => {
|
||||
IRPCActionType.TOOLBOX_CHECK_FIX,
|
||||
async (event, args) => {
|
||||
const [type] = args as IToolboxCheckArgs
|
||||
const handler = toolboxFixMap[type]
|
||||
if (handler) {
|
||||
@@ -52,6 +53,4 @@ toolboxRouter
|
||||
IRPCType.INVOKE
|
||||
)
|
||||
|
||||
export {
|
||||
toolboxRouter
|
||||
}
|
||||
export { toolboxRouter }
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import { RPCRouter } from '~/events/rpc/router'
|
||||
import { generateShortUrl, setTrayToolTip, handleCopyUrl } from '~/utils/common'
|
||||
@@ -42,8 +40,11 @@ const trayRoutes = [
|
||||
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
||||
if (img !== false) {
|
||||
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)
|
||||
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'),
|
||||
@@ -66,6 +67,4 @@ const trayRoutes = [
|
||||
|
||||
trayRouter.addBatch(trayRoutes)
|
||||
|
||||
export {
|
||||
trayRouter
|
||||
}
|
||||
export { trayRouter }
|
||||
|
||||
@@ -30,6 +30,4 @@ const uploadRoutes = [
|
||||
|
||||
uploadRouter.addBatch(uploadRoutes)
|
||||
|
||||
export {
|
||||
uploadRouter
|
||||
}
|
||||
export { uploadRouter }
|
||||
|
||||
@@ -27,9 +27,11 @@ export function startFileServer () {
|
||||
})
|
||||
})
|
||||
|
||||
server.listen(serverPort, () => {
|
||||
server
|
||||
.listen(serverPort, () => {
|
||||
logger.info(`File server is running, http://localhost:${serverPort}`)
|
||||
}).on('error', (err) => {
|
||||
})
|
||||
.on('error', err => {
|
||||
logger.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// TODO: so how to import pure esm module in electron main process????? help wanted
|
||||
|
||||
// just copy the fix-path because I can't import pure ESM module in electron main process
|
||||
// @ts-nocheck
|
||||
// @ts-nocheck since the module is not pure ESM
|
||||
// shell-path 3.0.0 not work
|
||||
|
||||
const shellPath = require('shell-path')
|
||||
@@ -11,10 +11,6 @@ export default function fixPath () {
|
||||
return
|
||||
}
|
||||
|
||||
process.env.PATH = shellPath.sync() || [
|
||||
'./node_modules/.bin',
|
||||
'/.nodebrew/current/bin',
|
||||
'/usr/local/bin',
|
||||
process.env.PATH
|
||||
].join(':')
|
||||
process.env.PATH =
|
||||
shellPath.sync() || ['./node_modules/.bin', '/.nodebrew/current/bin', '/usr/local/bin', process.env.PATH].join(':')
|
||||
}
|
||||
|
||||
+32
-39
@@ -1,19 +1,9 @@
|
||||
import axios from 'axios'
|
||||
import fs from 'fs-extra'
|
||||
import {
|
||||
app,
|
||||
globalShortcut,
|
||||
protocol,
|
||||
Notification,
|
||||
dialog,
|
||||
screen,
|
||||
shell
|
||||
} from 'electron'
|
||||
import { app, globalShortcut, protocol, Notification, dialog, screen, shell } from 'electron'
|
||||
import { UpdateInfo, autoUpdater } from 'electron-updater'
|
||||
import path from 'path'
|
||||
import {
|
||||
createProtocol
|
||||
} from 'vue-cli-plugin-electron-builder/lib'
|
||||
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
@@ -22,13 +12,8 @@ import logger from '@core/picgo/logger'
|
||||
|
||||
import { remoteNoticeHandler } from 'apis/app/remoteNotice'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import {
|
||||
createTray, setDockMenu
|
||||
} from 'apis/app/system'
|
||||
import {
|
||||
uploadChoosedFiles,
|
||||
uploadClipboardFiles
|
||||
} from 'apis/app/uploader/apis'
|
||||
import { createTray, setDockMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import busEventList from '~/events/busEventList'
|
||||
@@ -37,7 +22,6 @@ import { T } from '~/i18n'
|
||||
import '~/lifeCycle/errorHandler'
|
||||
import fixPath from '~/lifeCycle/fixPath'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { manageIpcList } from '~/manage/events/ipcList'
|
||||
import getManageApi from '~/manage/Main'
|
||||
import { clearTempFolder } from '~/manage/utils/common'
|
||||
import server from '~/server/index'
|
||||
@@ -57,7 +41,8 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
const files = getUploadFiles(argv, cwd, logger)
|
||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null || files.length > 0) {
|
||||
// 如果有文件列表作为参数,说明是命令行启动
|
||||
if (files === null) {
|
||||
logger.info('cli -> uploading file from clipboard')
|
||||
uploadClipboardFiles()
|
||||
@@ -83,34 +68,43 @@ autoUpdater.on('update-available', async (info: UpdateInfo) => {
|
||||
const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
let updateLog = ''
|
||||
try {
|
||||
const url = lang === II18nLanguage.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) {
|
||||
logger.error(e)
|
||||
}
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: T('FIND_NEW_VERSION'),
|
||||
buttons: ['Yes', 'Go to download page'],
|
||||
message: T('TIPS_FIND_NEW_VERSION', {
|
||||
message:
|
||||
T('TIPS_FIND_NEW_VERSION', {
|
||||
v: info.version
|
||||
}) + '\n\n' + updateLog,
|
||||
}) +
|
||||
'\n\n' +
|
||||
updateLog,
|
||||
checkboxLabel: T('NO_MORE_NOTICE'),
|
||||
checkboxChecked: false
|
||||
}).then((result) => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.response === 0) {
|
||||
autoUpdater.downloadUpdate()
|
||||
} else {
|
||||
shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest')
|
||||
}
|
||||
db.set(configPaths.settings.showUpdateTip, !result.checkboxChecked)
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('download-progress', (progressObj) => {
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
const percent = {
|
||||
progress: progressObj.percent
|
||||
}
|
||||
@@ -119,23 +113,26 @@ autoUpdater.on('download-progress', (progressObj) => {
|
||||
})
|
||||
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
dialog.showMessageBox({
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: T('UPDATE_DOWNLOADED'),
|
||||
buttons: ['Yes', 'No'],
|
||||
message: T('TIPS_UPDATE_DOWNLOADED')
|
||||
}).then((result) => {
|
||||
})
|
||||
.then(result => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', { progress: 100 })
|
||||
if (result.response === 0) {
|
||||
autoUpdater.quitAndInstall()
|
||||
}
|
||||
}).catch((err) => {
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
autoUpdater.on('error', err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
@@ -145,11 +142,10 @@ class LifeCycle {
|
||||
// fix the $PATH in macOS & linux
|
||||
fixPath()
|
||||
beforeOpen()
|
||||
initI18n()
|
||||
rpcServer.start()
|
||||
getManageApi()
|
||||
UpDownTaskQueue.getInstance()
|
||||
manageIpcList.listen()
|
||||
initI18n()
|
||||
rpcServer.start()
|
||||
busEventList.listen()
|
||||
}
|
||||
|
||||
@@ -173,7 +169,6 @@ class LifeCycle {
|
||||
const isHideDock = db.get(configPaths.settings.isHideDock) || false
|
||||
const startMode = db.get(configPaths.settings.startMode) || ISartMode.QUIET
|
||||
const currentPicBed = db.get(configPaths.picBed.uploader) || db.get(configPaths.picBed.current) || 'smms'
|
||||
// @ts-ignore
|
||||
const currentPicBedConfig = db.get(`picBed.${currentPicBed}`)?._configName || 'Default'
|
||||
const tooltip = `${currentPicBed} ${currentPicBedConfig}`
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -319,6 +314,4 @@ class LifeCycle {
|
||||
|
||||
const bootstrap = new LifeCycle()
|
||||
|
||||
export {
|
||||
bootstrap
|
||||
}
|
||||
export { bootstrap }
|
||||
|
||||
@@ -8,7 +8,13 @@ import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { hmacSha1Base64, getFileMimeType, formatError, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import {
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
formatError,
|
||||
NewDownloader,
|
||||
ConcurrencyPromisePool
|
||||
} from '~/manage/utils/common'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { isImage } from '#/utils/common'
|
||||
@@ -72,13 +78,19 @@ class AliyunApi {
|
||||
}, {} as IStringKeyMap)
|
||||
let canonicalizedOSSHeaders = ''
|
||||
const headerKeys = Object.keys(lowerCaseHeaders).sort()
|
||||
headerKeys.forEach((key) => {
|
||||
headerKeys.forEach(key => {
|
||||
key.startsWith('x-oss-') && (canonicalizedOSSHeaders += `${key}:${lowerCaseHeaders[key]}\n`)
|
||||
})
|
||||
return canonicalizedOSSHeaders
|
||||
}
|
||||
|
||||
authorization (method: string, canonicalizedResource: string, headers: IStringKeyMap, contentMd5: string, contentType: string) {
|
||||
authorization(
|
||||
method: string,
|
||||
canonicalizedResource: string,
|
||||
headers: IStringKeyMap,
|
||||
contentMd5: string,
|
||||
contentType: string
|
||||
) {
|
||||
const date = new Date().toUTCString()
|
||||
const stringToSign = `${method.toUpperCase()}\n${contentMd5}\n${contentType}\n${date}\n${this.getCanonicalizedOSSHeaders(headers)}${canonicalizedResource}`
|
||||
return `OSS ${this.accessKeyId}:${hmacSha1Base64(this.accessKeySecret, stringToSign)}`
|
||||
@@ -99,17 +111,21 @@ class AliyunApi {
|
||||
*/
|
||||
async getBucketList(): Promise<any> {
|
||||
const getBuckets = async (marker?: string) => {
|
||||
const res = await this.ctx.listBuckets({
|
||||
const res = (await this.ctx.listBuckets({
|
||||
marker,
|
||||
'max-keys': 1000
|
||||
}) as IStringKeyMap
|
||||
})) as IStringKeyMap
|
||||
if (res?.res?.statusCode !== 200 || !res?.buckets) return { result: [], isTruncated: false }
|
||||
const formattedBuckets = res.buckets.map((item: OSS.Bucket) => ({
|
||||
Name: item.name,
|
||||
Location: item.region,
|
||||
CreationDate: item.creationDate
|
||||
}))
|
||||
return { result: formattedBuckets, isTruncated: res.isTruncated, nextMarker: res.nextMarker }
|
||||
return {
|
||||
result: formattedBuckets,
|
||||
isTruncated: res.isTruncated,
|
||||
nextMarker: res.nextMarker
|
||||
}
|
||||
}
|
||||
const result: IStringKeyMap[] = []
|
||||
let NextMarker: string | undefined
|
||||
@@ -193,7 +209,12 @@ class AliyunApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
let marker
|
||||
@@ -212,13 +233,16 @@ class AliyunApi {
|
||||
}
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
do {
|
||||
res = await client.listV2({
|
||||
res = await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
})
|
||||
}
|
||||
)
|
||||
if (res?.res?.statusCode === 200) {
|
||||
res?.objects?.forEach((item: OSS.ObjectMeta) => {
|
||||
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
@@ -240,7 +264,12 @@ class AliyunApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
let marker
|
||||
@@ -259,14 +288,17 @@ class AliyunApi {
|
||||
}
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
do {
|
||||
res = await client.listV2({
|
||||
res = await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
delimiter: '/',
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
})
|
||||
}
|
||||
)
|
||||
if (res?.res?.statusCode === 200) {
|
||||
res?.prefixes?.forEach((item: string) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
@@ -305,19 +337,28 @@ class AliyunApi {
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com`
|
||||
|
||||
const client = this.getNewCtx(region, bucket)
|
||||
const res = await client.listV2({
|
||||
const res = (await client.listV2(
|
||||
{
|
||||
prefix: slicedPrefix || undefined,
|
||||
delimiter: '/',
|
||||
'max-keys': itemsPerPage.toString(),
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
}) as any
|
||||
}
|
||||
)) as any
|
||||
// prefixes can be null
|
||||
// objects will be [] when no file
|
||||
if (res?.res.statusCode !== 200) {
|
||||
@@ -330,7 +371,9 @@ class AliyunApi {
|
||||
}
|
||||
const fullList = [
|
||||
...(res.prefixes?.map((item: string) => this.formatFolder(item, slicedPrefix, urlPrefix)) || []),
|
||||
...(res.objects?.filter((item: OSS.ObjectMeta) => item.size !== 0).map((item: OSS.ObjectMeta) => this.formatFile(item, slicedPrefix, urlPrefix)) || [])
|
||||
...(res.objects
|
||||
?.filter((item: OSS.ObjectMeta) => item.size !== 0)
|
||||
.map((item: OSS.ObjectMeta) => this.formatFile(item, slicedPrefix, urlPrefix)) || [])
|
||||
]
|
||||
return {
|
||||
fullList,
|
||||
@@ -353,12 +396,9 @@ class AliyunApi {
|
||||
async renameBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, oldKey, newKey } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const copyRes = await client.copy(
|
||||
newKey,
|
||||
oldKey
|
||||
) as any
|
||||
const copyRes = (await client.copy(newKey, oldKey)) as any
|
||||
if (copyRes?.res.statusCode === 200) {
|
||||
const deleteRes = await client.delete(oldKey) as any
|
||||
const deleteRes = (await client.delete(oldKey)) as any
|
||||
return deleteRes?.res.statusCode === 204
|
||||
}
|
||||
return false
|
||||
@@ -376,7 +416,7 @@ class AliyunApi {
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const res = await client.delete(key) as any
|
||||
const res = (await client.delete(key)) as any
|
||||
return res?.res.statusCode === 204
|
||||
}
|
||||
|
||||
@@ -394,14 +434,17 @@ class AliyunApi {
|
||||
Contents: [] as any[]
|
||||
}
|
||||
do {
|
||||
const res = await client.listV2({
|
||||
const res = (await client.listV2(
|
||||
{
|
||||
prefix: key,
|
||||
delimiter: '/',
|
||||
'max-keys': '1000',
|
||||
'continuation-token': marker
|
||||
}, {
|
||||
},
|
||||
{
|
||||
timeout: this.timeOut
|
||||
}) as any
|
||||
}
|
||||
)) as any
|
||||
if (res?.res.statusCode !== 200) return false
|
||||
|
||||
res.prefixes !== null && allFileList.CommonPrefixes.push(...res.prefixes)
|
||||
@@ -423,8 +466,9 @@ class AliyunApi {
|
||||
if (allFileList.Contents.length > 0) {
|
||||
const cycle = Math.ceil(allFileList.Contents.length / 1000)
|
||||
for (let i = 0; i < cycle; i++) {
|
||||
const deleteRes = await client.deleteMulti(
|
||||
allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => item.name)) as any
|
||||
const deleteRes = (await client.deleteMulti(
|
||||
allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => item.name)
|
||||
)) as any
|
||||
if (deleteRes?.res.statusCode !== 200) return false
|
||||
}
|
||||
}
|
||||
@@ -485,10 +529,8 @@ class AliyunApi {
|
||||
targetFileBucket: bucketName,
|
||||
targetFileRegion: region
|
||||
})
|
||||
client.multipartUpload(
|
||||
key,
|
||||
filePath,
|
||||
{
|
||||
client
|
||||
.multipartUpload(key, filePath, {
|
||||
partSize: 1 * 1024 * 1024,
|
||||
mime: getFileMimeType(fileName),
|
||||
progress: (p: number) => {
|
||||
@@ -499,8 +541,8 @@ class AliyunApi {
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
}
|
||||
}
|
||||
).then((res: any) => {
|
||||
})
|
||||
.then((res: any) => {
|
||||
const id = `${bucketName}-${region}-${key}-${filePath}`
|
||||
if (res?.res?.statusCode === 200) {
|
||||
instance.updateUploadTask({
|
||||
@@ -519,8 +561,14 @@ class AliyunApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((err: any) => {
|
||||
this.logger.error(formatError(err, { class: 'AliyunApi', method: 'uploadBucketFile' }))
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
class: 'AliyunApi',
|
||||
method: 'uploadBucketFile'
|
||||
})
|
||||
)
|
||||
const id = `${bucketName}-${region}-${key}-${filePath}`
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -541,7 +589,7 @@ class AliyunApi {
|
||||
async createBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName, region, key } = configMap
|
||||
const client = this.getNewCtx(region, bucketName)
|
||||
const res = await client.put(key, Buffer.from('')) as any
|
||||
const res = (await client.put(key, Buffer.from(''))) as any
|
||||
return res?.res?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -571,20 +619,27 @@ class AliyunApi {
|
||||
const preSignedUrl = client.signatureUrl(key, {
|
||||
expires: 60 * 60 * 48
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error: any) => {
|
||||
this.logger.error(formatError(error, { class: 'AliyunApi', method: 'downloadBucketFile' }))
|
||||
this.logger.error(
|
||||
formatError(error, {
|
||||
class: 'AliyunApi',
|
||||
method: 'downloadBucketFile'
|
||||
})
|
||||
)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
+131
-49
@@ -6,7 +6,14 @@ import path from 'path'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { gotUpload, NewDownloader, getAgent, getOptions, ConcurrencyPromisePool, formatError } from '~/manage/utils/common'
|
||||
import {
|
||||
gotUpload,
|
||||
NewDownloader,
|
||||
getAgent,
|
||||
getOptions,
|
||||
ConcurrencyPromisePool,
|
||||
formatError
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList } from '#/types/enum'
|
||||
@@ -41,7 +48,16 @@ class GithubApi {
|
||||
rawUrl = cdnUrl
|
||||
? placeholders.some(item => cdnUrl.includes(item))
|
||||
? placeholders.reduce((url, ph) => {
|
||||
const value = ph === '{username}' ? this.username : ph === '{repo}' ? repo : ph === '{branch}' ? branch : ph === '{path}' ? key : ''
|
||||
const value =
|
||||
ph === '{username}'
|
||||
? this.username
|
||||
: ph === '{repo}'
|
||||
? repo
|
||||
: ph === '{branch}'
|
||||
? branch
|
||||
: ph === '{path}'
|
||||
? key
|
||||
: ''
|
||||
return url.replaceAll(ph, value)
|
||||
}, cdnUrl)
|
||||
: `${cdnUrl}/${key}`
|
||||
@@ -69,7 +85,16 @@ class GithubApi {
|
||||
rawUrl = cdnUrl
|
||||
? placeholders.some(item => cdnUrl.includes(item))
|
||||
? placeholders.reduce((url, ph) => {
|
||||
const value = ph === '{username}' ? this.username : ph === '{repo}' ? repo : ph === '{branch}' ? branch : ph === '{path}' ? `${slicedPrefix}/${item.path}` : ''
|
||||
const value =
|
||||
ph === '{username}'
|
||||
? this.username
|
||||
: ph === '{repo}'
|
||||
? repo
|
||||
: ph === '{branch}'
|
||||
? branch
|
||||
: ph === '{path}'
|
||||
? `${slicedPrefix}/${item.path}`
|
||||
: ''
|
||||
return url.replaceAll(ph, value)
|
||||
}, cdnUrl)
|
||||
: `${cdnUrl}/${key}`
|
||||
@@ -99,10 +124,18 @@ class GithubApi {
|
||||
let res
|
||||
const result = [] as any[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/user/repos`,
|
||||
getOptions('GET', this.commonHeaders, { page: initPage, per_page: 100 }, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{ page: initPage, per_page: 100 },
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (res.statusCode === 200) {
|
||||
res.body.forEach((item: any) => {
|
||||
result.push({
|
||||
@@ -129,10 +162,18 @@ class GithubApi {
|
||||
let res
|
||||
const result = [] as string[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/branches`,
|
||||
getOptions('GET', this.commonHeaders, { page: initPage, per_page: 100 }, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{ page: initPage, per_page: 100 },
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (res.statusCode === 200) {
|
||||
res.body.forEach((item: any) => result.push(item.name))
|
||||
} else {
|
||||
@@ -167,10 +208,10 @@ class GithubApi {
|
||||
return result
|
||||
}
|
||||
const currentPrefix = treeQueue[0]
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees/${branch}:${treeQueue.shift()}`,
|
||||
getOptions('GET', this.commonHeaders, {}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res && res.statusCode === 200) {
|
||||
const { tree } = res.body
|
||||
tree.forEach((item: any) => {
|
||||
@@ -265,65 +306,98 @@ class GithubApi {
|
||||
async deleteBucketFolder(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { bucketName: repo, githubBranch: branch, key } = configMap
|
||||
// get sha of the branch
|
||||
const refRes = await got(
|
||||
const refRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`,
|
||||
getOptions('GET', this.commonHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (refRes.statusCode !== 200) return false
|
||||
const refSha = refRes.body.object.sha
|
||||
// get sha of the root tree
|
||||
const rootRes = await got(
|
||||
const rootRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/branches/${branch}`,
|
||||
getOptions('GET', undefined, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (rootRes.statusCode !== 200) return false
|
||||
const rootSha = rootRes.body.commit.commit.tree.sha
|
||||
// TODO: if there are more than 10000 files in the folder, it will be truncated
|
||||
// Rare cases, not considered for now
|
||||
// get sha of the folder tree
|
||||
const treeRes = await got(
|
||||
const treeRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees/${branch}:${key.replace(/(^\/+|\/+$)/g, '')}`,
|
||||
getOptions('GET', this.commonHeaders, {
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{
|
||||
recursive: true
|
||||
}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
},
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (treeRes.statusCode !== 200) return false
|
||||
const oldTree = treeRes.body.tree
|
||||
// create a new tree
|
||||
const newTree = oldTree.filter((item: any) => item.type === 'blob')
|
||||
const newTree = oldTree
|
||||
.filter((item: any) => item.type === 'blob')
|
||||
.map((item: any) => ({
|
||||
path: `${key.replace(/(^\/+|\/+$)/g, '')}/${item.path}`,
|
||||
mode: item.mode,
|
||||
type: item.type,
|
||||
sha: null
|
||||
}))
|
||||
const newTreeShaRes = await got(
|
||||
const newTreeShaRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/trees`,
|
||||
getOptions('POST', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'POST',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
base_tree: rootSha,
|
||||
tree: newTree
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (newTreeShaRes.statusCode !== 201) return false
|
||||
const newTreeSha = newTreeShaRes.body.sha
|
||||
// create a new commit
|
||||
const commitRes = await got(
|
||||
const commitRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/commits`,
|
||||
getOptions('POST', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'POST',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
message: 'deleted by PicList',
|
||||
tree: newTreeSha,
|
||||
parents: [refSha]
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
if (commitRes.statusCode !== 201) return false
|
||||
const commitSha = commitRes.body.sha
|
||||
// update the branch
|
||||
const updateRefRes = await got(
|
||||
const updateRefRes = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`,
|
||||
getOptions('PATCH', this.commonHeaders, undefined, 'json', JSON.stringify({
|
||||
getOptions(
|
||||
'PATCH',
|
||||
this.commonHeaders,
|
||||
undefined,
|
||||
'json',
|
||||
JSON.stringify({
|
||||
sha: commitSha
|
||||
}), undefined, this.proxy)
|
||||
) as any
|
||||
}),
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
return updateRefRes.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -341,12 +415,20 @@ class GithubApi {
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName: repo, customUrl: branch, key, rawUrl, githubPrivate: isPrivate } = configMap
|
||||
if (!isPrivate) return rawUrl
|
||||
const res = await got(
|
||||
const res = (await got(
|
||||
`${this.baseUrl}/repos/${this.username}/${repo}/contents/${key}`,
|
||||
getOptions('GET', this.commonHeaders, {
|
||||
getOptions(
|
||||
'GET',
|
||||
this.commonHeaders,
|
||||
{
|
||||
ref: branch
|
||||
}, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
},
|
||||
'json',
|
||||
undefined,
|
||||
undefined,
|
||||
this.proxy
|
||||
)
|
||||
)) as any
|
||||
return res.statusCode === 200 ? res.body.download_url : ''
|
||||
}
|
||||
|
||||
@@ -454,27 +536,27 @@ class GithubApi {
|
||||
} else {
|
||||
downloadUrl = githubUrl
|
||||
}
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(
|
||||
instance,
|
||||
downloadUrl,
|
||||
id,
|
||||
savedFilePath,
|
||||
this.logger,
|
||||
this.proxyStr
|
||||
)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, downloadUrl, id, savedFilePath, this.logger, this.proxyStr).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
this.logger.error(formatError(error, { class: 'GithubApi', method: 'downloadBucketFile' }))
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(
|
||||
formatError(error, {
|
||||
class: 'GithubApi',
|
||||
method: 'downloadBucketFile'
|
||||
})
|
||||
)
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ class ImgurApi {
|
||||
let res
|
||||
const result = [] as any[]
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/albums/${initPage}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (!(res.statusCode === 200 && res.body.success)) {
|
||||
return []
|
||||
}
|
||||
@@ -95,7 +95,10 @@ class ImgurApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketConfig: { Location: albumHash }, cancelToken } = configMap
|
||||
const {
|
||||
bucketConfig: { Location: albumHash },
|
||||
cancelToken
|
||||
} = configMap
|
||||
const cancelTask = [false]
|
||||
ipcMain.on('cancelLoadingFileList', (_: IpcMainEvent, token: string) => {
|
||||
if (token === cancelToken) {
|
||||
@@ -110,10 +113,10 @@ class ImgurApi {
|
||||
finished: false
|
||||
}
|
||||
if (albumHash !== 'unclassified') {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/album/${albumHash}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res.statusCode === 200 && res.body.success) {
|
||||
res.body.data.images.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFile(item))
|
||||
@@ -127,10 +130,10 @@ class ImgurApi {
|
||||
} else {
|
||||
let initPage = 0
|
||||
do {
|
||||
res = await got(
|
||||
res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/images/${initPage}`,
|
||||
getOptions('GET', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
if (res.statusCode === 200 && res.body.success) {
|
||||
res.body.data.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFile(item))
|
||||
@@ -152,10 +155,10 @@ class ImgurApi {
|
||||
|
||||
async deleteBucketFile(configMap: IStringKeyMap): Promise<boolean> {
|
||||
const { DeleteHash: deleteHash } = configMap
|
||||
const res = await got(
|
||||
const res = (await got(
|
||||
`${this.baseUrl}/account/${this.userName}/image/${deleteHash}`,
|
||||
getOptions('DELETE', this.tokenHeaders, undefined, 'json', undefined, undefined, this.proxy)
|
||||
) as any
|
||||
)) as any
|
||||
return res.statusCode === 200 && res.body.success
|
||||
}
|
||||
|
||||
@@ -241,26 +244,21 @@ class ImgurApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(
|
||||
instance,
|
||||
url,
|
||||
id,
|
||||
savedFilePath,
|
||||
this.logger,
|
||||
this.proxyStr
|
||||
)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, url, id, savedFilePath, this.logger, this.proxyStr).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'ImgurApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -21,8 +21,7 @@ class LocalApi {
|
||||
this.isWindows = process.platform === 'win32'
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'LocalApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'LocalApi', method }))
|
||||
|
||||
// windows 系统下将路径转换为 unix 风格
|
||||
transPathToUnix(filePath: string | undefined) {
|
||||
@@ -33,7 +32,10 @@ class LocalApi {
|
||||
transBack(filePath: string | undefined) {
|
||||
if (!filePath) return ''
|
||||
return this.isWindows
|
||||
? filePath.split(path.posix.sep).join(path.sep).replace(/^\\+|\\+$/g, '')
|
||||
? filePath
|
||||
.split(path.posix.sep)
|
||||
.join(path.sep)
|
||||
.replace(/^\\+|\\+$/g, '')
|
||||
: `/${filePath.replace(/^\/+|\/+$/g, '')}`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import axios from 'axios'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import path from 'path'
|
||||
@@ -7,7 +6,13 @@ import qiniu from 'qiniu/index'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import { hmacSha1Base64, getFileMimeType, NewDownloader, formatError, ConcurrencyPromisePool } from '~/manage/utils/common'
|
||||
import {
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
formatError,
|
||||
ConcurrencyPromisePool
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
@@ -79,7 +84,7 @@ class QiniuApi {
|
||||
if (xQiniuHeaders) {
|
||||
const xQiniuHeaderStr = Object.keys(xQiniuHeaders)
|
||||
.sort()
|
||||
.map((key) => `\n${key}:${xQiniuHeaders[key]}`)
|
||||
.map(key => `\n${key}:${xQiniuHeaders[key]}`)
|
||||
.join('')
|
||||
signStr += xQiniuHeaderStr
|
||||
}
|
||||
@@ -252,11 +257,14 @@ class QiniuApi {
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
do {
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err: any, respBody: any, respInfo: any) => {
|
||||
},
|
||||
(err: any, respBody: any, respInfo: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -265,10 +273,13 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res && res.respInfo.statusCode === 200) {
|
||||
res.respBody && res.respBody.items && res.respBody.items.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.items &&
|
||||
res.respBody.items.forEach((item: any) => {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
@@ -308,12 +319,15 @@ class QiniuApi {
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
do {
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
delimiter: '/',
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err: any, respBody: any, respInfo: any) => {
|
||||
},
|
||||
(err: any, respBody: any, respInfo: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -322,13 +336,18 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res && res.respInfo.statusCode === 200) {
|
||||
res.respBody && res.respBody.commonPrefixes && res.respBody.commonPrefixes.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.commonPrefixes &&
|
||||
res.respBody.commonPrefixes.forEach((item: any) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
res.respBody && res.respBody.items && res.respBody.items.forEach((item: any) => {
|
||||
res.respBody &&
|
||||
res.respBody.items &&
|
||||
res.respBody.items.forEach((item: any) => {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
@@ -374,12 +393,15 @@ class QiniuApi {
|
||||
success: false
|
||||
}
|
||||
res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucket, {
|
||||
bucketManager.listPrefix(
|
||||
bucket,
|
||||
{
|
||||
limit: itemsPerPage,
|
||||
prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
marker,
|
||||
delimiter: '/'
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -388,7 +410,8 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
if (res.respBody?.commonPrefixes) {
|
||||
@@ -401,7 +424,7 @@ class QiniuApi {
|
||||
item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
}
|
||||
result.isTruncated = !!(res.respBody?.marker)
|
||||
result.isTruncated = !!res.respBody?.marker
|
||||
result.nextMarker = res.respBody?.marker ? res.respBody.marker : ''
|
||||
result.success = true
|
||||
}
|
||||
@@ -421,7 +444,7 @@ class QiniuApi {
|
||||
const { bucketName, key } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.delete(bucketName, key, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -432,7 +455,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -450,12 +473,15 @@ class QiniuApi {
|
||||
Contents: [] as any[]
|
||||
}
|
||||
do {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(bucketName, {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.listPrefix(
|
||||
bucketName,
|
||||
{
|
||||
prefix: key,
|
||||
marker,
|
||||
limit: 1000
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -464,13 +490,14 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
}
|
||||
)
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode === 200) {
|
||||
if (res.respBody?.items) {
|
||||
allFileList.Contents = allFileList.Contents.concat(res.respBody.items)
|
||||
}
|
||||
isTruncated = !!(res.respBody?.marker)
|
||||
isTruncated = !!res.respBody?.marker
|
||||
marker = res.respBody?.marker ? res.respBody.marker : ''
|
||||
} else {
|
||||
return false
|
||||
@@ -481,7 +508,7 @@ class QiniuApi {
|
||||
const deleteOps = allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => {
|
||||
return qiniu.rs.deleteOp(bucketName, item.key)
|
||||
})
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.batch(deleteOps, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -492,7 +519,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
if (res?.respInfo?.statusCode !== 200) return false
|
||||
}
|
||||
return true
|
||||
@@ -512,10 +539,16 @@ class QiniuApi {
|
||||
const { bucketName, oldKey, newKey } = configMap
|
||||
const config = new qiniu.conf.Config()
|
||||
const bucketManager = new qiniu.rs.BucketManager(this.mac, config)
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
bucketManager.move(bucketName, oldKey, bucketName, newKey, {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
bucketManager.move(
|
||||
bucketName,
|
||||
oldKey,
|
||||
bucketName,
|
||||
newKey,
|
||||
{
|
||||
force: true
|
||||
}, (err, respBody, respInfo) => {
|
||||
},
|
||||
(err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
@@ -524,8 +557,9 @@ class QiniuApi {
|
||||
respInfo
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
}
|
||||
)
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -585,7 +619,7 @@ class QiniuApi {
|
||||
putExtra.version = 'v2'
|
||||
putExtra.partSize = 4 * 1024 * 1024
|
||||
putExtra.progressCallback = (uploadBytes, totalBytes) => {
|
||||
const progress = Math.floor(uploadBytes / totalBytes * 100)
|
||||
const progress = Math.floor((uploadBytes / totalBytes) * 100)
|
||||
instance.updateUploadTask({
|
||||
id: `${bucketName}-${region}-${key}-${filePath}`,
|
||||
progress,
|
||||
@@ -594,7 +628,12 @@ class QiniuApi {
|
||||
}
|
||||
resumeUploader.putFile(uploadToken, key, filePath, putExtra, (respErr, respBody, respInfo) => {
|
||||
if (respErr) {
|
||||
this.logger.error(formatError(respErr, { class: 'Qiniu', method: 'uploadBucketFile' }))
|
||||
this.logger.error(
|
||||
formatError(respErr, {
|
||||
class: 'Qiniu',
|
||||
method: 'uploadBucketFile'
|
||||
})
|
||||
)
|
||||
instance.updateUploadTask({
|
||||
id: `${bucketName}-${region}-${key}-${filePath}`,
|
||||
progress: 0,
|
||||
@@ -636,7 +675,7 @@ class QiniuApi {
|
||||
const uploadToken = putPolicy.uploadToken(this.mac)
|
||||
const FormUploader = new qiniu.form_up.FormUploader()
|
||||
const putExtra = new qiniu.form_up.PutExtra()
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
const res = (await new Promise((resolve, reject) => {
|
||||
FormUploader.put(uploadToken, key, '', putExtra, (err, respBody, respInfo) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
@@ -647,7 +686,7 @@ class QiniuApi {
|
||||
})
|
||||
}
|
||||
})
|
||||
}) as any
|
||||
})) as any
|
||||
return res?.respInfo?.statusCode === 200
|
||||
}
|
||||
|
||||
@@ -673,20 +712,26 @@ class QiniuApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
const preSignedUrl = await this.getPreSignedUrl({ key, expires: 36000, customUrl })
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
const preSignedUrl = await this.getPreSignedUrl({
|
||||
key,
|
||||
expires: 36000,
|
||||
customUrl
|
||||
})
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'QiniuApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -77,7 +77,7 @@ class S3plistApi {
|
||||
|
||||
async getDogeCloudToken() {
|
||||
if (!this.dogeCloudSupport) return
|
||||
const token = await getTempToken(this.accessKeyId, this.secretAccessKey) as DogecloudToken
|
||||
const token = (await getTempToken(this.accessKeyId, this.secretAccessKey)) as DogecloudToken
|
||||
if (Object.keys(token).length === 0) {
|
||||
throw new Error('manage.setting.dogeCloudTokenError')
|
||||
}
|
||||
@@ -115,8 +115,7 @@ class S3plistApi {
|
||||
})
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'S3plistApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'S3plistApi', method }))
|
||||
|
||||
formatFolder(item: CommonPrefix, slicedPrefix: string, urlPrefix: string): any {
|
||||
return {
|
||||
@@ -186,7 +185,7 @@ class S3plistApi {
|
||||
const command = new ListBucketsCommand({})
|
||||
const data = await client.send(command)
|
||||
if (data.$metadata.httpStatusCode === 200) {
|
||||
const bucketList = data.Buckets?.map((item) => item.Name)
|
||||
const bucketList = data.Buckets?.map(item => item.Name)
|
||||
if (bucketList?.includes(BucketName)) {
|
||||
return true
|
||||
}
|
||||
@@ -258,7 +257,7 @@ class S3plistApi {
|
||||
}
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
const result: IStringKeyMap[] = []
|
||||
const endpoint = options.endpoint as string || ''
|
||||
const endpoint = (options.endpoint as string) || ''
|
||||
options.region = endpoint.includes('cloudflarestorage') ? 'auto' : 'us-east-1'
|
||||
try {
|
||||
const client = new S3Client(options)
|
||||
@@ -271,21 +270,26 @@ class S3plistApi {
|
||||
|
||||
if (data.Buckets) {
|
||||
if (endpoint.includes('cloudflarestorage')) {
|
||||
result.push(...data.Buckets.map(bucket => ({
|
||||
result.push(
|
||||
...data.Buckets.map(bucket => ({
|
||||
Name: bucket.Name,
|
||||
CreationDate: bucket.CreationDate,
|
||||
Location: 'auto'
|
||||
})))
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
for (const bucket of data.Buckets) {
|
||||
const bucketName = bucket.Name
|
||||
const bucketConfig = await client.send(new GetBucketLocationCommand({
|
||||
const bucketConfig = await client.send(
|
||||
new GetBucketLocationCommand({
|
||||
Bucket: bucketName
|
||||
}))
|
||||
})
|
||||
)
|
||||
result.push({
|
||||
Name: bucketName,
|
||||
CreationDate: bucket.CreationDate,
|
||||
Location: bucketConfig.$metadata.httpStatusCode === 200
|
||||
Location:
|
||||
bucketConfig.$metadata.httpStatusCode === 200
|
||||
? bucketConfig.LocationConstraint?.toLowerCase() || 'us-east-1'
|
||||
: 'us-east-1'
|
||||
})
|
||||
@@ -303,7 +307,12 @@ class S3plistApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
let marker
|
||||
@@ -333,7 +342,8 @@ class S3plistApi {
|
||||
})
|
||||
res = await client.send(command)
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.Contents && res.Contents.forEach((item: _Object) => {
|
||||
res.Contents &&
|
||||
res.Contents.forEach((item: _Object) => {
|
||||
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
@@ -361,7 +371,12 @@ class S3plistApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
let marker
|
||||
@@ -393,10 +408,12 @@ class S3plistApi {
|
||||
})
|
||||
res = await client.send(command)
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.CommonPrefixes && res.CommonPrefixes.forEach((item: CommonPrefix) => {
|
||||
res.CommonPrefixes &&
|
||||
res.CommonPrefixes.forEach((item: CommonPrefix) => {
|
||||
result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
res.Contents && res.Contents.forEach((item: _Object) => {
|
||||
res.Contents &&
|
||||
res.Contents.forEach((item: _Object) => {
|
||||
result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
})
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
@@ -423,7 +440,13 @@ class S3plistApi {
|
||||
}
|
||||
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com`
|
||||
const result = {
|
||||
@@ -434,7 +457,10 @@ class S3plistApi {
|
||||
}
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new ListObjectsV2Command({
|
||||
Bucket: bucket,
|
||||
@@ -474,7 +500,10 @@ class S3plistApi {
|
||||
let result = false
|
||||
try {
|
||||
await this.getDogeCloudToken()
|
||||
const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig
|
||||
const options = Object.assign(
|
||||
{},
|
||||
{ ...this.baseOptions, region: String(region) || 'us-east-1' }
|
||||
) as S3ClientConfig
|
||||
const client = new S3Client(options)
|
||||
const command = new CopyObjectCommand({
|
||||
Bucket: bucketName,
|
||||
@@ -562,7 +591,7 @@ class S3plistApi {
|
||||
Delimiter: '/',
|
||||
MaxKeys: 1000
|
||||
})
|
||||
res = await client.send(command) as ListObjectsV2CommandOutput
|
||||
res = (await client.send(command)) as ListObjectsV2CommandOutput
|
||||
if (res.$metadata.httpStatusCode === 200) {
|
||||
res.CommonPrefixes && allFileList.CommonPrefixes.push(...res.CommonPrefixes)
|
||||
res.Contents && allFileList.Contents.push(...res.Contents)
|
||||
@@ -595,7 +624,7 @@ class S3plistApi {
|
||||
const deleteCommand = new DeleteObjectsCommand({
|
||||
Bucket: bucketName,
|
||||
Delete: {
|
||||
Objects: deleteList.map((item) => {
|
||||
Objects: deleteList.map(item => {
|
||||
return {
|
||||
Key: item.Key
|
||||
}
|
||||
@@ -635,12 +664,16 @@ class S3plistApi {
|
||||
const options = Object.assign({}, this.baseOptions) as S3ClientConfig
|
||||
options.region = String(region) || 'us-east-1'
|
||||
const client = new S3Client(options)
|
||||
const signedUrl = await getSignedUrl(client, new GetObjectCommand({
|
||||
const signedUrl = await getSignedUrl(
|
||||
client,
|
||||
new GetObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: key
|
||||
}), {
|
||||
}),
|
||||
{
|
||||
expiresIn: expires || 3600
|
||||
})
|
||||
}
|
||||
)
|
||||
return signedUrl
|
||||
} catch (error) {
|
||||
this.logParam(error, 'getPreSignedUrl')
|
||||
@@ -693,7 +726,15 @@ class S3plistApi {
|
||||
fileArray.forEach((item: any) => {
|
||||
item.key.startsWith('/') && (item.key = item.key.slice(1))
|
||||
})
|
||||
const allowedAcl = ['private', 'public-read', 'public-read-write', 'aws-exec-read', 'authenticated-read', 'bucket-owner-read', 'bucket-owner-full-control']
|
||||
const allowedAcl = [
|
||||
'private',
|
||||
'public-read',
|
||||
'public-read-write',
|
||||
'aws-exec-read',
|
||||
'authenticated-read',
|
||||
'bucket-owner-read',
|
||||
'bucket-owner-full-control'
|
||||
]
|
||||
for (const item of fileArray) {
|
||||
const { bucketName, region, key, filePath, fileName, aclForUpload } = item
|
||||
const id = `${bucketName}-${String(region)}-${key}-${filePath}`
|
||||
@@ -743,11 +784,13 @@ class S3plistApi {
|
||||
parallelUploads3.on('httpUploadProgress', (progress: Progress) => {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: progress.loaded && progress.total ? Math.floor(progress.loaded / progress.total * 100) : 0,
|
||||
progress: progress.loaded && progress.total ? Math.floor((progress.loaded / progress.total) * 100) : 0,
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
})
|
||||
parallelUploads3.done().then((data) => {
|
||||
parallelUploads3
|
||||
.done()
|
||||
.then(data => {
|
||||
if (data.$metadata.httpStatusCode === 200) {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -763,7 +806,8 @@ class S3plistApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch(error => {
|
||||
this.logParam(error, 'uploadBucketFile')
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -806,19 +850,21 @@ class S3plistApi {
|
||||
expires: 36000,
|
||||
customUrl
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxy)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxy).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logParam(error, 'downloadBucketFile')
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -76,8 +76,7 @@ class SftpApi {
|
||||
}
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'SftpApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'SftpApi', method }))
|
||||
|
||||
transFormPermission = (permissionsStr: string) => {
|
||||
const permissions = permissionsStr.length === 10 ? permissionsStr.slice(1) : permissionsStr
|
||||
@@ -250,7 +249,8 @@ class SftpApi {
|
||||
if (formatedLSRes.length) {
|
||||
formatedLSRes.forEach((item: listDirResult) => {
|
||||
const relativePath = path.relative(baseDir, item.key.startsWith('/') ? item.key : `/${item.key}`)
|
||||
const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
const relative =
|
||||
webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
if (item.isDir) {
|
||||
result.fullList.push(this.formatFolder(item, webPath ? relative : urlPrefix, !!webPath))
|
||||
} else {
|
||||
|
||||
@@ -71,9 +71,7 @@ class SmmsApi {
|
||||
finished: false
|
||||
}
|
||||
do {
|
||||
res = await this.axiosInstance(
|
||||
'/upload_history',
|
||||
{
|
||||
res = await this.axiosInstance('/upload_history', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
@@ -131,9 +129,7 @@ class SmmsApi {
|
||||
nextMarker: '',
|
||||
success: false
|
||||
}
|
||||
const res = await this.axiosInstance(
|
||||
'/upload_history',
|
||||
{
|
||||
const res = await this.axiosInstance('/upload_history', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
@@ -141,8 +137,7 @@ class SmmsApi {
|
||||
params: {
|
||||
page: currentPage
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
if (res?.status !== 200 || !res?.data?.success) return result
|
||||
|
||||
if (res.data.Count === 0) return { ...result, success: true }
|
||||
@@ -167,16 +162,13 @@ class SmmsApi {
|
||||
* }
|
||||
*/
|
||||
async deleteBucketFile({ DeleteHash }: IStringKeyMap): Promise<boolean> {
|
||||
const res = await this.axiosInstance(
|
||||
`/delete/${DeleteHash}`,
|
||||
{
|
||||
const res = await this.axiosInstance(`/delete/${DeleteHash}`, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
hash: DeleteHash,
|
||||
format: 'json'
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
return res?.status === 200 && res?.data?.success
|
||||
}
|
||||
|
||||
@@ -239,19 +231,21 @@ class SmmsApi {
|
||||
sourceFileName: fileName,
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'SmmsApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import COS from 'cos-nodejs-sdk-v5'
|
||||
import { ipcMain, IpcMainEvent } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
@@ -99,7 +98,13 @@ class TcyunApi {
|
||||
|
||||
async getBucketListRecursively(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1, prefix.length)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const cancelTask = [false]
|
||||
@@ -125,8 +130,11 @@ class TcyunApi {
|
||||
Marker: marker
|
||||
})
|
||||
if (res?.statusCode === 200) {
|
||||
result.fullList.push(...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix)))
|
||||
result.fullList.push(
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
)
|
||||
window.webContents.send(refreshDownloadFileTransferList, result)
|
||||
} else {
|
||||
result.finished = true
|
||||
@@ -144,7 +152,13 @@ class TcyunApi {
|
||||
|
||||
async getBucketListBackstage(configMap: IStringKeyMap): Promise<any> {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
cancelToken
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1, prefix.length)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const cancelTask = [false]
|
||||
@@ -173,8 +187,9 @@ class TcyunApi {
|
||||
if (res?.statusCode === 200) {
|
||||
result.fullList.push(
|
||||
...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)),
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
)
|
||||
window.webContents.send('refreshFileTransferList', result)
|
||||
} else {
|
||||
@@ -207,17 +222,24 @@ class TcyunApi {
|
||||
* }
|
||||
*/
|
||||
async getBucketFileList(configMap: IStringKeyMap): Promise<any> {
|
||||
const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, marker, itemsPerPage } = configMap
|
||||
const {
|
||||
bucketName: bucket,
|
||||
bucketConfig: { Location: region },
|
||||
prefix,
|
||||
customUrl,
|
||||
marker,
|
||||
itemsPerPage
|
||||
} = configMap
|
||||
const slicedPrefix = prefix.slice(1)
|
||||
const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com`
|
||||
const res = await this.ctx.getBucket({
|
||||
const res = (await this.ctx.getBucket({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Prefix: slicedPrefix === '' ? undefined : slicedPrefix,
|
||||
Delimiter: '/',
|
||||
Marker: marker,
|
||||
MaxKeys: itemsPerPage
|
||||
}) as COS.GetBucketResult
|
||||
})) as COS.GetBucketResult
|
||||
if (res?.statusCode !== 200) {
|
||||
return {
|
||||
fullList: [],
|
||||
@@ -229,8 +251,9 @@ class TcyunApi {
|
||||
const result = {
|
||||
fullList: [
|
||||
...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)),
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0)
|
||||
.map(item => this.formatFile(item, slicedPrefix, urlPrefix))
|
||||
...res.Contents.filter(item => parseInt(item.Size) !== 0).map(item =>
|
||||
this.formatFile(item, slicedPrefix, urlPrefix)
|
||||
)
|
||||
],
|
||||
isTruncated: res.IsTruncated === 'true',
|
||||
nextMarker: res.NextMarker || '',
|
||||
@@ -317,7 +340,14 @@ class TcyunApi {
|
||||
marker = res.NextMarker
|
||||
} while (res.IsTruncated === 'true')
|
||||
for (const item of allFileList.CommonPrefixes) {
|
||||
if (!(await this.deleteBucketFolder({ bucketName, region, key: item.Prefix }))) return false
|
||||
if (
|
||||
!(await this.deleteBucketFolder({
|
||||
bucketName,
|
||||
region,
|
||||
key: item.Prefix
|
||||
}))
|
||||
)
|
||||
return false
|
||||
}
|
||||
const cycles = Math.ceil(allFileList.Contents.length / 1000)
|
||||
for (let i = 0; i < cycles; i++) {
|
||||
@@ -344,14 +374,16 @@ class TcyunApi {
|
||||
*/
|
||||
async getPreSignedUrl(configMap: IStringKeyMap): Promise<string> {
|
||||
const { bucketName, region, key, expires, customUrl } = configMap
|
||||
const res = this.ctx.getObjectUrl({
|
||||
const res = this.ctx.getObjectUrl(
|
||||
{
|
||||
Bucket: bucketName,
|
||||
Region: region,
|
||||
Key: key,
|
||||
Expires: expires,
|
||||
Sign: true
|
||||
}, () => {
|
||||
})
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
return customUrl ? `${customUrl.replace(/\/+$/, '')}/${key}${res.slice(res.indexOf('?'))}` : res
|
||||
}
|
||||
|
||||
@@ -412,7 +444,12 @@ class TcyunApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
} else {
|
||||
this.logger.error(formatError(err, { method: 'uploadBucketFile', class: 'TcyunApi' }))
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
method: 'uploadBucketFile',
|
||||
class: 'TcyunApi'
|
||||
})
|
||||
)
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: 0,
|
||||
@@ -472,7 +509,8 @@ class TcyunApi {
|
||||
targetFilePath: path.join(downloadPath, fileName)
|
||||
})
|
||||
fs.ensureDirSync(path.dirname(path.join(downloadPath, fileName)))
|
||||
this.ctx.downloadFile({
|
||||
this.ctx
|
||||
.downloadFile({
|
||||
Bucket: bucketName,
|
||||
Region: region,
|
||||
Key: key,
|
||||
@@ -486,7 +524,8 @@ class TcyunApi {
|
||||
status: downloadTaskSpecialStatus.downloading
|
||||
})
|
||||
}
|
||||
}).then((res: any) => {
|
||||
})
|
||||
.then((res: any) => {
|
||||
instance.updateDownloadTask({
|
||||
id,
|
||||
progress: res && res.statusCode === 200 ? 100 : 0,
|
||||
@@ -494,8 +533,14 @@ class TcyunApi {
|
||||
response: typeof res === 'object' ? JSON.stringify(res) : String(res),
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}).catch((err: any) => {
|
||||
this.logger.error(formatError(err, { method: 'downloadBucketFile', class: 'TcyunApi' }))
|
||||
})
|
||||
.catch((err: any) => {
|
||||
this.logger.error(
|
||||
formatError(err, {
|
||||
method: 'downloadBucketFile',
|
||||
class: 'TcyunApi'
|
||||
})
|
||||
)
|
||||
instance.updateDownloadTask({
|
||||
id,
|
||||
progress: 0,
|
||||
|
||||
@@ -7,7 +7,15 @@ import Upyun from 'upyun'
|
||||
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { md5, hmacSha1Base64, getFileMimeType, NewDownloader, gotUpload, ConcurrencyPromisePool, formatError } from '~/manage/utils/common'
|
||||
import {
|
||||
md5,
|
||||
hmacSha1Base64,
|
||||
getFileMimeType,
|
||||
NewDownloader,
|
||||
gotUpload,
|
||||
ConcurrencyPromisePool,
|
||||
formatError
|
||||
} from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
|
||||
@@ -26,7 +34,14 @@ class UpyunApi {
|
||||
stopMarker = 'g2gCZAAEbmV4dGQAA2VvZg'
|
||||
logger: ManageLogger
|
||||
|
||||
constructor (bucket: string, operator: string, password: string, logger: ManageLogger, antiLeechToken?: string, expireTime?: number) {
|
||||
constructor(
|
||||
bucket: string,
|
||||
operator: string,
|
||||
password: string,
|
||||
logger: ManageLogger,
|
||||
antiLeechToken?: string,
|
||||
expireTime?: number
|
||||
) {
|
||||
this.ser = new Upyun.Service(bucket, operator, password)
|
||||
this.cli = new Upyun.Client(this.ser)
|
||||
this.bucket = bucket
|
||||
@@ -87,13 +102,7 @@ class UpyunApi {
|
||||
}
|
||||
}
|
||||
|
||||
authorization (
|
||||
method: string,
|
||||
uri: string,
|
||||
contentMd5: string,
|
||||
operator: string,
|
||||
password: string
|
||||
) {
|
||||
authorization(method: string, uri: string, contentMd5: string, operator: string, password: string) {
|
||||
return `UPYUN ${operator}:${hmacSha1Base64(
|
||||
md5(password, 'hex'),
|
||||
`${method.toUpperCase()}&${encodeURI(uri)}&${new Date().toUTCString()}${contentMd5 ? `&${contentMd5}` : ''}`
|
||||
@@ -313,11 +322,13 @@ class UpyunApi {
|
||||
})
|
||||
if (res) {
|
||||
res.files.forEach((item: any) => {
|
||||
item.type === 'N' && allFileList.Contents.push({
|
||||
item.type === 'N' &&
|
||||
allFileList.Contents.push({
|
||||
...item,
|
||||
key: `${key}${item.name}`
|
||||
})
|
||||
item.type === 'F' && allFileList.CommonPrefixes.push({
|
||||
item.type === 'F' &&
|
||||
allFileList.CommonPrefixes.push({
|
||||
...item,
|
||||
key: `${key}${item.name}/`
|
||||
})
|
||||
@@ -445,19 +456,21 @@ class UpyunApi {
|
||||
targetFilePath: savedFilePath
|
||||
})
|
||||
const preSignedUrl = `${customUrl}/${key}`
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger).then((res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logger.error(formatError(error, { class: 'UpyunApi', method: 'downloadBucketFile' }))
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -29,7 +29,15 @@ class WebdavplistApi {
|
||||
agent: https.Agent | http.Agent
|
||||
ctx: WebDAVClient
|
||||
|
||||
constructor (endpoint: string, username: string, password: string, sslEnabled: boolean, proxy: string | undefined, authType: 'basic' | 'digest' | undefined, logger: ManageLogger) {
|
||||
constructor(
|
||||
endpoint: string,
|
||||
username: string,
|
||||
password: string,
|
||||
sslEnabled: boolean,
|
||||
proxy: string | undefined,
|
||||
authType: 'basic' | 'digest' | undefined,
|
||||
logger: ManageLogger
|
||||
) {
|
||||
this.endpoint = formatEndpoint(endpoint, sslEnabled)
|
||||
this.username = username
|
||||
this.password = password
|
||||
@@ -50,14 +58,10 @@ class WebdavplistApi {
|
||||
if (this.authType === 'digest') {
|
||||
options.authType = AuthType.Digest
|
||||
}
|
||||
this.ctx = createClient(
|
||||
this.endpoint,
|
||||
options
|
||||
)
|
||||
this.ctx = createClient(this.endpoint, options)
|
||||
}
|
||||
|
||||
logParam = (error:any, method: string) =>
|
||||
this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
|
||||
logParam = (error: any, method: string) => this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
|
||||
|
||||
formatFolder(item: FileStat, urlPrefix: string, isWebPath = false) {
|
||||
const key = item.filename.replace(/^\/+/, '')
|
||||
@@ -166,7 +170,8 @@ class WebdavplistApi {
|
||||
if (res.data?.length) {
|
||||
res.data.forEach((item: FileStat) => {
|
||||
const relativePath = path.relative(baseDir, item.filename)
|
||||
const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
const relative =
|
||||
webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')
|
||||
if (item.type === 'directory') {
|
||||
result.fullList.push(this.formatFolder(item, webPath ? relative : urlPrefix, !!webPath))
|
||||
} else {
|
||||
@@ -261,10 +266,8 @@ class WebdavplistApi {
|
||||
targetFileRegion: region,
|
||||
noProgress: true
|
||||
})
|
||||
this.ctx.putFileContents(
|
||||
key,
|
||||
this.authType === 'digest' ? fs.readFileSync(filePath) : fs.createReadStream(filePath),
|
||||
{
|
||||
this.ctx
|
||||
.putFileContents(key, this.authType === 'digest' ? fs.readFileSync(filePath) : fs.createReadStream(filePath), {
|
||||
overwrite: true,
|
||||
onUploadProgress: (progressEvent: ProgressEvent) => {
|
||||
instance.updateUploadTask({
|
||||
@@ -273,8 +276,8 @@ class WebdavplistApi {
|
||||
status: uploadTaskSpecialStatus.uploading
|
||||
})
|
||||
}
|
||||
}
|
||||
).then((res: boolean) => {
|
||||
})
|
||||
.then((res: boolean) => {
|
||||
if (res) {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -290,7 +293,8 @@ class WebdavplistApi {
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
})
|
||||
.catch((error: any) => {
|
||||
this.logParam(error, 'uploadBucketFile')
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -345,25 +349,35 @@ class WebdavplistApi {
|
||||
Authorization: `Basic ${base64Str}`
|
||||
}
|
||||
} else if (this.authType === 'digest') {
|
||||
const authHeader = await getAuthHeader('GET', this.endpoint, `/${key.replace(/^\/+/, '')}`, this.username, this.password)
|
||||
const authHeader = await getAuthHeader(
|
||||
'GET',
|
||||
this.endpoint,
|
||||
`/${key.replace(/^\/+/, '')}`,
|
||||
this.username,
|
||||
this.password
|
||||
)
|
||||
headers = {
|
||||
Authorization: authHeader
|
||||
}
|
||||
preSignedUrl = `${this.endpoint}/${key.replace(/^\/+/, '')}`
|
||||
}
|
||||
promises.push(() => new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxyStr, headers)
|
||||
.then((res: boolean) => {
|
||||
promises.push(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxyStr, headers).then(
|
||||
(res: boolean) => {
|
||||
if (res) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}))
|
||||
)
|
||||
}
|
||||
const pool = new ConcurrencyPromisePool(maxDownloadFileCount)
|
||||
pool.all(promises).catch((error) => {
|
||||
pool.all(promises).catch(error => {
|
||||
this.logParam(error, 'downloadBucketFile')
|
||||
})
|
||||
return true
|
||||
|
||||
@@ -42,9 +42,13 @@ function manageDbChecker () {
|
||||
fs.unlinkSync(manageConfigFilePath)
|
||||
if (fs.existsSync(manageConfigFileBackupPath)) {
|
||||
try {
|
||||
configFile = fs.readFileSync(manageConfigFileBackupPath, { encoding: 'utf-8' })
|
||||
configFile = fs.readFileSync(manageConfigFileBackupPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
JSON.parse(configFile)
|
||||
writeFile.sync(manageConfigFilePath, configFile, { encoding: 'utf-8' })
|
||||
writeFile.sync(manageConfigFilePath, configFile, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const stats = fs.statSync(manageConfigFileBackupPath)
|
||||
optionsTpl.body = `${errorMsg.brokenButBackup}\n${T('TIPS_PICGO_BACKUP_FILE_VERSION', {
|
||||
v: dayjs(stats.mtime).format('YYYY-MM-DD HH:mm:ss')
|
||||
@@ -61,7 +65,9 @@ function manageDbChecker () {
|
||||
global.notificationList.push(optionsTpl)
|
||||
return
|
||||
}
|
||||
writeFile.sync(manageConfigFileBackupPath, configFile, { encoding: 'utf-8' })
|
||||
writeFile.sync(manageConfigFileBackupPath, configFile, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +86,9 @@ function managePathChecker (): string {
|
||||
return _configFilePath
|
||||
}
|
||||
try {
|
||||
const configString = fs.readFileSync(defaultManageConfigPath, { encoding: 'utf-8' })
|
||||
const configString = fs.readFileSync(defaultManageConfigPath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
const config = JSON.parse(configString)
|
||||
const userConfigPath: string = config.configPath || ''
|
||||
if (userConfigPath) {
|
||||
@@ -111,8 +119,4 @@ function managePathDir () {
|
||||
return path.dirname(managePathChecker())
|
||||
}
|
||||
|
||||
export {
|
||||
managePathChecker,
|
||||
managePathDir,
|
||||
manageDbChecker
|
||||
}
|
||||
export { managePathChecker, managePathDir, manageDbChecker }
|
||||
|
||||
@@ -104,11 +104,21 @@ class UpDownTaskQueue {
|
||||
}
|
||||
|
||||
removeUploadedTask() {
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = UpDownTaskQueue.getInstance().uploadTaskQueue.filter(item => item.status !== uploadTaskSpecialStatus.uploaded && item.status !== commonTaskStatus.canceled && item.status !== commonTaskStatus.failed)
|
||||
UpDownTaskQueue.getInstance().uploadTaskQueue = UpDownTaskQueue.getInstance().uploadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== uploadTaskSpecialStatus.uploaded &&
|
||||
item.status !== commonTaskStatus.canceled &&
|
||||
item.status !== commonTaskStatus.failed
|
||||
)
|
||||
}
|
||||
|
||||
removeDownloadedTask() {
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = UpDownTaskQueue.getInstance().downloadTaskQueue.filter(item => item.status !== downloadTaskSpecialStatus.downloaded && item.status !== commonTaskStatus.canceled && item.status !== commonTaskStatus.failed)
|
||||
UpDownTaskQueue.getInstance().downloadTaskQueue = UpDownTaskQueue.getInstance().downloadTaskQueue.filter(
|
||||
item =>
|
||||
item.status !== downloadTaskSpecialStatus.downloaded &&
|
||||
item.status !== commonTaskStatus.canceled &&
|
||||
item.status !== commonTaskStatus.failed
|
||||
)
|
||||
}
|
||||
|
||||
clearDownloadTaskQueue() {
|
||||
@@ -123,10 +133,13 @@ class UpDownTaskQueue {
|
||||
persist() {
|
||||
try {
|
||||
this.checkPersistPath()
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
@@ -146,18 +159,24 @@ class UpDownTaskQueue {
|
||||
|
||||
private checkPersistPath() {
|
||||
if (!fs.existsSync(this.persistPath)) {
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
try {
|
||||
JSON.parse(fs.readFileSync(this.persistPath, { encoding: 'utf-8' }))
|
||||
} catch (e) {
|
||||
fs.writeFileSync(this.persistPath, JSON.stringify({
|
||||
fs.writeFileSync(
|
||||
this.persistPath,
|
||||
JSON.stringify({
|
||||
uploadTaskQueue: this.uploadTaskQueue,
|
||||
downloadTaskQueue: this.downloadTaskQueue
|
||||
}))
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export const PICLIST_MANAGE_GET_CONFIG = 'PICLIST_MANAGE_GET_CONFIG'
|
||||
export const PICLIST_MANAGE_REMOVE_CONFIG = 'PICLIST_MANAGE_REMOVE_CONFIG'
|
||||
export const PICLIST_MANAGE_SAVE_CONFIG = 'PICLIST_MANAGE_SAVE_CONFIG'
|
||||
@@ -1,155 +0,0 @@
|
||||
import { ipcMain, IpcMainInvokeEvent, dialog, app, shell } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import manageCoreIPC from '~/manage/events/manageCoreIPC'
|
||||
import { ManageApi } from '~/manage/manageApi'
|
||||
import { downloadFileFromUrl } from '~/manage/utils/common'
|
||||
|
||||
import { selectDownloadFolder } from '#/utils/static'
|
||||
|
||||
export const manageIpcList = {
|
||||
listen () {
|
||||
manageCoreIPC.listen()
|
||||
|
||||
ipcMain.handle('getBucketList', async (_: IpcMainInvokeEvent, currentPicBed: string) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getBucketList()
|
||||
})
|
||||
|
||||
ipcMain.handle('createBucket', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.createBucket(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('getBucketFileList', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getBucketFileList(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('getBucketDomain', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
const result = await manage.getBucketDomain(param)
|
||||
return result
|
||||
})
|
||||
|
||||
ipcMain.handle('setBucketAclPolicy', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.setBucketAclPolicy(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('renameBucketFile', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.renameBucketFile(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('deleteBucketFile', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.deleteBucketFile(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('deleteBucketFolder', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.deleteBucketFolder(param)
|
||||
})
|
||||
|
||||
ipcMain.on('getBucketListBackstage', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getBucketListBackstage(param)
|
||||
})
|
||||
|
||||
ipcMain.on('getBucketListRecursively', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getBucketListRecursively(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('convertPathToBase64', async (_: IpcMainInvokeEvent, filePath: string) => {
|
||||
const res = fs.readFileSync(filePath, 'base64')
|
||||
return res
|
||||
})
|
||||
|
||||
ipcMain.handle('openFileSelectDialog', async () => {
|
||||
const res = await dialog.showOpenDialog({
|
||||
properties: ['openFile', 'multiSelections']
|
||||
})
|
||||
if (res.canceled) {
|
||||
return []
|
||||
} else {
|
||||
return res.filePaths
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('getPreSignedUrl', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.getPreSignedUrl(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('getUploadTaskList', async () => {
|
||||
return UpDownTaskQueue.getInstance().getAllUploadTask()
|
||||
})
|
||||
|
||||
ipcMain.handle('getDownloadTaskList', async () => {
|
||||
return UpDownTaskQueue.getInstance().getAllDownloadTask()
|
||||
})
|
||||
|
||||
ipcMain.on('uploadBucketFile', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.uploadBucketFile(param)
|
||||
})
|
||||
|
||||
ipcMain.on('downloadBucketFile', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.downloadBucketFile(param)
|
||||
})
|
||||
|
||||
ipcMain.handle('createBucketFolder', async (_: IpcMainInvokeEvent, currentPicBed: string, param: IStringKeyMap) => {
|
||||
const manage = new ManageApi(currentPicBed)
|
||||
return manage.createBucketFolder(param)
|
||||
})
|
||||
|
||||
ipcMain.on('deleteUploadedTask', async () => {
|
||||
UpDownTaskQueue.getInstance().removeUploadedTask()
|
||||
})
|
||||
|
||||
ipcMain.on('deleteAllUploadedTask', async () => {
|
||||
UpDownTaskQueue.getInstance().clearUploadTaskQueue()
|
||||
})
|
||||
|
||||
ipcMain.on('deleteDownloadedTask', async () => {
|
||||
UpDownTaskQueue.getInstance().removeDownloadedTask()
|
||||
})
|
||||
|
||||
ipcMain.on('deleteAllDownloadedTask', async () => {
|
||||
UpDownTaskQueue.getInstance().clearDownloadTaskQueue()
|
||||
})
|
||||
|
||||
ipcMain.handle(selectDownloadFolder, async () => {
|
||||
const res = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
return res.filePaths[0]
|
||||
})
|
||||
|
||||
ipcMain.handle('getDefaultDownloadFolder', async () => {
|
||||
return app.getPath('downloads')
|
||||
})
|
||||
|
||||
ipcMain.on('OpenDownloadedFolder', async (_: IpcMainInvokeEvent, path: string | undefined) => {
|
||||
if (path) {
|
||||
shell.showItemInFolder(path)
|
||||
} else {
|
||||
shell.openPath(app.getPath('downloads'))
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('OpenLocalFile', async (_: IpcMainInvokeEvent, fullPath: string) => {
|
||||
fs.existsSync(fullPath) ? shell.showItemInFolder(fullPath) : shell.openPath(path.dirname(fullPath))
|
||||
})
|
||||
|
||||
ipcMain.handle('downloadFileFromUrl', async (_: IpcMainInvokeEvent, urls: string[]) => {
|
||||
const res = await downloadFileFromUrl(urls)
|
||||
return res
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import {
|
||||
IpcMainEvent,
|
||||
IpcMainInvokeEvent,
|
||||
ipcMain
|
||||
} from 'electron'
|
||||
|
||||
import getManageApi from '~/manage/Main'
|
||||
import { PICLIST_MANAGE_GET_CONFIG, PICLIST_MANAGE_SAVE_CONFIG, PICLIST_MANAGE_REMOVE_CONFIG } from '~/manage/events/constants'
|
||||
|
||||
const manageApi = getManageApi()
|
||||
|
||||
const handleManageGetConfig = () => {
|
||||
ipcMain.handle(PICLIST_MANAGE_GET_CONFIG, (_: IpcMainInvokeEvent, key: string | undefined) => {
|
||||
return manageApi.getConfig(key)
|
||||
})
|
||||
}
|
||||
|
||||
const handleManageSaveConfig = () => {
|
||||
ipcMain.on(PICLIST_MANAGE_SAVE_CONFIG, (_: IpcMainEvent, data: any) => {
|
||||
manageApi.saveConfig(data)
|
||||
})
|
||||
}
|
||||
|
||||
const handleManageRemoveConfig = () => {
|
||||
ipcMain.on(PICLIST_MANAGE_REMOVE_CONFIG, (_: IpcMainEvent, key: string, propName: string) => {
|
||||
manageApi.removeConfig(key, propName)
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
listen () {
|
||||
handleManageGetConfig()
|
||||
handleManageSaveConfig()
|
||||
handleManageRemoveConfig()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { ipcMain } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import fs from 'fs-extra'
|
||||
@@ -15,12 +14,7 @@ import { isInputConfigValid, formatError } from '~/manage/utils/common'
|
||||
import { ManageLogger } from '~/manage/utils/logger'
|
||||
|
||||
import { IWindowList } from '#/types/enum'
|
||||
import {
|
||||
IManageApiType,
|
||||
IManageConfigType,
|
||||
IManageError,
|
||||
IPicBedMangeConfig
|
||||
} from '#/types/manage'
|
||||
import { IManageApiType, IManageConfigType, IManageError, IPicBedMangeConfig } from '#/types/manage'
|
||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||
|
||||
export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
@@ -58,11 +52,25 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
const name = this.currentPicBedConfig.picBedName
|
||||
switch (name) {
|
||||
case 'aliyun':
|
||||
return new API.AliyunApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.accessKeySecret, this.logger)
|
||||
return new API.AliyunApi(
|
||||
this.currentPicBedConfig.accessKeyId,
|
||||
this.currentPicBedConfig.accessKeySecret,
|
||||
this.logger
|
||||
)
|
||||
case 'github':
|
||||
return new API.GithubApi(this.currentPicBedConfig.token, this.currentPicBedConfig.githubUsername, this.currentPicBedConfig.proxy, this.logger)
|
||||
return new API.GithubApi(
|
||||
this.currentPicBedConfig.token,
|
||||
this.currentPicBedConfig.githubUsername,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger
|
||||
)
|
||||
case 'imgur':
|
||||
return new API.ImgurApi(this.currentPicBedConfig.imgurUserName, this.currentPicBedConfig.accessToken, this.currentPicBedConfig.proxy, this.logger)
|
||||
return new API.ImgurApi(
|
||||
this.currentPicBedConfig.imgurUserName,
|
||||
this.currentPicBedConfig.accessToken,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger
|
||||
)
|
||||
case 'local':
|
||||
return new API.LocalApi(this.logger)
|
||||
case 'qiniu':
|
||||
@@ -70,15 +78,50 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
case 'smms':
|
||||
return new API.SmmsApi(this.currentPicBedConfig.token, this.logger)
|
||||
case 's3plist':
|
||||
return new API.S3plistApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.secretAccessKey, this.currentPicBedConfig.endpoint, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.s3ForcePathStyle, this.currentPicBedConfig.proxy, this.logger, this.currentPicBedConfig.dogeCloudSupport || false, this.currentPicBedConfig.bucketName || '')
|
||||
return new API.S3plistApi(
|
||||
this.currentPicBedConfig.accessKeyId,
|
||||
this.currentPicBedConfig.secretAccessKey,
|
||||
this.currentPicBedConfig.endpoint,
|
||||
this.currentPicBedConfig.sslEnabled,
|
||||
this.currentPicBedConfig.s3ForcePathStyle,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.logger,
|
||||
this.currentPicBedConfig.dogeCloudSupport || false,
|
||||
this.currentPicBedConfig.bucketName || ''
|
||||
)
|
||||
case 'sftp':
|
||||
return new API.SftpApi(this.currentPicBedConfig.host, this.currentPicBedConfig.port, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.privateKey, this.currentPicBedConfig.passphrase, this.currentPicBedConfig.fileMode, this.currentPicBedConfig.dirMode, this.logger)
|
||||
return new API.SftpApi(
|
||||
this.currentPicBedConfig.host,
|
||||
this.currentPicBedConfig.port,
|
||||
this.currentPicBedConfig.username,
|
||||
this.currentPicBedConfig.password,
|
||||
this.currentPicBedConfig.privateKey,
|
||||
this.currentPicBedConfig.passphrase,
|
||||
this.currentPicBedConfig.fileMode,
|
||||
this.currentPicBedConfig.dirMode,
|
||||
this.logger
|
||||
)
|
||||
case 'tcyun':
|
||||
return new API.TcyunApi(this.currentPicBedConfig.secretId, this.currentPicBedConfig.secretKey, this.logger)
|
||||
case 'upyun':
|
||||
return new API.UpyunApi(this.currentPicBedConfig.bucketName, this.currentPicBedConfig.operator, this.currentPicBedConfig.password, this.logger, this.currentPicBedConfig.antiLeechToken, this.currentPicBedConfig.expireTime)
|
||||
return new API.UpyunApi(
|
||||
this.currentPicBedConfig.bucketName,
|
||||
this.currentPicBedConfig.operator,
|
||||
this.currentPicBedConfig.password,
|
||||
this.logger,
|
||||
this.currentPicBedConfig.antiLeechToken,
|
||||
this.currentPicBedConfig.expireTime
|
||||
)
|
||||
case 'webdavplist':
|
||||
return new API.WebdavplistApi(this.currentPicBedConfig.endpoint, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.proxy, this.currentPicBedConfig.authType, this.logger)
|
||||
return new API.WebdavplistApi(
|
||||
this.currentPicBedConfig.endpoint,
|
||||
this.currentPicBedConfig.username,
|
||||
this.currentPicBedConfig.password,
|
||||
this.currentPicBedConfig.sslEnabled,
|
||||
this.currentPicBedConfig.proxy,
|
||||
this.currentPicBedConfig.authType,
|
||||
this.logger
|
||||
)
|
||||
default:
|
||||
return {} as any
|
||||
}
|
||||
@@ -117,9 +160,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
|
||||
saveConfig(config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn(
|
||||
'the format of config is invalid, please provide object'
|
||||
)
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
}
|
||||
this.setConfig(config)
|
||||
@@ -136,9 +177,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
|
||||
setConfig(config: IStringKeyMap): void {
|
||||
if (!isInputConfigValid(config)) {
|
||||
this.logger.warn(
|
||||
'the format of config is invalid, please provide object'
|
||||
)
|
||||
this.logger.warn('the format of config is invalid, please provide object')
|
||||
return
|
||||
}
|
||||
Object.keys(config).forEach((name: string) => {
|
||||
@@ -151,9 +190,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
unset(this.getConfig(key), propName)
|
||||
}
|
||||
|
||||
async getBucketList (
|
||||
param?: IStringKeyMap | undefined
|
||||
): Promise<any> {
|
||||
async getBucketList(param?: IStringKeyMap | undefined): Promise<any> {
|
||||
let client
|
||||
const name = this.currentPicBedConfig.picBedName.replace('plist', '')
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
@@ -171,36 +208,36 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
return []
|
||||
}
|
||||
case 'upyun':
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
Name: this.currentPicBedConfig.bucketName,
|
||||
Location: 'upyun',
|
||||
CreationDate: new Date().toISOString()
|
||||
}]
|
||||
}
|
||||
]
|
||||
case 'smms':
|
||||
case 'webdavplist':
|
||||
case 'local':
|
||||
case 'sftp':
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
Name: name,
|
||||
Location: name,
|
||||
CreationDate: new Date().toISOString()
|
||||
}]
|
||||
}
|
||||
]
|
||||
default:
|
||||
console.log(param)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketInfo (
|
||||
param?: IStringKeyMap | undefined
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketInfo(param?: IStringKeyMap | undefined): Promise<IStringKeyMap | IManageError> {
|
||||
console.log(param)
|
||||
return {}
|
||||
}
|
||||
|
||||
async getBucketDomain (
|
||||
param: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketDomain(param: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -225,9 +262,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async createBucket (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async createBucket(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -246,44 +281,32 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucket (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucket(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async getOperatorList (
|
||||
param?: IStringKeyMap
|
||||
): Promise<string[] | IManageError> {
|
||||
async getOperatorList(param?: IStringKeyMap): Promise<string[] | IManageError> {
|
||||
console.log(param)
|
||||
return []
|
||||
}
|
||||
|
||||
async addOperator (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async addOperator(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async deleteOperator (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteOperator(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async getBucketAclPolicy (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketAclPolicy(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
console.log(param)
|
||||
return {}
|
||||
}
|
||||
|
||||
async setBucketAclPolicy (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async setBucketAclPolicy(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'qiniu':
|
||||
@@ -299,9 +322,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async getBucketListRecursively (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketListRecursively(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
let window
|
||||
const defaultResult = {
|
||||
@@ -344,9 +365,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
* @param param
|
||||
* @returns
|
||||
*/
|
||||
async getBucketListBackstage (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketListBackstage(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
let client
|
||||
let window
|
||||
const defaultResult = {
|
||||
@@ -393,9 +412,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
* isDir: 是否是文件夹
|
||||
* fileSize: 文件大小
|
||||
**/
|
||||
async getBucketFileList (
|
||||
param?: IStringKeyMap
|
||||
): Promise<IStringKeyMap | IManageError> {
|
||||
async getBucketFileList(param?: IStringKeyMap): Promise<IStringKeyMap | IManageError> {
|
||||
const defaultResponse = {
|
||||
fullList: <any>[],
|
||||
isTruncated: false,
|
||||
@@ -422,9 +439,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -451,9 +466,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteBucketFolder (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async deleteBucketFolder(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -477,9 +490,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async renameBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async renameBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -502,9 +513,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async downloadBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async downloadBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -531,16 +540,12 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async copyMoveBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async copyMoveBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
console.log(param)
|
||||
return false
|
||||
}
|
||||
|
||||
async createBucketFolder (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async createBucketFolder(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -564,9 +569,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadBucketFile (
|
||||
param?: IStringKeyMap
|
||||
): Promise<boolean> {
|
||||
async uploadBucketFile(param?: IStringKeyMap): Promise<boolean> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
@@ -592,9 +595,7 @@ export class ManageApi extends EventEmitter implements IManageApiType {
|
||||
}
|
||||
}
|
||||
|
||||
async getPreSignedUrl (
|
||||
param?: IStringKeyMap
|
||||
): Promise<string> {
|
||||
async getPreSignedUrl(param?: IStringKeyMap): Promise<string> {
|
||||
let client
|
||||
switch (this.currentPicBedConfig.picBedName) {
|
||||
case 'tcyun':
|
||||
|
||||
@@ -18,17 +18,12 @@ import { ManageLogger } from '~/manage/utils/logger'
|
||||
import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum'
|
||||
import { formatHttpProxy } from '#/utils/common'
|
||||
|
||||
export const getFSFile = async (
|
||||
filePath: string,
|
||||
stream: boolean = false
|
||||
): Promise<IStringKeyMap> => {
|
||||
export const getFSFile = async (filePath: string, stream: boolean = false): Promise<IStringKeyMap> => {
|
||||
try {
|
||||
return {
|
||||
extension: path.extname(filePath),
|
||||
fileName: path.basename(filePath),
|
||||
buffer: stream
|
||||
? fs.createReadStream(filePath)
|
||||
: await fs.readFile(filePath),
|
||||
buffer: stream ? fs.createReadStream(filePath) : await fs.readFile(filePath),
|
||||
success: true
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -39,9 +34,7 @@ export const getFSFile = async (
|
||||
}
|
||||
|
||||
export function isInputConfigValid(config: any): boolean {
|
||||
return typeof config === 'object' &&
|
||||
!Array.isArray(config) &&
|
||||
Object.keys(config).length > 0
|
||||
return typeof config === 'object' && !Array.isArray(config) && Object.keys(config).length > 0
|
||||
}
|
||||
|
||||
export const getFileMimeType = (filePath: string): string => mime.lookup(filePath) || 'application/octet-stream'
|
||||
@@ -83,7 +76,8 @@ export const clearTempFolder = () => fs.emptyDirSync(getTempDirPath())
|
||||
|
||||
export const md5 = (str: string, code: 'hex' | 'base64'): string => crypto.createHash('md5').update(str).digest(code)
|
||||
|
||||
export const hmacSha1Base64 = (secretKey: string, stringToSign: string) : string => crypto.createHmac('sha1', secretKey).update(Buffer.from(stringToSign, 'utf8')).digest('base64')
|
||||
export const hmacSha1Base64 = (secretKey: string, stringToSign: string): string =>
|
||||
crypto.createHmac('sha1', secretKey).update(Buffer.from(stringToSign, 'utf8')).digest('base64')
|
||||
|
||||
export const NewDownloader = async (
|
||||
instance: UpDownTaskQueue,
|
||||
@@ -150,9 +144,7 @@ export const gotUpload = async (
|
||||
throwHttpErrors: boolean = false,
|
||||
agent: any = {}
|
||||
) => {
|
||||
got(
|
||||
url,
|
||||
{
|
||||
got(url, {
|
||||
headers,
|
||||
method,
|
||||
body,
|
||||
@@ -161,8 +153,7 @@ export const gotUpload = async (
|
||||
},
|
||||
throwHttpErrors,
|
||||
agent
|
||||
}
|
||||
)
|
||||
})
|
||||
.on('uploadProgress', (progress: any) => {
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
@@ -174,7 +165,10 @@ export const gotUpload = async (
|
||||
instance.updateUploadTask({
|
||||
id,
|
||||
progress: res?.statusCode === 200 || res?.statusCode === 201 ? 100 : 0,
|
||||
status: res?.statusCode === 200 || res?.statusCode === 201 ? uploadTaskSpecialStatus.uploaded : commonTaskStatus.failed,
|
||||
status:
|
||||
res?.statusCode === 200 || res?.statusCode === 201
|
||||
? uploadTaskSpecialStatus.uploaded
|
||||
: commonTaskStatus.failed,
|
||||
finishTime: new Date().toLocaleString()
|
||||
})
|
||||
})
|
||||
@@ -220,7 +214,10 @@ const commonOptions = {
|
||||
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
|
||||
} as any
|
||||
|
||||
export const getAgent = (proxy:any, https: boolean = true): {
|
||||
export const getAgent = (
|
||||
proxy: any,
|
||||
https: boolean = true
|
||||
): {
|
||||
https?: HttpsProxyAgent
|
||||
http?: HttpProxyAgent
|
||||
} => {
|
||||
@@ -298,7 +295,9 @@ export function getOptions (
|
||||
...(body && { body }),
|
||||
...(responseType && { responseType }),
|
||||
...(timeout !== undefined ? { timeout: { request: timeout } } : { timeout: { request: 30000 } }),
|
||||
...(proxy && { agent: Object.fromEntries(Object.entries(getAgent(proxy)).filter(([, v]) => v !== undefined)) }),
|
||||
...(proxy && {
|
||||
agent: Object.fromEntries(Object.entries(getAgent(proxy)).filter(([, v]) => v !== undefined))
|
||||
}),
|
||||
throwHttpErrors: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ export async function dogecloudApi (
|
||||
secretKey: string
|
||||
) {
|
||||
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||
const sign = crypto.createHmac('sha1', secretKey).update(Buffer.from(apiPath + '\n' + body, 'utf8')).digest('hex')
|
||||
const sign = crypto
|
||||
.createHmac('sha1', secretKey)
|
||||
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
||||
.digest('hex')
|
||||
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||
try {
|
||||
const res = await axios.request({
|
||||
@@ -40,16 +43,22 @@ export async function dogecloudApi (
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTempToken (accessKey: string, secretKey: string): Promise<{} | DogecloudToken> {
|
||||
const dogeTempToken = await picgo.getConfig('Credentials.doge-token') || {} as any
|
||||
export async function getTempToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudToken> {
|
||||
const dogeTempToken = (await picgo.getConfig('Credentials.doge-token')) || ({} as any)
|
||||
if (dogeTempToken.token && dogeTempToken.expires > Date.now() + 7200000) {
|
||||
return dogeTempToken.token
|
||||
}
|
||||
try {
|
||||
const data = await dogecloudApi('/auth/tmp_token.json', {
|
||||
const data = await dogecloudApi(
|
||||
'/auth/tmp_token.json',
|
||||
{
|
||||
channel: 'OSS_FULL',
|
||||
scopes: ['*']
|
||||
}, true, accessKey, secretKey)
|
||||
},
|
||||
true,
|
||||
accessKey,
|
||||
secretKey
|
||||
)
|
||||
const token = data.Credentials
|
||||
picgo.saveConfig({
|
||||
Credentials: {
|
||||
|
||||
@@ -27,9 +27,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
|
||||
#handleLog(type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](
|
||||
`[PicList ${type.toUpperCase()}]`
|
||||
)
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](`[PicList ${type.toUpperCase()}]`)
|
||||
console.log(logHeader, ...msg)
|
||||
this.#logLevel = this.#ctx.getConfig(configPaths.settings.logLevel)
|
||||
this.#logPath =
|
||||
@@ -61,11 +59,7 @@ export class ManageLogger implements ILogger {
|
||||
if (fs.existsSync(logPath)) {
|
||||
const logFileSize = fs.statSync(logPath).size
|
||||
const logFileSizeLimit =
|
||||
enforceNumber(
|
||||
this.#ctx.getConfig<Undefinable<number>>(
|
||||
configPaths.settings.logFileSizeLimit
|
||||
) || 10
|
||||
) *
|
||||
enforceNumber(this.#ctx.getConfig<Undefinable<number>>(configPaths.settings.logFileSizeLimit) || 10) *
|
||||
1024 *
|
||||
1024
|
||||
return {
|
||||
@@ -87,11 +81,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
#handleWriteLog (
|
||||
logPath: string,
|
||||
type: string,
|
||||
...msg: ILogArgvTypeWithError[]
|
||||
): void {
|
||||
#handleWriteLog(logPath: string, type: string, ...msg: ILogArgvTypeWithError[]): void {
|
||||
try {
|
||||
if (this.#checkLogLevel(type, this.#logLevel)) {
|
||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicList ${type.toUpperCase()}] `
|
||||
@@ -122,10 +112,7 @@ export class ManageLogger implements ILogger {
|
||||
return result
|
||||
}
|
||||
|
||||
#checkLogLevel (
|
||||
type: string,
|
||||
level: undefined | string | string[]
|
||||
): boolean {
|
||||
#checkLogLevel(type: string, level: undefined | string | string[]): boolean {
|
||||
if (level === undefined || level === 'all') {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
|
||||
import routers from '~/server/routerManager'
|
||||
import {
|
||||
handleResponse,
|
||||
ensureHTTPLink
|
||||
} from '~/server/utils'
|
||||
import { handleResponse, ensureHTTPLink } from '~/server/utils'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
@@ -31,9 +28,7 @@ const multerStorage = multer.diskStorage({
|
||||
filename: function (_req: any, file: { originalname: any }, cb: (arg0: null, arg1: any) => void) {
|
||||
// eslint-disable-next-line no-control-regex
|
||||
if (!/[^\u0000-\u00ff]/.test(file.originalname)) {
|
||||
file.originalname = Buffer.from(file.originalname, 'latin1').toString(
|
||||
'utf8'
|
||||
)
|
||||
file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
|
||||
}
|
||||
cb(null, file.originalname)
|
||||
}
|
||||
@@ -63,7 +58,7 @@ class Server {
|
||||
}
|
||||
|
||||
#isValidConfig(config: IObj | undefined) {
|
||||
return config && config.port && config.host && (config.enable !== undefined)
|
||||
return config && config.port && config.host && config.enable !== undefined
|
||||
}
|
||||
|
||||
#handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
@@ -108,7 +103,7 @@ class Server {
|
||||
}
|
||||
}
|
||||
if (request.headers['content-type'] && request.headers['content-type'].startsWith('multipart/form-data')) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error since the multer type is not correct
|
||||
uploadMulter.any()(request, response, (err: any) => {
|
||||
if (err) {
|
||||
logger.info('[PicList Server]', err)
|
||||
@@ -120,7 +115,7 @@ class Server {
|
||||
}
|
||||
})
|
||||
}
|
||||
// @ts-ignore
|
||||
// @ts-expect-error since the multer type is not correct
|
||||
const list = request.files.map(file => file.path)
|
||||
logger.info('[PicList Server] get a formData request')
|
||||
const handler = routers.getHandler(url!, 'POST')?.handler
|
||||
@@ -140,7 +135,7 @@ class Server {
|
||||
})
|
||||
request.on('end', () => {
|
||||
try {
|
||||
postObj = (body === '') ? {} : JSON.parse(body)
|
||||
postObj = body === '' ? {} : JSON.parse(body)
|
||||
} catch (err: any) {
|
||||
logger.error('[PicList Server]', err)
|
||||
return handleResponse({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
type HttpMethod = 'GET' | 'POST'
|
||||
|
||||
class Router {
|
||||
#router = new Map<string, Map<HttpMethod, {handler: routeHandler, urlparams?: URLSearchParams}>>()
|
||||
#router = new Map<string, Map<HttpMethod, { handler: routeHandler; urlparams?: URLSearchParams }>>()
|
||||
|
||||
#addRoute(method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
if (!this.#router.has(url)) {
|
||||
|
||||
@@ -16,10 +16,7 @@ import windowManager from 'apis/app/window/windowManager'
|
||||
|
||||
import { markdownContent } from '~/server/apiDoc'
|
||||
import router from '~/server/router'
|
||||
import {
|
||||
deleteChoosedFiles,
|
||||
handleResponse
|
||||
} from '~/server/utils'
|
||||
import { deleteChoosedFiles, handleResponse } from '~/server/utils'
|
||||
|
||||
import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
@@ -42,13 +39,15 @@ async function responseForGet ({ response } : {response: http.ServerResponse}) {
|
||||
router.get('/', responseForGet)
|
||||
router.get('/upload', responseForGet)
|
||||
|
||||
router.post('/upload', async ({
|
||||
router.post(
|
||||
'/upload',
|
||||
async ({
|
||||
response,
|
||||
list = [],
|
||||
urlparams
|
||||
}: {
|
||||
response: IHttpResponse,
|
||||
list?: string[],
|
||||
response: IHttpResponse
|
||||
list?: string[]
|
||||
urlparams?: URLSearchParams
|
||||
}): Promise<void> => {
|
||||
try {
|
||||
@@ -70,9 +69,9 @@ router.post('/upload', async ({
|
||||
let currentPicBedConfigId = ''
|
||||
let needRestore = false
|
||||
if (picbed) {
|
||||
const currentPicBed = picgo.getConfig<IStringKeyMap>('picBed') || {} as IStringKeyMap
|
||||
currentPicBedType = currentPicBed.current || ''
|
||||
currentPicBedConfig = currentPicBed[currentPicBedType] || {} as IStringKeyMap
|
||||
const currentPicBed = picgo.getConfig<IStringKeyMap>('picBed') || ({} as IStringKeyMap)
|
||||
currentPicBedType = currentPicBed.uploader || currentPicBed.current || 'smms'
|
||||
currentPicBedConfig = currentPicBed[currentPicBedType] || ({} as IStringKeyMap)
|
||||
currentPicBedConfigId = currentPicBedConfig._id
|
||||
const configName = urlparams?.get('configName') || currentPicBed[picbed]?._configName
|
||||
if (picbed === currentPicBedType && configName === currentPicBedConfig._configName) {
|
||||
@@ -176,15 +175,12 @@ router.post('/upload', async ({
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
router.post('/delete', async ({
|
||||
response,
|
||||
list = []
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
list?: IStringKeyMap[]
|
||||
}): Promise<void> => {
|
||||
router.post(
|
||||
'/delete',
|
||||
async ({ response, list = [] }: { response: IHttpResponse; list?: IStringKeyMap[] }): Promise<void> => {
|
||||
if (list.length === 0) {
|
||||
handleResponse({
|
||||
response,
|
||||
@@ -221,13 +217,10 @@ router.post('/delete', async ({
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
router.any('/heartbeat', async ({
|
||||
response
|
||||
} : {
|
||||
response: IHttpResponse,
|
||||
}) => {
|
||||
router.any('/heartbeat', async ({ response }: { response: IHttpResponse }) => {
|
||||
handleResponse({
|
||||
response,
|
||||
body: {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
Notification
|
||||
} from 'electron'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
@@ -29,9 +27,9 @@ export const handleResponse = ({
|
||||
success: false
|
||||
}
|
||||
}: {
|
||||
response: IHttpResponse,
|
||||
statusCode?: number,
|
||||
header?: IObj,
|
||||
response: IHttpResponse
|
||||
statusCode?: number
|
||||
header?: IObj
|
||||
body?: any
|
||||
}) => {
|
||||
if (body?.success === false) {
|
||||
@@ -43,9 +41,7 @@ export const handleResponse = ({
|
||||
}
|
||||
|
||||
export const ensureHTTPLink = (url: string): string => {
|
||||
return url.startsWith('http')
|
||||
? url
|
||||
: `http://${url}`
|
||||
return url.startsWith('http') ? url : `http://${url}`
|
||||
}
|
||||
|
||||
export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => {
|
||||
@@ -61,10 +57,7 @@ export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> =>
|
||||
const noteFunc = (value: boolean) => {
|
||||
const notification = new Notification({
|
||||
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
|
||||
body: T(value
|
||||
? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED'
|
||||
: 'GALLERY_SYNC_DELETE_NOTICE_FAILED'
|
||||
)
|
||||
body: T(value ? 'GALLERY_SYNC_DELETE_NOTICE_SUCCEED' : 'GALLERY_SYNC_DELETE_NOTICE_FAILED')
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
@@ -83,10 +83,14 @@ class WebServer {
|
||||
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) => {
|
||||
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 {
|
||||
|
||||
@@ -18,10 +18,7 @@ function beforeOpen () {
|
||||
resolveOtherI18nFiles()
|
||||
}
|
||||
|
||||
function copyFileOutsideOfElectronAsar (
|
||||
sourceInAsarArchive: string,
|
||||
destOutsideAsarArchive: string
|
||||
) {
|
||||
function copyFileOutsideOfElectronAsar(sourceInAsarArchive: string, destOutsideAsarArchive: string) {
|
||||
if (fs.existsSync(sourceInAsarArchive)) {
|
||||
// file will be copied
|
||||
if (fs.statSync(sourceInAsarArchive).isFile()) {
|
||||
@@ -85,13 +82,7 @@ function resolveClipboardImageGenerator () {
|
||||
}
|
||||
|
||||
function getClipboardFiles() {
|
||||
const files = [
|
||||
'/linux.sh',
|
||||
'/mac.applescript',
|
||||
'/windows.ps1',
|
||||
'/windows10.ps1',
|
||||
'/wsl.sh'
|
||||
]
|
||||
const files = ['/linux.sh', '/mac.applescript', '/windows.ps1', '/windows10.ps1', '/wsl.sh']
|
||||
|
||||
return files.map(item => {
|
||||
return {
|
||||
|
||||
+21
-10
@@ -12,7 +12,9 @@ import { configPaths } from '#/utils/configPaths'
|
||||
|
||||
export let tray: Tray
|
||||
|
||||
export const setTray = (t: Tray) => { tray = t }
|
||||
export const setTray = (t: Tray) => {
|
||||
tray = t
|
||||
}
|
||||
|
||||
export const getTray = () => tray
|
||||
|
||||
@@ -32,13 +34,15 @@ export const handleCopyUrl = (str: string): void => {
|
||||
* show notification
|
||||
* @param options
|
||||
*/
|
||||
export const showNotification = (options: IPrivateShowNotificationOption = {
|
||||
export const showNotification = (
|
||||
options: IPrivateShowNotificationOption = {
|
||||
title: '',
|
||||
body: '',
|
||||
clickToCopy: false,
|
||||
copyContent: '',
|
||||
clickFn: () => {}
|
||||
}) => {
|
||||
}
|
||||
) => {
|
||||
const notification = new Notification({
|
||||
title: options.title,
|
||||
body: options.body
|
||||
@@ -60,10 +64,8 @@ export const showNotification = (options: IPrivateShowNotificationOption = {
|
||||
}
|
||||
|
||||
export const showMessageBox = (options: any) => {
|
||||
return new Promise<IShowMessageBoxResult>(async (resolve) => {
|
||||
dialog.showMessageBox(
|
||||
options
|
||||
).then((res) => {
|
||||
return new Promise<IShowMessageBoxResult>(async resolve => {
|
||||
dialog.showMessageBox(options).then(res => {
|
||||
resolve({
|
||||
result: res.response,
|
||||
checkboxChecked: res.checkboxChecked
|
||||
@@ -118,14 +120,18 @@ export const getClipboardFilePath = (): string => {
|
||||
}
|
||||
|
||||
if (img.isEmpty() && platform === 'win32') {
|
||||
const imgPath = clipboard.readBuffer('FileNameW')?.toString('ucs2')?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
||||
const imgPath = clipboard
|
||||
.readBuffer('FileNameW')
|
||||
?.toString('ucs2')
|
||||
?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
||||
return imgPath || ''
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export const handleUrlEncodeWithSetting = (url: string) => db.get(configPaths.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'
|
||||
|
||||
@@ -163,7 +169,12 @@ const generateYOURLSShortUrl = async (url: string) => {
|
||||
if (!/^https?:\/\//.test(domain)) {
|
||||
domain = `http://${domain}`
|
||||
}
|
||||
const params = new URLSearchParams({ signature, action: 'shorturl', format: 'json', url })
|
||||
const params = new URLSearchParams({
|
||||
signature,
|
||||
action: 'shorturl',
|
||||
format: 'json',
|
||||
url
|
||||
})
|
||||
try {
|
||||
const res = await axios.get(`${domain}/yourls-api.php?${params.toString()}`)
|
||||
if (res.data?.shorturl) {
|
||||
|
||||
@@ -40,7 +40,10 @@ async function dogecloudApi (
|
||||
secretKey: string
|
||||
) {
|
||||
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||
const sign = crypto.createHmac('sha1', secretKey).update(Buffer.from(apiPath + '\n' + body, 'utf8')).digest('hex')
|
||||
const sign = crypto
|
||||
.createHmac('sha1', secretKey)
|
||||
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
||||
.digest('hex')
|
||||
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||
try {
|
||||
const res = await axios.request({
|
||||
@@ -62,12 +65,18 @@ async function dogecloudApi (
|
||||
}
|
||||
}
|
||||
|
||||
async function getDogeToken (accessKey: string, secretKey: string): Promise<{} | DogecloudTokenFull> {
|
||||
async function getDogeToken(accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
||||
try {
|
||||
const data = await dogecloudApi('/auth/tmp_token.json', {
|
||||
const data = await dogecloudApi(
|
||||
'/auth/tmp_token.json',
|
||||
{
|
||||
channel: 'OSS_FULL',
|
||||
scopes: ['*']
|
||||
}, true, accessKey, secretKey)
|
||||
},
|
||||
true,
|
||||
accessKey,
|
||||
secretKey
|
||||
)
|
||||
return data
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
@@ -77,8 +86,15 @@ async function getDogeToken (accessKey: string, secretKey: string): Promise<{} |
|
||||
|
||||
export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||
try {
|
||||
const { url: rawUrl, type, config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap
|
||||
let { imgUrl, config: { region } } = configMap
|
||||
const {
|
||||
url: rawUrl,
|
||||
type,
|
||||
config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy }
|
||||
} = configMap
|
||||
let {
|
||||
imgUrl,
|
||||
config: { region }
|
||||
} = configMap
|
||||
if (type === 'aws-s3' || type === 'aws-s3-plist') {
|
||||
imgUrl = rawUrl || imgUrl || ''
|
||||
}
|
||||
@@ -139,6 +155,9 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode
|
||||
}
|
||||
}
|
||||
let result: any
|
||||
try {
|
||||
fileKey = decodeURIComponent(fileKey)
|
||||
} catch (err: any) {}
|
||||
try {
|
||||
const client = new S3Client(s3Options)
|
||||
const command = new DeleteObjectCommand({
|
||||
@@ -164,8 +183,10 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode
|
||||
|
||||
export async function removeFileFromDogeInMain(configMap: IStringKeyMap) {
|
||||
try {
|
||||
const { config: { bucketName, AccessKey, SecretKey } } = configMap
|
||||
const token = await getDogeToken(AccessKey, SecretKey) as DogecloudTokenFull
|
||||
const {
|
||||
config: { bucketName, AccessKey, SecretKey }
|
||||
} = configMap
|
||||
const token = (await getDogeToken(AccessKey, SecretKey)) as DogecloudTokenFull
|
||||
const bucket = token.Buckets?.find(item => item.name === bucketName || item.s3Bucket === bucketName)
|
||||
const newConfigMap = Object.assign({}, configMap)
|
||||
newConfigMap.config = {
|
||||
@@ -227,7 +248,7 @@ export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileNa
|
||||
try {
|
||||
const client = SSHClient.instance
|
||||
await client.connect(config)
|
||||
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
|
||||
const uploadPath = `/${config.uploadPath || ''}/`.replace(/\/+/g, '/')
|
||||
const remote = path.join(uploadPath, fileName)
|
||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||
client.close()
|
||||
|
||||
@@ -9,11 +9,8 @@ export const isMacOS = process.platform === 'darwin'
|
||||
|
||||
let version: string | undefined
|
||||
|
||||
const clean = (version: string) => version.split('.').length === 1
|
||||
? `${version}.0.0`
|
||||
: version.split('.').length === 2
|
||||
? `${version}.0`
|
||||
: version
|
||||
const clean = (version: string) =>
|
||||
version.split('.').length === 1 ? `${version}.0.0` : version.split('.').length === 2 ? `${version}.0` : version
|
||||
|
||||
const parseVersion = (plist: string) => {
|
||||
const matches = /<key>ProductVersion<\/key>\s*<string>([\d.]+)<\/string>/.exec(plist)
|
||||
|
||||
@@ -4,14 +4,16 @@ import { configPaths } from '#/utils/configPaths'
|
||||
const getPicBeds = () => {
|
||||
const picBedTypes = picgo.helper.uploader.getIdList()
|
||||
const picBedFromDB = picgo.getConfig<IPicBedType[]>(configPaths.picBed.list) || []
|
||||
const picBeds = picBedTypes.map((item: string) => {
|
||||
const picBeds = picBedTypes
|
||||
.map((item: string) => {
|
||||
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
|
||||
return {
|
||||
type: item,
|
||||
name: picgo.helper.uploader.get(item)!.name || item,
|
||||
visible: visible ? visible.visible : true
|
||||
}
|
||||
}).sort((a) => {
|
||||
})
|
||||
.sort(a => {
|
||||
if (a.type === 'tcyun') {
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
|
||||
if (fileList?.length === 0) {
|
||||
return null // for uploading images in clipboard
|
||||
} else if ((fileList?.length || 0) > 0) {
|
||||
const result = fileList!.map(item => {
|
||||
const result = fileList!
|
||||
.map(item => {
|
||||
if (isUrl(item)) {
|
||||
return {
|
||||
path: item
|
||||
@@ -57,13 +58,12 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger
|
||||
return null
|
||||
}
|
||||
}
|
||||
}).filter(item => item !== null) as Result
|
||||
})
|
||||
.filter(item => item !== null) as Result
|
||||
return result
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export {
|
||||
getUploadFiles
|
||||
}
|
||||
export { getUploadFiles }
|
||||
|
||||
@@ -13,6 +13,7 @@ export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IP
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
config[i].choices = (config[i].choices as Function)()
|
||||
}
|
||||
}
|
||||
@@ -20,13 +21,17 @@ export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IP
|
||||
}
|
||||
|
||||
export const completeUploaderMetaConfig = (originData: IStringKeyMap): IUploaderConfigListItem => {
|
||||
return Object.assign({
|
||||
return Object.assign(
|
||||
{
|
||||
_configName: 'Default'
|
||||
}, trimValues(originData), {
|
||||
},
|
||||
trimValues(originData),
|
||||
{
|
||||
_id: uuid(),
|
||||
_createdAt: Date.now(),
|
||||
_updatedAt: Date.now()
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +135,9 @@ export const deleteUploaderConfig = (type: string, id: string): IUploaderConfigI
|
||||
/**
|
||||
* upgrade old uploader config to new format
|
||||
*/
|
||||
export const upgradeUploaderConfig = (type: string): {
|
||||
export const upgradeUploaderConfig = (
|
||||
type: string
|
||||
): {
|
||||
configList: IStringKeyMap[]
|
||||
defaultId: string
|
||||
} => {
|
||||
|
||||
+36
-14
@@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import fs from 'fs-extra'
|
||||
import { NodeSSH, Config, SSHExecCommandResponse } from 'node-ssh-no-cpu-features'
|
||||
import path from 'path'
|
||||
@@ -26,7 +25,11 @@ class SSHClient {
|
||||
async connect(config: ISftpPlistConfig): Promise<boolean> {
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
const loginInfo: Config = privateKey
|
||||
? { username, privateKeyPath: privateKey, passphrase: passphrase || undefined }
|
||||
? {
|
||||
username,
|
||||
privateKeyPath: privateKey,
|
||||
passphrase: passphrase || undefined
|
||||
}
|
||||
: { username, password }
|
||||
try {
|
||||
await SSHClient.client.connect({
|
||||
@@ -46,30 +49,42 @@ class SSHClient {
|
||||
const client = new Client()
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
const loginInfo: Config = privateKey
|
||||
? { username, privateKey: fs.readFileSync(privateKey), passphrase: passphrase || undefined }
|
||||
? {
|
||||
username,
|
||||
privateKey: fs.readFileSync(privateKey),
|
||||
passphrase: passphrase || undefined
|
||||
}
|
||||
: { username, password }
|
||||
remote = this.changeWinStylePathToUnix(remote)
|
||||
if (remote === '/' || remote.includes('*')) return false
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
client.on('ready', () => {
|
||||
client.sftp((err, sftp) => {
|
||||
client
|
||||
.on('ready', () => {
|
||||
client.sftp(
|
||||
(
|
||||
err: any,
|
||||
sftp: {
|
||||
unlink: (arg0: string, arg1: (err: any) => void) => void
|
||||
}
|
||||
) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (err) reject(false)
|
||||
sftp.unlink(remote, (err) => {
|
||||
sftp.unlink(remote, (err: any) => {
|
||||
// eslint-disable-next-line prefer-promise-reject-errors
|
||||
if (err) reject(false)
|
||||
client.end()
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}).connect({
|
||||
.connect({
|
||||
host: config.host,
|
||||
port: Number(config.port) || 22,
|
||||
...loginInfo
|
||||
})
|
||||
}
|
||||
)
|
||||
return await promise
|
||||
})
|
||||
return (await promise) as boolean
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
return false
|
||||
@@ -103,10 +118,14 @@ class SSHClient {
|
||||
}
|
||||
}
|
||||
|
||||
async putFile (local: string, remote: string, config: {
|
||||
async putFile(
|
||||
local: string,
|
||||
remote: string,
|
||||
config: {
|
||||
fileMode?: string
|
||||
dirMode?: string
|
||||
} = {}): Promise<boolean> {
|
||||
} = {}
|
||||
): Promise<boolean> {
|
||||
if (!this._isConnected) {
|
||||
throw new Error('SSH 未连接')
|
||||
}
|
||||
@@ -126,9 +145,12 @@ class SSHClient {
|
||||
}
|
||||
}
|
||||
|
||||
async mkdir (dirPath: string, config: {
|
||||
async mkdir(
|
||||
dirPath: string,
|
||||
config: {
|
||||
dirMode?: string
|
||||
} = {}): Promise<boolean> {
|
||||
} = {}
|
||||
): Promise<boolean> {
|
||||
if (!this._isConnected) {
|
||||
throw new Error('SSH 未连接')
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ const STORE_PATH = app.getPath('userData')
|
||||
const readFileAsBase64 = (filePath: string) => fs.readFileSync(filePath, { encoding: 'base64' })
|
||||
|
||||
const isHttpResSuccess = (res: any) => res.status >= 200 && res.status < 300
|
||||
const uploadOrUpdateMsg = (fileName: string, isUpdate: boolean = true) => isUpdate ? `update ${fileName} from PicList` : `upload ${fileName} from PicList`
|
||||
const uploadOrUpdateMsg = (fileName: string, isUpdate: boolean = true) =>
|
||||
isUpdate ? `update ${fileName} from PicList` : `upload ${fileName} from PicList`
|
||||
|
||||
const getSyncConfig = () => {
|
||||
return db.get(configPaths.settings.sync) || {
|
||||
return (
|
||||
db.get(configPaths.settings.sync) || {
|
||||
type: 'github',
|
||||
username: '',
|
||||
repo: '',
|
||||
@@ -26,6 +28,7 @@ const getSyncConfig = () => {
|
||||
token: '',
|
||||
proxy: ''
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const getProxyagent = (proxy: string | undefined) => {
|
||||
@@ -50,13 +53,7 @@ function getOctokit (syncConfig: ISyncConfig) {
|
||||
})
|
||||
}
|
||||
|
||||
const isSyncConfigValidate = ({
|
||||
type,
|
||||
username,
|
||||
repo,
|
||||
branch,
|
||||
token
|
||||
}: ISyncConfig) => {
|
||||
const isSyncConfigValidate = ({ type, username, repo, branch, token }: ISyncConfig) => {
|
||||
return type && username && repo && branch && token
|
||||
}
|
||||
|
||||
@@ -179,12 +176,16 @@ async function updateLocalToRemote (syncConfig: ISyncConfig, fileName: string) {
|
||||
}
|
||||
const data = shaRes.data as any
|
||||
const sha = data.sha
|
||||
const res = await axios.put(apiUrl, {
|
||||
const res = await axios.put(
|
||||
apiUrl,
|
||||
{
|
||||
...defaultConfig,
|
||||
sha
|
||||
}, {
|
||||
},
|
||||
{
|
||||
headers
|
||||
})
|
||||
}
|
||||
)
|
||||
return isHttpResSuccess(res)
|
||||
}
|
||||
default:
|
||||
@@ -220,7 +221,10 @@ async function uploadFile (fileName: string[]): Promise<number> {
|
||||
async function downloadAndWriteFile(url: string, localFilePath: string, config: any, isWriteJson = false) {
|
||||
const res = await axios.get(url, config)
|
||||
if (isHttpResSuccess(res)) {
|
||||
await fs.writeFile(localFilePath, isWriteJson ? JSON.stringify(res.data, null, 2) : Buffer.from(res.data.content, 'base64'))
|
||||
await fs.writeFile(
|
||||
localFilePath,
|
||||
isWriteJson ? JSON.stringify(res.data, null, 2) : Buffer.from(res.data.content, 'base64')
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -251,9 +255,14 @@ async function downloadRemoteToLocal (syncConfig: ISyncConfig, fileName: string)
|
||||
if (res.status === 200) {
|
||||
const data = res.data as any
|
||||
const downloadUrl = data.download_url
|
||||
return downloadAndWriteFile(downloadUrl, localFilePath, {
|
||||
return downloadAndWriteFile(
|
||||
downloadUrl,
|
||||
localFilePath,
|
||||
{
|
||||
httpsAgent: getProxyagent(proxy)
|
||||
}, true)
|
||||
},
|
||||
true
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -294,7 +303,4 @@ async function downloadFile (fileName: string[]): Promise<number> {
|
||||
return (await Promise.all(fileName.map(downloadFunc))).reduce((a, b) => a + b, 0)
|
||||
}
|
||||
|
||||
export {
|
||||
uploadFile,
|
||||
downloadFile
|
||||
}
|
||||
export { uploadFile, downloadFile }
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
id="app"
|
||||
:key="pageReloadCount"
|
||||
>
|
||||
<div id="app" :key="pageReloadCount">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,7 +22,6 @@ onBeforeMount(async () => {
|
||||
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import axios from 'axios'
|
||||
import path from 'path'
|
||||
|
||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
|
||||
|
||||
interface IConfigMap {
|
||||
fileName: string
|
||||
config: {
|
||||
url: string
|
||||
username: string
|
||||
password: string
|
||||
uploadPath: string
|
||||
token: string
|
||||
}
|
||||
}
|
||||
|
||||
const getAListToken = async (url: string, username: string, password: string) => {
|
||||
const tokenStore = await getConfig<IStringKeyMap>('picgo-plugin-buildin-alistplist')
|
||||
if (tokenStore && tokenStore.refreshedAt && Date.now() - tokenStore.refreshedAt < 3600000 && tokenStore.token) {
|
||||
return tokenStore.token
|
||||
}
|
||||
const res = await axios.post(`${url}/api/auth/login`, {
|
||||
username,
|
||||
password
|
||||
})
|
||||
if (res.data.code === 200 && res.data.message === 'success') {
|
||||
const token = res.data.data.token
|
||||
saveConfig({
|
||||
'picgo-plugin-buildin-alistplist': {
|
||||
token,
|
||||
refreshedAt: Date.now()
|
||||
}
|
||||
})
|
||||
return token
|
||||
}
|
||||
}
|
||||
|
||||
export default class AListplistApi {
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
const { url, username, password, uploadPath } = config
|
||||
let token = config.token
|
||||
if (!token) {
|
||||
token = await getAListToken(url, username, password)
|
||||
}
|
||||
if (!url || !(token || (username && password))) {
|
||||
deleteFailedLog(fileName, 'Alist', 'No valid token or username/password provided')
|
||||
return false
|
||||
}
|
||||
const result = await axios.request({
|
||||
method: 'post',
|
||||
url: `${url}/api/fs/remove`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token
|
||||
},
|
||||
data: {
|
||||
dir: path.join('/', uploadPath, path.dirname(fileName)),
|
||||
names: [path.basename(fileName)]
|
||||
}
|
||||
})
|
||||
if (result.data.code === 200) {
|
||||
deleteLog(fileName, 'Alist')
|
||||
return true
|
||||
}
|
||||
deleteLog(fileName, 'Alist', false)
|
||||
return false
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(fileName, 'Alist', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,7 @@ interface IConfigMap {
|
||||
|
||||
export default class AliyunApi {
|
||||
static #getKey(fileName: string, path?: string): string {
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
: fileName
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` : fileName
|
||||
}
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AlistApi from '@/apis/alist'
|
||||
import AlistplistApi from '@/apis/alistplist'
|
||||
import AliyunApi from '@/apis/aliyun'
|
||||
import AwsS3Api from '@/apis/awss3'
|
||||
import DogeCloudApi from '@/apis/dogecloud'
|
||||
@@ -17,6 +18,7 @@ import WebdavApi from '@/apis/webdav'
|
||||
|
||||
const apiMap: IStringKeyMap = {
|
||||
alist: AlistApi,
|
||||
alistplist: AlistplistApi,
|
||||
aliyun: AliyunApi,
|
||||
'aws-s3': AwsS3Api,
|
||||
'aws-s3-plist': AwsS3Api,
|
||||
|
||||
@@ -9,7 +9,7 @@ export default class AwsS3Api {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromS3InMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'AWS S3', error)
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class AwsS3Api {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromDogeInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'DogeCloud', error)
|
||||
|
||||
@@ -17,13 +17,15 @@ export default class GithubApi {
|
||||
|
||||
static #createKey(path: string | undefined, fileName: string): string {
|
||||
const formatedFileName = fileName.replace(/%2F/g, '/')
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}`
|
||||
: formatedFileName
|
||||
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` : formatedFileName
|
||||
}
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, hash, config: { repo, token, branch, path } } = configMap
|
||||
const {
|
||||
fileName,
|
||||
hash,
|
||||
config: { repo, token, branch, path }
|
||||
} = configMap
|
||||
const [owner, repoName] = repo.split('/')
|
||||
const octokit = GithubApi.#createOctokit(token)
|
||||
const key = GithubApi.#createKey(path, fileName)
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class HuaweicloudApi {
|
||||
static async delete(configMap: IStringKeyMap): Promise<boolean> {
|
||||
try {
|
||||
return ipcRenderer
|
||||
? await triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap)) || false
|
||||
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap))) || false
|
||||
: await removeFileFromHuaweiInMain(getRawData(configMap))
|
||||
} catch (error: any) {
|
||||
deleteFailedLog(configMap.fileName, 'HuaweiCloud', error)
|
||||
|
||||
@@ -11,10 +11,7 @@ export default class ImgurApi {
|
||||
static #baseUrl = 'https://api.imgur.com/3'
|
||||
|
||||
static async delete(configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
config: { clientId = '', username = '', accessToken = '' } = {},
|
||||
hash = ''
|
||||
} = configMap
|
||||
const { config: { clientId = '', username = '', accessToken = '' } = {}, hash = '' } = configMap
|
||||
let Authorization: string, apiUrl: string
|
||||
|
||||
if (username && accessToken) {
|
||||
|
||||
@@ -26,8 +26,7 @@ export default class LskyplistApi {
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
try {
|
||||
const response: AxiosResponse = await axios.delete(
|
||||
`${host}/api/v1/images/${hash}`, {
|
||||
const response: AxiosResponse = await axios.delete(`${host}/api/v1/images/${hash}`, {
|
||||
headers: v2Headers,
|
||||
timeout: 30000,
|
||||
httpsAgent: requestAgent
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user