diff --git a/.vscode/settings.json b/.vscode/settings.json index 329bd7b..fbe6d00 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,24 +1,7 @@ { - "eslint.enable": true, - "eslint.alwaysShowStatus": true, - "eslint.validate": [ - "javascript", - "javascriptreact", - "typescript", - "vue", - "typescriptreact" - ], "[stylus]": { "editor.formatOnSave": true }, - "stylusSupremacy.insertSemicolons": false, - "stylusSupremacy.insertBraces": false, - "stylusSupremacy.insertNewLineBetweenSelectors": true, - "stylusSupremacy.insertParenthesisAroundIfCondition": false, - "stylusSupremacy.alwaysUseNoneOverZero": true, - "stylusSupremacy.alwaysUseZeroWithoutUnit": true, - "stylusSupremacy.sortProperties": "grouped", - "stylusSupremacy.quoteChar": "\"", "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index f84334a..ee78f65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# :tada: 2.9.0 (2024-06-14) + + +### :sparkles: Features + +* **custom:** add tray tooltip ([8a565c1](https://github.com/Kuingsmile/piclist/commit/8a565c1)) +* **custom:** optimize get config speed ([106290f](https://github.com/Kuingsmile/piclist/commit/106290f)) +* **custom:** refactor all main ipc event ([5ddc182](https://github.com/Kuingsmile/piclist/commit/5ddc182)) +* **custom:** smms delete for repeat file ([07e7a26](https://github.com/Kuingsmile/piclist/commit/07e7a26)) +* **custom:** support create bucket for s3 ([226f170](https://github.com/Kuingsmile/piclist/commit/226f170)) +* **custom:** update i18n force update ([e9c386d](https://github.com/Kuingsmile/piclist/commit/e9c386d)) +* **custom:** use new ssh2 and node-ssh ([2290e4e](https://github.com/Kuingsmile/piclist/commit/2290e4e)) + + +### :bug: Bug Fixes + +* **custom:** change manage file name ([d7028fc](https://github.com/Kuingsmile/piclist/commit/d7028fc)) +* **custom:** fix bug while using webp plugin ([30341d4](https://github.com/Kuingsmile/piclist/commit/30341d4)), closes [#205](https://github.com/Kuingsmile/piclist/issues/205) +* **custom:** fix css error ([0c241bc](https://github.com/Kuingsmile/piclist/commit/0c241bc)) +* **custom:** fix url copy error for dirs ([f1a7a13](https://github.com/Kuingsmile/piclist/commit/f1a7a13)) + + +### :pencil: Documentation + +* **custom:** v2.9.0 changelog ([7226cc8](https://github.com/Kuingsmile/piclist/commit/7226cc8)) + + + ## :tada: 2.8.6 (2024-05-26) diff --git a/currentVersion.md b/currentVersion.md index 5e9ccfe..5924fe0 100644 --- a/currentVersion.md +++ b/currentVersion.md @@ -1,19 +1,32 @@ ### ✨ Features -- 现在从相册删除云端图片时的日志记录于日志文件中,而不是打印到控制台 -- 现在软件内窗口打开手册和打开图床手册时,会根据软件语言自动设置语言 -- 现在设置自定义mini窗口图标和保持置顶后会即时生效,不再需要重启软件 -- 优化了同步配置时的下载速度 -- 优化了监听剪贴板功能的性能表现 +- 图床 + - 现在`sm.ms`图床上传重复图片时,后上传的图片也支持云端删除了 +- 管理 + - 现在s3图床支持新建存储通 + - 调整了管理页面的部分布局 + - 代码高亮样式调整为stackoverflow-light +- 界面 + - 现在托盘菜单会根据当前状态显示开启/关闭剪贴板监听,而不是始终显示全部菜单 + - 现在托盘菜单会根据当前状态显示开启/关闭mini窗口 + - 现在鼠标悬停于托盘图标时,会显示当前图床和配置名 + - 移除了设置页面部分不必要的全局通知 + - 优化了英文下的设置页面布局 +- 性能 + - 现在启动软件时会自动清空剪贴板图片缓存文件夹 + - 优化了读取配置的性能表现 + - 优化了多个页面的加载速度 + - 升级vue等依赖至最新版本 ### 🐛 Bug Fixes -- 修复了第一次进入页面时,下拉选择项显示的默认值是后台值而非标签的问题 -- 修复了tray页面监听器没有正确移除的问题 - -### 📦Chore - -- 优化了[官网](https://piclist.cn)的加载速度,添加了`配置文件结构`的说明 -- mac打包平台迁移至`macos-12` -- 移除了已废弃的配置项相关的代码 -- 移除了管理配置中试剂未使用的`currentPicBedConfig`配置项 +- 修复了从obsidan发送删除请求时,会导致软件闪退的问题 +- 修复了使用`webp`插件时,上传剪贴板图片会导致软件闪退的问题 +- 修复了设置了图片处理操作后,上传URL会报错的问题 +- 修复了设置原始PicGo窗口大小后,重新打开设置界面时窗口大小数值没有更新的问题 +- 修复了主界面菜单加载和点击时反复触发获取图床配置导致的卡顿问题 +- 修复了切换语言时部分下拉框没有更新的问题 +- 修复了部分翻译错误 +- 管理页面 + - 修复了部分图床的文件夹复制链接错误的问题 + - 修复了文件浏览页面的css错误 diff --git a/currentVersion_en.md b/currentVersion_en.md index 2db2929..f2560c4 100644 --- a/currentVersion_en.md +++ b/currentVersion_en.md @@ -1,19 +1,32 @@ ### ✨ Features -- Now when deleting cloud images from the album, the log is recorded in the log file instead of being printed to the console -- Now when opening the manual and the image bed manual in the software window, the language will be automatically set according to the software language -- Now the custom mini window icon and keep top will take effect immediately after setting, no need to restart the software -- Optimized the download speed of synchronizing configuration -- Optimized the performance of listening to the clipboard function +- 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 ### 🐛 Bug Fixes -- Fixed the problem that the default value displayed in the drop-down selection when entering the page for the first time is the background value rather than the label -- Fixed the problem that the tray page listener was not correctly removed - -### 📦Chore - -- Optimized the loading speed of [official website](https://piclist.cn), added the description of `configuration file structure` -- Mac packaging platform migrated to `macos-12` -- Removed the code related to the deprecated configuration items -- Removed the `currentPicBedConfig` configuration item that is not used in the management configuration +- 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 diff --git a/package.json b/package.json index c9e3eab..9e424c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "piclist", - "version": "2.8.6", + "version": "2.9.0", "author": { "name": "Kuingsmile", "email": "pkukuing@gmail.com" @@ -38,7 +38,7 @@ "@aws-sdk/lib-storage": "^3.421.0", "@aws-sdk/s3-request-presigner": "^3.421.0", "@element-plus/icons-vue": "^2.3.1", - "@highlightjs/vue-plugin": "^2.1.0", + "@highlightjs/vue-plugin": "^2.1.2", "@nodelib/fs.walk": "^2.0.0", "@octokit/rest": "^19.0.7", "@picgo/i18n": "^1.0.0", @@ -48,11 +48,11 @@ "ali-oss": "^6.18.1", "axios": "^1.6.8", "compare-versions": "^4.1.3", - "core-js": "^3.33.3", + "core-js": "^3.37.1", "cos-nodejs-sdk-v5": "^2.12.5", "dexie": "^3.2.4", "electron-updater": "^6.1.4", - "element-plus": "2.4.4", + "element-plus": "2.7.4", "epipebomb": "^1.0.0", "fast-xml-parser": "^4.3.2", "form-data": "^4.0.0", @@ -60,34 +60,34 @@ "got": "^12.6.0", "highlight.js": "^11.9.0", "hpagent": "^1.2.0", - "keycode": "^2.2.0", "lowdb": "^1.0.0", "marked": "^9.1.5", "mime-types": "^2.1.35", "mitt": "^3.0.1", "multer": "^1.4.5-lts.1", - "node-ssh-no-cpu-features": "^1.0.1", + "node-ssh-no-cpu-features": "^2.0.0", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.8.8", + "piclist": "^1.8.10", "pinia": "^2.1.7", - "pinia-plugin-persistedstate": "^3.2.0", + "pinia-plugin-persistedstate": "^3.2.1", "proxy-agent": "^5.0.0", "qiniu": "7.9.0", "qrcode.vue": "^3.4.1", "querystring": "^0.2.1", "shell-path": "2.1.0", - "ssh2-no-cpu-features": "^1.0.0", + "ssh2-no-cpu-features": "^2.0.0", "upyun": "^3.4.6", "uuid": "^9.0.1", "video.js": "^8.6.1", - "vue": "^3.3.13", - "vue-router": "^4.2.5", + "vue": "^3.4.27", + "vue-router": "^4.3.2", "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", @@ -99,11 +99,10 @@ "@types/mime-types": "^2.1.4", "@types/multer": "^1.4.11", "@types/node": "^16.10.2", - "@types/request-promise-native": "^1.0.21", "@types/semver": "^7.5.6", - "@types/tunnel": "^0.0.6", + "@types/tunnel": "^0.0.7", "@types/upyun": "^3.4.3", - "@types/uuid": "^9.0.7", + "@types/uuid": "^9.0.8", "@types/write-file-atomic": "^4.0.3", "@typescript-eslint/eslint-plugin": "^6.12.0", "@typescript-eslint/parser": "^6.12.0", @@ -114,22 +113,22 @@ "@vue/cli-service": "^5.0.8", "@vue/eslint-config-standard": "^8.0.1", "@vue/eslint-config-typescript": "^12.0.0", - "@vue/runtime-dom": "^3.3.13", + "@vue/runtime-dom": "^3.4.27", "conventional-changelog": "^5.1.0", "cz-customizable": "^7.0.0", "dotenv": "^16.3.1", "dpdm": "^3.14.0", - "electron": "^22.0.2", + "electron": "^22.3.27", "eslint": "^8.54.0", - "eslint-config-standard": ">=16.0.0", + "eslint-config-standard": "^17.1.0", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-vue": "^9.18.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.2.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.12", + "npm-check-updates": "^16.14.20", "stylus": "^0.59.0", "stylus-loader": "^7.1.3", "typescript": "^4.9.5", diff --git a/public/i18n/en.yml b/public/i18n/en.yml index 02f1ee1..e7ba04d 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -2,6 +2,7 @@ LANG_DISPLAY_LABEL: 'English' ABOUT: About OPEN_MAIN_WINDOW: Open Main Window OPEN_MINI_WINDOW: Open Mini Window +HIDE_MINI_WINDOW: Hide Mini Window CHOOSE_DEFAULT_PICBED: Choose Default Picbed OPEN_UPDATE_HELPER: Open Update Helper RELOAD_APP: Reload App @@ -311,7 +312,7 @@ SETTINGS_SYNC_MANAGE_CONFIG: Manage configuration SETTINGS_AUTO_IMPORT: Auto import config in manage page SETTINGS_AUTO_IMPORT_SELECT_PICBED: Select picbed SETTINGS_TAB_SYSTEM: System -SETTINGS_TAB_SYNC_CONFIG: Sync and Configuration +SETTINGS_TAB_SYNC_CONFIG: Configuration SETTINGS_TAB_UPLOAD: Upload SETTINGS_TAB_ADVANCED: Advanced SETTINGS_TAB_UPDATE: Update @@ -378,37 +379,37 @@ TIPS_GET_PLUGIN_LIST_FAILED: Get plugin list failed # manageSetting MANAGE_SETTING_TITLE: Manage Setting -MANAGE_SETTING_AUTO_FRESH_TITLE: Auto refresh file list when entering new directory -MANAGE_SETTING_AUTO_FRESH_TIPS: Only applies to non-paginated mode, data is cached to indexdb to speed up loading speed +MANAGE_SETTING_ISAUTOREFRESH_TITLE: Auto refresh file list when entering new directory +MANAGE_SETTING_ISAUTOREFRESH_TIPS: Only applies to non-paginated mode, data is cached to indexdb to speed up loading speed MANAGE_SETTING_CLEAR_CACHE_TITLE: 'Clear file list cache database, currently in use:' MANAGE_SETTING_CLEAR_CACHE_FREE_TITLE: 'Available:' MANAGE_SETTING_CLEAR_CACHE_TIPS: After clearing, the file list will be reloaded when entering a new directory next time MANAGE_SETTING_CLEAR_CACHE_PROMPT: Are you sure you want to clear the file list cache database? MANAGE_SETTING_CLEAR_CACHE_BUTTON: Clear -MANAGE_SETTING_SHOW_THUMBNAIL_TITLE: Display the original image instead of format icon (requires public access permissions) -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_TITLE: Default display mode for the file list -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_LIST: List -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_CARD: Card -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TITLE: Force custom URL to use HTTPS -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TIPS: After enabling, all operations will automatically add the https prefix to custom domains -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TITLE: Preserve directory structure when uploading -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TIPS: After disabling, all files will be expanded to the specified directory -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_A: Download -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_B: ' File ' -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_C: will preserve the directory structure -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_D: ' Folder ' -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_FILE_TIPS: After enabling, the original directory structure will be preserved +MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: Display the original image instead of format icon (requires public access permissions) +MANAGE_SETTING_ISSHOWLIST_TITLE: Default display mode for the file list +MANAGE_SETTING_ISSHOWLIST_ON: List +MANAGE_SETTING_ISSHOWLIST_OFF: Card +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TITLE: Force custom URL to use HTTPS +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TIPS: After enabling, all operations will automatically add the https prefix to custom domains +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TITLE: Preserve directory structure when uploading +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TIPS: After disabling, all files will be expanded to the specified directory +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_A: Download +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_B: ' File ' +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_C: will preserve the directory structure +MANAGE_SETTING_ISDOWNLOADFOLDERKEEPDIRSTRUCTURE_TITLE_D: ' Folder ' +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TIPS: After enabling, the original directory structure will be preserved MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TITLE: Maximum number of files to download simultaneously (1-9999) MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TIPS: Not work on Tencent MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_INPUT_TIPS: Please enter the maximum number of files to download simultaneously -MANAGE_SETTING_SEARCH_IGNORE_CASE_TITLE: Should file search be case-insensitive -MANAGE_SETTING_SEARCH_IGNORE_CASE_TIPS: After enabling, the search will be case-insensitive -MANAGE_SETTING_TIMESTAMP_RENAME_TITLE: Rename uploaded files with timestamp - (highest priority) -MANAGE_SETTING_TIMESTAMP_RENAME_TIPS: After enabling, the uploaded file will be renamed with the timestamp -MANAGE_SETTING_RANDOM_STRING_RENAME_TITLE: Rename uploaded files with random strings - (medium priority) -MANAGE_SETTING_RANDOM_STRING_RENAME_TIPS: Random string length is 20 -MANAGE_SETTING_CUSTOM_RENAME_TITLE: Rename uploaded files with custom names - (lowest priority) -MANAGE_SETTING_CUSTOM_RENAME_TIPS: After enabling, the uploaded file will be renamed with the custom pattern +MANAGE_SETTING_ISIGNORECASE_TITLE: Should file search be case-insensitive +MANAGE_SETTING_ISIGNORECASE_TIPS: After enabling, the search will be case-insensitive +MANAGE_SETTING_TIMESTAMPRENAME_TITLE: Rename uploaded files with timestamp - (highest priority) +MANAGE_SETTING_TIMESTAMPRENAME_TIPS: After enabling, the uploaded file will be renamed with the timestamp +MANAGE_SETTING_RANDOMSTRINGRENAME_TITLE: Rename uploaded files with random strings - (medium priority) +MANAGE_SETTING_RANDOMSTRINGRENAME_TIPS: Random string length is 20 +MANAGE_SETTING_CUSTOMRENAME_TITLE: Rename uploaded files with custom names - (lowest priority) +MANAGE_SETTING_CUSTOMRENAME_TIPS: After enabling, the uploaded file will be renamed with the custom pattern MANAGE_SETTING_CUSTOM_PATTERN_TITLE: Custom rename format, placeholders can be freely combined, please refer to the table below MANAGE_SETTING_CUSTOM_PATTERN_TIPS: Please enter the custom rename format MANAGE_SETTING_CUSTOM_PATTERN_TABLE_TITLE: Placeholder @@ -430,8 +431,8 @@ MANAGE_SETTING_CHOOSE_DOWNLOAD_FOLDER_BUTTON: Choose folder MANAGE_SETTING_COPY_MESSAGE: Copied MANAGE_SETTING_CLEAR_CACHE_SUCCESS: Cleared successfully MANAGE_SETTING_CLEAR_CACHE_FAILED: Clear failed -MANAGE_SETTING_ENCODE_URL_WHEN_COPY: Encode URL when copy -MANAGE_SETTING_ENCODE_URL_WHEN_COPY_TIPS: After enabling, the URL will be encoded when copying +MANAGE_SETTING_ISENCODEURL_TITLE: Encode URL when copy +MANAGE_SETTING_ISENCODEURL_TIPS: After enabling, the URL will be encoded when copying # Empty MANAGE_NO_DATA: No data @@ -758,13 +759,18 @@ MANAGE_BUCKET_DELETE_BTN: Delete MANAGE_BUCKET_SORT_TITLE: Sort MANAGE_BUCKET_SORT_NAME: Name MANAGE_BUCKET_SORT_SIZE: Size -MANAGE_BUCKET_SORT_TYPE: Type +MANAGE_BUCKET_SORT_EXT: Type MANAGE_BUCKET_SORT_TIME: Time -MANAGE_BUCKET_SORT_SELECTED: Selected status -MANAGE_BUCKET_INIT: Init +MANAGE_BUCKET_SORT_CHECK: Selected status +MANAGE_BUCKET_SORT_INIT: Init MANAGE_BUCKET_URL_UPLOAD_DIALOG_TITLE: Please enter URL(s), support multiple URLs separated by line breaks MANAGE_BUCKET_URL_UPLOAD_DIALOG_CONFIRM: Confirm MANAGE_BUCKET_URL_UPLOAD_DIALOG_CANCEL: Cancel +MANAGE_BUCKET_URL_FORMAT_MARKDOWN: Markdown +MANAGE_BUCKET_URL_FORMAT_MARKDOWN_WITH_LINK: Markdown-link +MANAGE_BUCKET_URL_FORMAT_URL: Url +MANAGE_BUCKET_URL_FORMAT_HTML: Html +MANAGE_BUCKET_URL_FORMAT_BBCODE: BBCode MANAGE_BUCKET_URL_FORMAT_CUSTOM: Custom MANAGE_BUCKET_URL_FORMAT_PRESIGN: Presigned link MANAGE_BUCKET_FILE_INFO_TITLE: File information @@ -944,19 +950,16 @@ MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_B: Bucket name length cannot exceed MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_C: Bucket names can only contain lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. MANAGE_NEW_BUCKET_QINIU_REGION: Region MANAGE_NEW_BUCKET_QINIU_ACL_DESC: Public Access -MANAGE_NEW_BUCKET_UPYUN_NAME: Upyun -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_DESC: Bucket Name -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_PLACEHOLDER: Please enter bucket name -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_A: Bucket name cannot be empty -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_B: Bucket name length should be between 5-20 characters -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_C: Bucket names can only contain lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen. -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_DESC: Operator Name -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_PLACEHOLDER: Please enter operator name -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_RULE_MSG_A: Operator name cannot be empty -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_DESC: Password -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_PLACEHOLDER: Please enter password -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_RULE_MSG_A: Password cannot be empty - +MANAGE_NEW_BUCKET_S3PLIST_NAME: S3-Compatible +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_DESC: Bucket Name +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_PLACEHOLDER: Please enter the Bucket name +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_RULE_MSG_A: Bucket name can't be empty +MANAGE_NEW_BUCKET_S3PLIST_REGION: Region +MANAGE_NEW_BUCKET_S3PLIST_ACL_DESC: Access Control +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_RW: Public Read and Write +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_R: Public Read +MANAGE_NEW_BUCKET_S3PLIST_ACL_PRIVATE: Private +MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ: Authenticated Read # ---renderer i18n end--- # plugins diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 1a2a1c2..0ca38fe 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -2,6 +2,7 @@ LANG_DISPLAY_LABEL: 中文 ABOUT: 关于 OPEN_MAIN_WINDOW: 打开主窗口 OPEN_MINI_WINDOW: 打开mini窗口 +HIDE_MINI_WINDOW: 隐藏mini窗口 CHOOSE_DEFAULT_PICBED: 选择默认图床 OPEN_UPDATE_HELPER: 打开更新助手 RELOAD_APP: 重启应用 @@ -380,37 +381,37 @@ TIPS_GET_PLUGIN_LIST_FAILED: 获取插件列表失败 # manageSetting MANAGE_SETTING_TITLE: 管理页面设置 -MANAGE_SETTING_AUTO_FRESH_TITLE: 每次进入新目录时,是否自动刷新文件列表 -MANAGE_SETTING_AUTO_FRESH_TIPS: 仅对不分页模式有效,默认在加载过一次后自动缓存到数据库来加快下次加载速度 +MANAGE_SETTING_ISAUTOREFRESH_TITLE: 每次进入新目录时,是否自动刷新文件列表 +MANAGE_SETTING_ISAUTOREFRESH_TIPS: 仅对不分页模式有效,默认在加载过一次后自动缓存到数据库来加快下次加载速度 MANAGE_SETTING_CLEAR_CACHE_TITLE: '清空文件列表缓存数据库 已占用:' MANAGE_SETTING_CLEAR_CACHE_FREE_TITLE: '剩余可用:' MANAGE_SETTING_CLEAR_CACHE_TIPS: 清空后下次进入新目录时将会重新加载文件列表 MANAGE_SETTING_CLEAR_CACHE_PROMPT: 确定要清空文件列表缓存数据库吗? MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空 -MANAGE_SETTING_SHOW_THUMBNAIL_TITLE: 图片显示为原图而非默认文件格式图标(需要存储桶可公开访问) -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_TITLE: 文件列表默认显示方式 -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_LIST: 列表 -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_CARD: 卡片 -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TITLE: 为自定义域名开启强制HTTPS -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TIPS: 开启后, 复制链接等操作将会自动为自定义域名添加https前缀 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TITLE: 上传时保留目录结构 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TIPS: 关闭后会将所有文件展开到指定目录下 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_A: 下载 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_B: 文件 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_C: 时保留目录结构 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_D: 文件夹 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_FILE_TIPS: 开启后,下载时会保留原始目录结构 +MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 图片显示为原图而非默认文件格式图标(需要存储桶可公开访问) +MANAGE_SETTING_ISSHOWLIST_TITLE: 文件列表默认显示方式 +MANAGE_SETTING_ISSHOWLIST_ON: 列表 +MANAGE_SETTING_ISSHOWLIST_OFF: 卡片 +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TITLE: 为自定义域名开启强制HTTPS +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TIPS: 开启后, 复制链接等操作将会自动为自定义域名添加https前缀 +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TITLE: 上传时保留目录结构 +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TIPS: 关闭后会将所有文件展开到指定目录下 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_A: 下载 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_B: 文件 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_C: 时保留目录结构 +MANAGE_SETTING_ISDOWNLOADFOLDERKEEPDIRSTRUCTURE_TITLE_D: 文件夹 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TIPS: 开启后,下载时会保留原始目录结构 MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TITLE: 最大同时下载文件数(1-9999) MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TIPS: 腾讯云由于后端实现不同,该设置不生效 MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_INPUT_TIPS: 请输入最大同时下载文件数 -MANAGE_SETTING_SEARCH_IGNORE_CASE_TITLE: 文件搜索时,是否忽略大小写 -MANAGE_SETTING_SEARCH_IGNORE_CASE_TIPS: 开启后,搜索时会忽略大小写 -MANAGE_SETTING_TIMESTAMP_RENAME_TITLE: 上传文件时间戳重命名--(优先级最高) -MANAGE_SETTING_TIMESTAMP_RENAME_TIPS: 开启后,上传文件时会自动重命名为时间戳 -MANAGE_SETTING_RANDOM_STRING_RENAME_TITLE: 上传文件随机字符串重命名--(优先级中) -MANAGE_SETTING_RANDOM_STRING_RENAME_TIPS: 随机字符串长度为20 -MANAGE_SETTING_CUSTOM_RENAME_TITLE: 上传文件自定义重命名--(优先级最低) -MANAGE_SETTING_CUSTOM_RENAME_TIPS: 请填写自定义重命名格式 +MANAGE_SETTING_ISIGNORECASE_TITLE: 文件搜索时,是否忽略大小写 +MANAGE_SETTING_ISIGNORECASE_TIPS: 开启后,搜索时会忽略大小写 +MANAGE_SETTING_TIMESTAMPRENAME_TITLE: 上传文件时间戳重命名--(优先级最高) +MANAGE_SETTING_TIMESTAMPRENAME_TIPS: 开启后,上传文件时会自动重命名为时间戳 +MANAGE_SETTING_RANDOMSTRINGRENAME_TITLE: 上传文件随机字符串重命名--(优先级中) +MANAGE_SETTING_RANDOMSTRINGRENAME_TIPS: 随机字符串长度为20 +MANAGE_SETTING_CUSTOMRENAME_TITLE: 上传文件自定义重命名--(优先级最低) +MANAGE_SETTING_CUSTOMRENAME_TIPS: 请填写自定义重命名格式 MANAGE_SETTING_CUSTOM_PATTERN_TITLE: 自定义重命名格式,占位符请参考下表,可自由组合 MANAGE_SETTING_CUSTOM_PATTERN_TIPS: 请填写自定义重命名格式 MANAGE_SETTING_CUSTOM_PATTERN_TABLE_TITLE: 占位符 @@ -432,8 +433,8 @@ MANAGE_SETTING_CHOOSE_DOWNLOAD_FOLDER_BUTTON: 选择目录 MANAGE_SETTING_COPY_MESSAGE: 已复制 MANAGE_SETTING_CLEAR_CACHE_SUCCESS: 清除成功 MANAGE_SETTING_CLEAR_CACHE_FAILED: 清除失败 -MANAGE_SETTING_ENCODE_URL_WHEN_COPY: 复制链接时进行URL编码 -MANAGE_SETTING_ENCODE_URL_WHEN_COPY_TIPS: 根据平台选择是否开启 +MANAGE_SETTING_ISENCODEURL_TITLE: 复制链接时进行URL编码 +MANAGE_SETTING_ISENCODEURL_TIPS: 根据平台选择是否开启 # Empty MANAGE_NO_DATA: 暂无数据 @@ -763,13 +764,18 @@ MANAGE_BUCKET_DELETE_BTN: 删除 MANAGE_BUCKET_SORT_TITLE: 排序 MANAGE_BUCKET_SORT_NAME: 文件名 MANAGE_BUCKET_SORT_SIZE: 大小 -MANAGE_BUCKET_SORT_TYPE: 类型 +MANAGE_BUCKET_SORT_EXT: 类型 MANAGE_BUCKET_SORT_TIME: 时间 -MANAGE_BUCKET_SORT_SELECTED: 选中状态 -MANAGE_BUCKET_INIT: 初始化 +MANAGE_BUCKET_SORT_CHECK: 选中状态 +MANAGE_BUCKET_SORT_INIT: 初始化 MANAGE_BUCKET_URL_UPLOAD_DIALOG_TITLE: 请输入URL,支持多个URL,以换行分隔 MANAGE_BUCKET_URL_UPLOAD_DIALOG_CONFIRM: 确定 MANAGE_BUCKET_URL_UPLOAD_DIALOG_CANCEL: 取消 +MANAGE_BUCKET_URL_FORMAT_MARKDOWN: Markdown +MANAGE_BUCKET_URL_FORMAT_MARKDOWN_WITH_LINK: Markdown-link +MANAGE_BUCKET_URL_FORMAT_URL: Url +MANAGE_BUCKET_URL_FORMAT_HTML: Html +MANAGE_BUCKET_URL_FORMAT_BBCODE: BBCode MANAGE_BUCKET_URL_FORMAT_CUSTOM: 自定义 MANAGE_BUCKET_URL_FORMAT_PRESIGN: 预签名链接 MANAGE_BUCKET_FILE_INFO_TITLE: 文件信息 @@ -949,19 +955,16 @@ MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_B: Bucket名称长度不能超过63 MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_C: Bucket名称只能包含小写字母、数字和中划线,且不能以中划线开头和结尾 MANAGE_NEW_BUCKET_QINIU_REGION: 区域 MANAGE_NEW_BUCKET_QINIU_ACL_DESC: 公开访问 -MANAGE_NEW_BUCKET_UPYUN_NAME: 又拍云 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_DESC: Bucket名 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_PLACEHOLDER: 请输入Bucket名 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_A: Bucket名不能为空 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_B: Bucket名称长度为5-20个字符 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_C: Bucket名称只能包含小写字母、数字和中划线,且不能以中划线开头和结尾 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_DESC: 操作员 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_PLACEHOLDER: 请输入操作员 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_RULE_MSG_A: 操作员不能为空 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_DESC: 密码 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_PLACEHOLDER: 请输入密码 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_RULE_MSG_A: 密码不能为空 - +MANAGE_NEW_BUCKET_S3PLIST_NAME: S3兼容云 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_DESC: Bucket名 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_PLACEHOLDER: 请输入Bucket名 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_RULE_MSG_A: Bucket名不能为空 +MANAGE_NEW_BUCKET_S3PLIST_REGION: 区域 +MANAGE_NEW_BUCKET_S3PLIST_ACL_DESC: 访问权限 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_RW: 公共读写 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_R: 公共读 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PRIVATE: 私有 +MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ: 授权读 # ---renderer i18n end--- diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index 971e944..2e0be82 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -2,6 +2,7 @@ LANG_DISPLAY_LABEL: 繁體中文 ABOUT: 關於 OPEN_MAIN_WINDOW: 打開主視窗 OPEN_MINI_WINDOW: 打開mini視窗 +HIDE_MINI_WINDOW: 隱藏mini視窗 CHOOSE_DEFAULT_PICBED: 選擇預設圖床 OPEN_UPDATE_HELPER: 開啟更新助手 RELOAD_APP: 重啟程式 @@ -48,13 +49,13 @@ TOOLBOX_CHECK_PROXY_SUCCESS_TIPS: 代理設置正常 TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS: 無代理設置 TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_CORRECT: 代理設置不正確 TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_WORKING: 代理設置不可用 -TOOLBOX_CHECK_CLIPBOARD_FILE_PATH_TIPS: 剪貼板圖片臨時文件夾路徑是:${path} +TOOLBOX_CHECK_CLIPBOARD_FILE_PATH_TIPS: 剪貼板圖片臨時文件夾路徑是:${path} TOOLBOX_CHECK_CLIPBOARD_FILE_PATH_NOT_EXIST_TIPS: 剪貼板圖片臨時文件夾不存在:${path} TOOLBOX_CHECK_CLIPBOARD_FILE_PATH_ERROR_TIPS: 請自行創建文件夾:${path} MANUAL_PAGE_OPEN_TIP: 請選擇打開方式 MANUAL_PAGE_OPEN_TIP_TITLE: Tips MANUAL_PAGE_OPEN_BY_BROWSER: 瀏覽器 -MANUAL_PAGE_OPEN_BY_BUILD_IN: 內置窗口s +MANUAL_PAGE_OPEN_BY_BUILD_IN: 內置窗口 MANUAL_PAGE_OPEN_SETTING_TIP: 選擇打開手冊方式 # ---renderer i18n begin--- @@ -378,37 +379,37 @@ TIPS_GET_PLUGIN_LIST_FAILED: 取得插件列表失敗 # manageSetting MANAGE_SETTING_TITLE: 管理設定 -MANAGE_SETTING_AUTO_FRESH_TITLE: 每次進入新目錄時,是否自動重新整理檔案列表 -MANAGE_SETTING_AUTO_FRESH_TIPS: 僅對不分頁模式有效,預設會在載入後自動快取至資料庫以提升下次載入速度 +MANAGE_SETTING_ISAUTOREFRESH_TITLE: 每次進入新目錄時,是否自動重新整理檔案列表 +MANAGE_SETTING_ISAUTOREFRESH_TIPS: 僅對不分頁模式有效,預設會在載入後自動快取至資料庫以提升下次載入速度 MANAGE_SETTING_CLEAR_CACHE_TITLE: '清空檔案列表快取資料庫 已佔用:' MANAGE_SETTING_CLEAR_CACHE_FREE_TITLE: '剩餘可用:' MANAGE_SETTING_CLEAR_CACHE_TIPS: 清空後下次進入新目錄時將會重新載入檔案列表 MANAGE_SETTING_CLEAR_CACHE_PROMPT: 確定要清空檔案列表快取資料庫嗎? MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空 -MANAGE_SETTING_SHOW_THUMBNAIL_TITLE: 顯示圖片的原始圖像而非預設的檔案格式圖示(需要存儲桶公開訪問權限) -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_TITLE: 檔案列表預設顯示方式 -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_LIST: 列表 -MANAGE_SETTING_SHOW_FILE_LIST_TYPE_CARD: 卡片 -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TITLE: 自定義域名啟用強制 HTTPS -MANAGE_SETTING_FORCE_CUSTOM_URL_HTTPS_TIPS: 開啟後,複製鏈結等操作將會自動為自定義域名添加 HTTPS 前綴 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TITLE: 保留上傳時的目錄結構 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_UPLOAD_TIPS: 停用後,所有文件將會展開到指定目錄下 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_A: 下載 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_B: 文件 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_C: 時保留目錄結構 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_TITLE_D: 目錄 -MANAGE_SETTING_KEEP_FOLDER_STRUCTURE_DOWNLOAD_FILE_TIPS: 啟用後,下載時會保留原始目錄結構 +MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 顯示圖片的原始圖像而非預設的檔案格式圖示(需要存儲桶公開訪問權限) +MANAGE_SETTING_ISSHOWLIST_TITLE: 檔案列表預設顯示方式 +MANAGE_SETTING_ISSHOWLIST_ON: 列表 +MANAGE_SETTING_ISSHOWLIST_OFF: 卡片 +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TITLE: 自定義域名啟用強制 HTTPS +MANAGE_SETTING_ISFORCECUSTOMURLHTTPS_TIPS: 開啟後,複製鏈結等操作將會自動為自定義域名添加 HTTPS 前綴 +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TITLE: 保留上傳時的目錄結構 +MANAGE_SETTING_ISUPLOADKEEPDIRSTRUCTURE_TIPS: 停用後,所有文件將會展開到指定目錄下 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_A: 下載 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_B: 文件 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TITLE_C: 時保留目錄結構 +MANAGE_SETTING_ISDOWNLOADFOLDERKEEPDIRSTRUCTURE_TITLE_D: 目錄 +MANAGE_SETTING_ISDOWNLOADFILEKEEPDIRSTRUCTURE_TIPS: 啟用後,下載時會保留原始目錄結構 MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TITLE: 最大同時下載檔案數量(1-9999) MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_TIPS: 由於後端實現方式不同,此設定在腾讯云上不生效 MANAGE_SETTING_MAX_DOWNLOAD_FILE_SIZE_INPUT_TIPS: 請輸入最大同時下載檔案數量 -MANAGE_SETTING_SEARCH_IGNORE_CASE_TITLE: 搜尋檔案時,是否忽略大小寫 -MANAGE_SETTING_SEARCH_IGNORE_CASE_TIPS: 啟用後,搜尋時將會忽略大小寫 -MANAGE_SETTING_TIMESTAMP_RENAME_TITLE: 上傳檔案時間戳重新命名--(最高優先級) -MANAGE_SETTING_TIMESTAMP_RENAME_TIPS: 啟用後,上傳檔案時將會使用時間戳重新命名 -MANAGE_SETTING_RANDOM_STRING_RENAME_TITLE: 上傳檔案隨機字符串重新命名--(中優先級) -MANAGE_SETTING_RANDOM_STRING_RENAME_TIPS: 隨機字符串長度為20 -MANAGE_SETTING_CUSTOM_RENAME_TITLE: 上傳檔案自定義重新命名--(最低優先級) -MANAGE_SETTING_CUSTOM_RENAME_TIPS: 啟用後,上傳檔案時將會使用自定義重新命名 +MANAGE_SETTING_ISIGNORECASE_TITLE: 搜尋檔案時,是否忽略大小寫 +MANAGE_SETTING_ISIGNORECASE_TIPS: 啟用後,搜尋時將會忽略大小寫 +MANAGE_SETTING_TIMESTAMPRENAME_TITLE: 上傳檔案時間戳重新命名--(最高優先級) +MANAGE_SETTING_TIMESTAMPRENAME_TIPS: 啟用後,上傳檔案時將會使用時間戳重新命名 +MANAGE_SETTING_RANDOMSTRINGRENAME_TITLE: 上傳檔案隨機字符串重新命名--(中優先級) +MANAGE_SETTING_RANDOMSTRINGRENAME_TIPS: 隨機字符串長度為20 +MANAGE_SETTING_CUSTOMRENAME_TITLE: 上傳檔案自定義重新命名--(最低優先級) +MANAGE_SETTING_CUSTOMRENAME_TIPS: 啟用後,上傳檔案時將會使用自定義重新命名 MANAGE_SETTING_CUSTOM_PATTERN_TITLE: 自訂重新命名格式,占位符請參考下表,可自由組合 MANAGE_SETTING_CUSTOM_PATTERN_TIPS: 請輸入自訂重新命名格式 MANAGE_SETTING_CUSTOM_PATTERN_TABLE_TITLE: 占位符 @@ -430,8 +431,8 @@ MANAGE_SETTING_CHOOSE_DOWNLOAD_FOLDER_BUTTON: 選擇目錄 MANAGE_SETTING_COPY_MESSAGE: 已複製 MANAGE_SETTING_CLEAR_CACHE_SUCCESS: 清除成功 MANAGE_SETTING_CLEAR_CACHE_FAILED: 清除失敗 -MANAGE_SETTING_ENCODE_URL_WHEN_COPY: 複製鏈結時編碼 -MANAGE_SETTING_ENCODE_URL_WHEN_COPY_TIPS: 啟用後,複製鏈結時將會編碼 +MANAGE_SETTING_ISENCODEURL_TITLE: 複製鏈結時編碼 +MANAGE_SETTING_ISENCODEURL_TIPS: 啟用後,複製鏈結時將會編碼 # Empty MANAGE_NO_DATA: 暫無數據 @@ -758,13 +759,18 @@ MANAGE_BUCKET_DELETE_BTN: 刪除 MANAGE_BUCKET_SORT_TITLE: 排序 MANAGE_BUCKET_SORT_NAME: 檔案名稱 MANAGE_BUCKET_SORT_SIZE: 大小 -MANAGE_BUCKET_SORT_TYPE: 類型 +MANAGE_BUCKET_SORT_EXT: 類型 MANAGE_BUCKET_SORT_TIME: 時間 -MANAGE_BUCKET_SORT_SELECTED: 選取狀態 -MANAGE_BUCKET_INIT: 初始化 +MANAGE_BUCKET_SORT_CHECK: 選取狀態 +MANAGE_BUCKET_SORT_INIT: 初始化 MANAGE_BUCKET_URL_UPLOAD_DIALOG_TITLE: 請輸入 URL,支援多個 URL,以換行分隔 MANAGE_BUCKET_URL_UPLOAD_DIALOG_CONFIRM: 確定 MANAGE_BUCKET_URL_UPLOAD_DIALOG_CANCEL: 取消 +MANAGE_BUCKET_URL_FORMAT_MARKDOWN: Markdown +MANAGE_BUCKET_URL_FORMAT_MARKDOWN_WITH_LINK: Markdown-link +MANAGE_BUCKET_URL_FORMAT_URL: Url +MANAGE_BUCKET_URL_FORMAT_HTML: Html +MANAGE_BUCKET_URL_FORMAT_BBCODE: BBCode MANAGE_BUCKET_URL_FORMAT_CUSTOM: 自訂 MANAGE_BUCKET_URL_FORMAT_PRESIGN: 預簽名連結 MANAGE_BUCKET_FILE_INFO_TITLE: 檔案資訊 @@ -944,18 +950,16 @@ MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_B: Bucket名稱長度不能超過63 MANAGE_NEW_BUCKET_QINIU_BUCKETNAME_RULE_MSG_C: Bucket名稱只能包含小寫字母、數字和中橫線,且不能以中橫線開頭和結尾 MANAGE_NEW_BUCKET_QINIU_REGION: 區域 MANAGE_NEW_BUCKET_QINIU_ACL_DESC: 公開訪問 -MANAGE_NEW_BUCKET_UPYUN_NAME: 又拍雲 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_DESC: Bucket名稱 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_PLACEHOLDER: 請輸入Bucket名稱 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_A: Bucket名稱不能為空 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_B: Bucket名稱長度為5-20個字符 -MANAGE_NEW_BUCKET_UPYUN_BUCKETNAME_RULE_MSG_C: Bucket名稱只能包含小寫字母、數字和中橫線,且不能以中橫線開頭和結尾 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_DESC: 操作員 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_PLACEHOLDER: 請輸入操作員 -MANAGE_NEW_BUCKET_UPYUN_OPERATORNAME_RULE_MSG_A: 操作員不能為空 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_DESC: 密碼 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_PLACEHOLDER: 請輸入密碼 -MANAGE_NEW_BUCKET_UPYUN_PASSWORD_RULE_MSG_A: 密碼不能為空 +MANAGE_NEW_BUCKET_S3PLIST_NAME: S3兼容雲 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_DESC: Bucket名稱 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_PLACEHOLDER: 請輸入Bucket名稱 +MANAGE_NEW_BUCKET_S3PLIST_BUCKETNAME_RULE_MSG_A: Bucket名稱不能為空 +MANAGE_NEW_BUCKET_S3PLIST_REGION: 區域 +MANAGE_NEW_BUCKET_S3PLIST_ACL_DESC: 訪問權限 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_RW: 公共讀寫 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PUBLIC_R: 公共讀 +MANAGE_NEW_BUCKET_S3PLIST_ACL_PRIVATE: 私有 +MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ: 授權讀 # ---renderer i18n end--- # plugins diff --git a/src/background.ts b/src/background.ts index c8b4acb..ebba2cb 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,3 +1,3 @@ -import { bootstrap } from '~/main/lifeCycle' +import { bootstrap } from '~/lifeCycle' bootstrap.launchApp() diff --git a/src/main.ts b/src/main.ts index 332b7bd..911de3d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,81 +1,45 @@ - -// Vue 相关 -import { createApp } from 'vue' -import App from './renderer/App.vue' -import router from './renderer/router' +import { webFrame } from 'electron' import ElementUI from 'element-plus' import 'element-plus/dist/index.css' -import VueLazyLoad from 'vue3-lazyload' -import { initTalkingData } from './renderer/utils/analytic' -import vue3PhotoPreview from 'vue3-photo-preview' -import 'vue3-photo-preview/dist/index.css' -import VueVideoPlayer from '@videojs-player/vue' -// Electron 相关 -import { webFrame } from 'electron' - -// Axios -import axios from 'axios' - -// Mixins -import { mainMixin } from './renderer/utils/mainMixin' -import { dragMixin } from '@/utils/mixin' - -// 数据库 -import db from './renderer/utils/db' - -// 国际化 -import { i18nManager, T } from './renderer/i18n/index' - -// 工具函数 -import { getConfig, saveConfig, sendToMain, triggerRPC } from '@/utils/dataSender' - -// 状态管理 -import { store } from '@/store' import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' -// 代码高亮 -import 'highlight.js/styles/atom-one-dark.css' -import hljsVuePlugin from '@highlightjs/vue-plugin' -import hljsCommon from 'highlight.js/lib/common' +import { createApp } from 'vue' +import VueLazyLoad from 'vue3-lazyload' + +import vue3PhotoPreview from 'vue3-photo-preview' +import 'vue3-photo-preview/dist/index.css' + +import VueVideoPlayer from '@videojs-player/vue' import 'video.js/dist/video-js.css' +import 'highlight.js/styles/stackoverflow-light.css' +import hljsVuePlugin from '@highlightjs/vue-plugin' +import 'highlight.js/lib/common' + +import App from '@/App.vue' +import router from '@/router' +import { sendRPC, sendToMain, triggerRPC } from '@/utils/common' +import db from '@/utils/db' +import { T } from '@/i18n/index' +import { store } from '@/store' +import { initTalkingData } from '@/utils/analytic' +import { dragMixin } from '@/utils/mixin' + webFrame.setVisualZoomLevelLimits(1, 1) const app = createApp(App) -app.config.globalProperties.$builtInPicBed = [ - 'smms', - 'imgur', - 'qiniu', - 'tcyun', - 'upyun', - 'aliyun', - 'github', - 'webdavplist', - 'local', - 'sftpplist', - 'telegraphplist', - 'piclist', - 'lskyplist', - 'aws-s3-plist' -] - app.config.globalProperties.$$db = db -app.config.globalProperties.$http = axios app.config.globalProperties.$T = T -app.config.globalProperties.$i18n = i18nManager -app.config.globalProperties.getConfig = getConfig app.config.globalProperties.triggerRPC = triggerRPC -app.config.globalProperties.saveConfig = saveConfig +app.config.globalProperties.sendRPC = sendRPC app.config.globalProperties.sendToMain = sendToMain -app.mixin(mainMixin) app.mixin(dragMixin) const pinia = createPinia() pinia.use(piniaPluginPersistedstate) - app.use(VueLazyLoad, { error: `file://${__static.replace(/\\/g, '/')}/unknown-file-type.svg` }) @@ -84,9 +48,7 @@ app.use(router) app.use(store) app.use(vue3PhotoPreview) app.use(pinia) -console.log(hljsCommon.highlightAuto('

Highlight.js has been registered successfully!

').value) app.use(hljsVuePlugin) app.use(VueVideoPlayer) app.mount('#app') - initTalkingData() diff --git a/src/main/apis/app/remoteNotice/index.ts b/src/main/apis/app/remoteNotice/index.ts index cdf373a..ab6ba2f 100644 --- a/src/main/apis/app/remoteNotice/index.ts +++ b/src/main/apis/app/remoteNotice/index.ts @@ -1,25 +1,22 @@ -// External dependencies import axios from 'axios' +import { + app, + clipboard, + dialog, + shell +} from 'electron' import fs from 'fs-extra' import path from 'path' +import { gte, lte } from 'semver' -// Electron modules -import { app, clipboard, dialog, shell } from 'electron' - -// Custom modules and utilities -import windowManager from '../window/windowManager' -import { showNotification } from '~/main/utils/common' - -// Custom types/enums +import windowManager from 'apis/app/window/windowManager' +import { showNotification } from '~/utils/common' import { IRemoteNoticeActionType, IRemoteNoticeTriggerCount, IRemoteNoticeTriggerHook } from '#/types/enum' -// External utility functions -import { gte, lte } from 'semver' - // for test const REMOTE_NOTICE_URL = 'https://release.piclist.cn/remote-notice.json' diff --git a/src/main/apis/app/shortKey/shortKeyHandler.ts b/src/main/apis/app/shortKey/shortKeyHandler.ts index 69831fd..25c983e 100644 --- a/src/main/apis/app/shortKey/shortKeyHandler.ts +++ b/src/main/apis/app/shortKey/shortKeyHandler.ts @@ -1,24 +1,18 @@ -// External dependencies + import { globalShortcut } from 'electron' +import shortKeyService from 'apis/app/shortKey/shortKeyService' +import GuiApi from 'apis/gui' + import bus from '@core/bus' -import db from '~/main/apis/core/datastore' +import db from '@core/datastore' import logger from '@core/picgo/logger' import picgo from '@core/picgo' -// Electron modules - -// Custom utilities and modules -import GuiApi from '../../gui' -import shortKeyService from './shortKeyService' - -// Custom types/enums - -// External utility functions import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants' -import { configPaths } from '~/universal/utils/configPaths' +import { configPaths } from '#/utils/configPaths' class ShortKeyHandler { private isInModifiedMode: boolean = false diff --git a/src/main/apis/app/system/index.ts b/src/main/apis/app/system/index.ts index 5123c97..f315bac 100644 --- a/src/main/apis/app/system/index.ts +++ b/src/main/apis/app/system/index.ts @@ -1,52 +1,45 @@ -// External dependencies +import { + app, + clipboard, + dialog, + Menu, + MenuItem, + MenuItemConstructorOptions, + nativeTheme, + Notification, + Tray +} from 'electron' import fs from 'fs-extra' import { cloneDeep } from 'lodash' -// Electron modules -import { - app, - Menu, - Tray, - dialog, - clipboard, - Notification, - screen, - nativeTheme -} from 'electron' +import db, { GalleryDB } from '@core/datastore' +import picgo from '@core/picgo' -// Custom utilities and modules import uploader from 'apis/app/uploader' -import db, { GalleryDB } from '~/main/apis/core/datastore' +import { uploadClipboardFiles } from 'apis/app/uploader/apis' import windowManager from 'apis/app/window/windowManager' + +import { buildPicBedListMenu } from '~/events/remotes/menu' +import { T } from '~/i18n' +import clipboardPoll from '~/utils/clipboardPoll' +import { ensureFilePath, handleCopyUrl, setTray, tray } from '~/utils/common' +import { isMacOSVersionGreaterThanOrEqualTo } from '~/utils/getMacOSVersion' +import pasteTemplate from '~/utils/pasteTemplate' + +import { configPaths } from '#/utils/configPaths' import { IPasteStyle, IWindowList } from '#/types/enum' -import pasteTemplate from '~/main/utils/pasteTemplate' + import pkg from 'root/package.json' -import { ensureFilePath, handleCopyUrl } from '~/main/utils/common' -import { T } from '~/main/i18n' -import { isMacOSVersionGreaterThanOrEqualTo } from '~/main/utils/getMacOSVersion' -import { buildPicBedListMenu } from '~/main/events/remotes/menu' -import clipboardPoll from '~/main/utils/clipboardPoll' -import picgo from '../../core/picgo' -import { uploadClipboardFiles } from '../uploader/apis' -import { configPaths } from '~/universal/utils/configPaths' +import { hideMiniWindow, openMainWindow, openMiniWindow } from '~/utils/windowHelper' let contextMenu: Menu | null -let tray: Tray | null export function setDockMenu () { const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false const dockMenu = Menu.buildFromTemplate([ { label: T('OPEN_MAIN_WINDOW'), - click () { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - settingWindow!.show() - settingWindow!.focus() - if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } + click: openMainWindow }, { label: T('START_WATCH_CLIPBOARD'), @@ -59,7 +52,7 @@ export function setDockMenu () { }) setDockMenu() }, - enabled: !isListeningClipboard + visible: !isListeningClipboard }, { label: T('STOP_WATCH_CLIPBOARD'), @@ -69,7 +62,7 @@ export function setDockMenu () { clipboardPoll.removeAllListeners() setDockMenu() }, - enabled: isListeningClipboard + visible: isListeningClipboard } ]) app.dock.setMenu(dockMenu) @@ -81,59 +74,27 @@ export function createMenu () { { label: 'PicList', submenu: [ - { - label: T('OPEN_MAIN_WINDOW'), - click () { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false - settingWindow!.show() - settingWindow!.focus() - if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } - }, - { - label: T('RELOAD_APP'), - click () { - app.relaunch() - app.exit(0) - } - } + { label: T('OPEN_MAIN_WINDOW'), click: openMainWindow }, + { label: T('RELOAD_APP'), click () { app.relaunch(); app.exit(0) } } ] }, - { - label: T('CHOOSE_DEFAULT_PICBED'), - type: 'submenu', - // @ts-ignore - submenu - }, + { label: T('CHOOSE_DEFAULT_PICBED'), type: 'submenu', submenu }, { label: 'Edit', - // @ts-ignore submenu: [ - // @ts-ignore - { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' }, - // @ts-ignore - { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' }, + { label: 'Undo', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, + { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', role: 'redo' }, { type: 'separator' }, - // @ts-ignore - { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' }, - // @ts-ignore - { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' }, - // @ts-ignore - { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' }, - // @ts-ignore - { label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' } + { label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' }, + { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, + { label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' }, + { label: 'Select All', accelerator: 'CmdOrCtrl+A', role: 'selectAll' } ] }, { label: T('QUIT'), submenu: [ - { - label: T('QUIT'), - role: 'quit' - } + { label: T('QUIT'), role: 'quit' } ] } ]) @@ -143,94 +104,39 @@ 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 startWatchClipboard = () => { + db.set(configPaths.settings.isListeningClipboard, true) + ClipboardWatcher.startListening() + ClipboardWatcher.on('change', () => { + picgo.log.info('clipboard changed') + uploadClipboardFiles() + }) + createContextMenu() + } + + const stopWatchClipboard = () => { + db.set(configPaths.settings.isListeningClipboard, false) + ClipboardWatcher.stopListening() + ClipboardWatcher.removeAllListeners() + createContextMenu() + } + if (process.platform === 'darwin' || process.platform === 'win32') { const submenu = buildPicBedListMenu() - const template = [ - { - label: T('OPEN_MAIN_WINDOW'), - click () { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false - settingWindow!.show() - settingWindow!.focus() - if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } - }, - { - label: T('CHOOSE_DEFAULT_PICBED'), - type: 'submenu', - // @ts-ignore - submenu - }, - { - label: T('START_WATCH_CLIPBOARD'), - click () { - db.set(configPaths.settings.isListeningClipboard, true) - ClipboardWatcher.startListening() - ClipboardWatcher.on('change', () => { - picgo.log.info('clipboard changed') - uploadClipboardFiles() - }) - createContextMenu() - }, - enabled: !isListeningClipboard - }, - { - label: T('STOP_WATCH_CLIPBOARD'), - click () { - db.set(configPaths.settings.isListeningClipboard, false) - ClipboardWatcher.stopListening() - ClipboardWatcher.removeAllListeners() - createContextMenu() - }, - enabled: isListeningClipboard - }, - { - label: T('RELOAD_APP'), - click () { - app.relaunch() - app.exit(0) - } - }, - // @ts-ignore - { - role: 'quit', - label: T('QUIT') - } - ] as any + 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('QUIT'), role: 'quit' } + ] if (process.platform === 'win32') { template.splice(2, 0, - { - label: T('OPEN_MINI_WINDOW'), - click () { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! - miniWindow.removeAllListeners() - if (db.get(configPaths.settings.miniWindowOntop)) { - miniWindow.setAlwaysOnTop(true) - } - const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get(configPaths.settings.miniWindowPosition) - if (lastPosition) { - miniWindow.setPosition(lastPosition[0], lastPosition[1]) - } else { - miniWindow.setPosition(width - 100, height - 100) - } - const setPositionFunc = () => { - const position = miniWindow.getPosition() - db.set(configPaths.settings.miniWindowPosition, position) - } - miniWindow.on('close', setPositionFunc) - miniWindow.on('move', setPositionFunc) - miniWindow.show() - miniWindow.focus() - const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false - if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { - windowManager.get(IWindowList.SETTING_WINDOW)!.hide() - } - } - } + { label: T('OPEN_MINI_WINDOW'), click () { openMiniWindow(false) }, visible: !isMiniWindowVisible }, + { label: T('HIDE_MINI_WINDOW'), click: hideMiniWindow, visible: isMiniWindowVisible } ) } contextMenu = Menu.buildFromTemplate(template) @@ -243,70 +149,11 @@ export function createContextMenu () { // 目前的实现无法正常工作 contextMenu = Menu.buildFromTemplate([ - { - label: T('OPEN_MAIN_WINDOW'), - click () { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false - settingWindow!.show() - settingWindow!.focus() - if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } - }, - { - label: T('OPEN_MINI_WINDOW'), - click () { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! - miniWindow.removeAllListeners() - if (db.get(configPaths.settings.miniWindowOntop)) { - miniWindow.setAlwaysOnTop(true) - } - const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get(configPaths.settings.miniWindowPosition) - if (lastPosition) { - miniWindow.setPosition(lastPosition[0], lastPosition[1]) - } else { - miniWindow.setPosition(width - 100, height - 100) - } - const setPositionFunc = () => { - const position = miniWindow.getPosition() - db.set(configPaths.settings.miniWindowPosition, position) - } - miniWindow.on('close', setPositionFunc) - miniWindow.on('move', setPositionFunc) - miniWindow.show() - miniWindow.focus() - const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false - if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { - windowManager.get(IWindowList.SETTING_WINDOW)!.hide() - } - } - }, - { - label: T('START_WATCH_CLIPBOARD'), - click () { - db.set(configPaths.settings.isListeningClipboard, true) - ClipboardWatcher.startListening() - ClipboardWatcher.on('change', () => { - picgo.log.info('clipboard changed') - uploadClipboardFiles() - }) - createContextMenu() - }, - enabled: !isListeningClipboard - }, - { - label: T('STOP_WATCH_CLIPBOARD'), - click () { - db.set(configPaths.settings.isListeningClipboard, false) - ClipboardWatcher.stopListening() - ClipboardWatcher.removeAllListeners() - createContextMenu() - }, - enabled: isListeningClipboard - }, + { 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('ABOUT'), click () { @@ -318,11 +165,7 @@ export function createContextMenu () { }) } }, - // @ts-ignore - { - role: 'quit', - label: T('QUIT') - } + { label: T('QUIT'), role: 'quit' } ]) } } @@ -336,9 +179,10 @@ const getTrayIcon = () => { } } -export function createTray () { +export function createTray (tooltip: string) { const menubarPic = getTrayIcon() - tray = new Tray(menubarPic) + setTray(new Tray(menubarPic)) + tray.setToolTip(tooltip) // click事件在Mac和Windows上可以触发(在Ubuntu上无法触发,Unity不支持) if (process.platform === 'darwin' || process.platform === 'win32') { tray.on('right-click', () => { @@ -348,7 +192,7 @@ export function createTray () { createContextMenu() tray!.popUpContextMenu(contextMenu!) }) - tray.on('click', (event, bounds) => { + tray.on('click', (_, bounds) => { if (process.platform === 'darwin') { toggleWindow(bounds) setTimeout(async () => { @@ -412,7 +256,7 @@ export function createTray () { // drop-files only be supported in macOS // so the tray window must be available - tray.on('drop-files', async (event: Event, files: string[]) => { + tray.on('drop-files', async (_: Event, files: string[]) => { const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN const rawInput = cloneDeep(files) const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! diff --git a/src/main/apis/app/uploader/apis.ts b/src/main/apis/app/uploader/apis.ts index d9e8b45..d24f3cb 100644 --- a/src/main/apis/app/uploader/apis.ts +++ b/src/main/apis/app/uploader/apis.ts @@ -1,30 +1,22 @@ -// External dependencies -import fs from 'fs-extra' -import { cloneDeep } from 'lodash' - -// Electron modules import { Notification, WebContents } from 'electron' +import fs from 'fs-extra' +import { cloneDeep } from 'lodash' -// Custom utilities and modules -import windowManager from 'apis/app/window/windowManager' -import pasteTemplate from '~/main/utils/pasteTemplate' -import db, { GalleryDB } from '~/main/apis/core/datastore' -import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/main/utils/common' -import { T } from '~/main/i18n/index' -import ALLApi from '@/apis/allApi' import picgo from '@core/picgo' -import GuiApi from '../../gui' -import uploader from '.' +import db, { GalleryDB } from '@core/datastore' + +import uploader from 'apis/app/uploader' +import windowManager from 'apis/app/window/windowManager' + +import { T } from '~/i18n/index' +import { handleCopyUrl, handleUrlEncodeWithSetting } from '~/utils/common' +import pasteTemplate from '~/utils/pasteTemplate' + import { IPasteStyle, IWindowList } from '#/types/enum' -import { picBedsCanbeDeleted } from '#/utils/static' -import path from 'path' -import SSHClient from '~/main/utils/sshClient' -import { ISftpPlistConfig } from 'piclist' -import { getRawData } from '~/renderer/utils/common' -import { configPaths } from '~/universal/utils/configPaths' +import { configPaths } from '#/utils/configPaths' const handleClipboardUploading = async (): Promise => { const useBuiltinClipboard = db.get(configPaths.settings.useBuiltinClipboard) === undefined ? true : !!db.get(configPaths.settings.useBuiltinClipboard) @@ -129,62 +121,3 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW return [] } } - -async function deleteSFTPFile (config: ISftpPlistConfig, fileName: string) { - try { - const client = SSHClient.instance - await client.connect(config) - const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/') - const remote = path.join(uploadPath, fileName) - const deleteResult = await client.deleteFileSFTP(config, remote) - client.close() - return deleteResult - } catch (err: any) { - picgo.log.error(err) - return false - } -} - -export const deleteChoosedFiles = async (list: ImgInfo[]): Promise => { - const result = [] - for (const item of list) { - if (item.id) { - try { - const dbStore = GalleryDB.getInstance() - const file = await dbStore.getById(item.id) - await dbStore.removeById(item.id) - if (await picgo.getConfig(configPaths.settings.deleteCloudFile)) { - if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) { - 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') - }) - notification.show() - } - if (item.type === 'sftpplist') { - const { fileName, config } = item - setTimeout(() => { - deleteSFTPFile(getRawData(config), fileName || '').then(noteFunc) - }, 0) - } else { - setTimeout(() => { - ALLApi.delete(item).then(noteFunc) - }, 0) - } - } - } - setTimeout(() => { - picgo.emit('remove', [file], GuiApi.getInstance()) - }, 500) - result.push(true) - } catch (e) { - result.push(false) - } - } - } - if (windowManager.has(IWindowList.SETTING_WINDOW)) { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') - } - return result -} diff --git a/src/main/apis/app/uploader/index.ts b/src/main/apis/app/uploader/index.ts index 33c9316..845c844 100644 --- a/src/main/apis/app/uploader/index.ts +++ b/src/main/apis/app/uploader/index.ts @@ -1,39 +1,34 @@ -// External dependencies import dayjs from 'dayjs' +import { + BrowserWindow, + clipboard, + ipcMain, + Notification, + WebContents +} from 'electron' +import fs from 'fs-extra' import util from 'util' import path from 'path' -import writeFile from 'write-file-atomic' -import fse from 'fs-extra' - -// Electron modules -import { - Notification, - BrowserWindow, - ipcMain, - WebContents, - clipboard -} from 'electron' - -// Custom utilities and modules -import picgo from '@core/picgo' -import db from '~/main/apis/core/datastore' -import windowManager from 'apis/app/window/windowManager' -import { showNotification, getClipboardFilePath, calcDurationRange } from '~/main/utils/common' -import logger from '@core/picgo/logger' -import { T } from '~/main/i18n' -import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' - -// Custom types/enums -import { IWindowList } from '#/types/enum' - -// External utility functions import { IPicGo } from 'piclist' +import writeFile from 'write-file-atomic' + +import windowManager from 'apis/app/window/windowManager' + +import db from '@core/datastore' +import picgo from '@core/picgo' +import logger from '@core/picgo/logger' + +import { T } from '~/i18n' +import { showNotification, getClipboardFilePath, calcDurationRange } from '~/utils/common' + import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME, TALKING_DATA_EVENT -} from '~/universal/events/constants' -import { configPaths } from '~/universal/utils/configPaths' +} 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 => { return new Promise((resolve) => { @@ -66,21 +61,22 @@ const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) => class Uploader { private webContents: WebContents | null = null - // private uploading: boolean = false + constructor () { this.init() } init () { - picgo.on('notification', (message: Electron.NotificationConstructorOptions | undefined) => { + picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: Electron.NotificationConstructorOptions | undefined) => { const notification = new Notification(message) notification.show() }) - picgo.on('uploadProgress', (progress: any) => { + picgo.on(ICOREBuildInEvent.UPLOAD_PROGRESS, (progress: any) => { this.webContents?.send('uploadProgress', progress) }) - picgo.on('beforeTransform', () => { + + picgo.on(ICOREBuildInEvent.BEFORE_TRANSFORM, () => { if (db.get(configPaths.settings.uploadNotification)) { const notification = new Notification({ title: T('UPLOAD_PROGRESS'), @@ -89,6 +85,7 @@ class Uploader { notification.show() } }) + picgo.helper.beforeUploadPlugins.register('renameFn', { handle: async (ctx: IPicGo) => { const rename = db.get(configPaths.settings.rename) @@ -155,7 +152,7 @@ class Uploader { return false } finally { if (filePath) { - fse.unlink(filePath) + fs.remove(filePath) } } } diff --git a/src/main/apis/app/window/constants.ts b/src/main/apis/app/window/constants.ts index c484e9a..913d3fe 100644 --- a/src/main/apis/app/window/constants.ts +++ b/src/main/apis/app/window/constants.ts @@ -1,12 +1,8 @@ const isDevelopment = process.env.NODE_ENV !== 'production' -export const TRAY_WINDOW_URL = isDevelopment - ? (process.env.WEBPACK_DEV_SERVER_URL as string) - : 'picgo://./index.html' - -export const SETTING_WINDOW_URL = isDevelopment - ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload` - : 'picgo://./index.html#main-page/upload' +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` @@ -16,10 +12,14 @@ 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` + : '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 TOOLBOX_WINDOW_URL = process.env.NODE_ENV === 'development' ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#toolbox-page` : 'picgo://./index.html#toolbox-page' - -export const MANUAL_WINDOW_URL = process.env.NODE_ENV === 'development' - ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#documents` - : 'picgo://./index.html#documents' diff --git a/src/main/apis/app/window/windowList.ts b/src/main/apis/app/window/windowList.ts index 78bbb56..20a15b8 100644 --- a/src/main/apis/app/window/windowList.ts +++ b/src/main/apis/app/window/windowList.ts @@ -1,45 +1,37 @@ -// External dependencies import { app } from 'electron' -// Electron modules - -// Custom utilities and modules -import bus from '@core/bus' -import db from '~/main/apis/core/datastore' -import picgo from '~/main/apis/core/picgo' -import { T } from '~/main/i18n' import { - SETTING_WINDOW_URL, - TRAY_WINDOW_URL, + MANUAL_WINDOW_URL, MINI_WINDOW_URL, RENAME_WINDOW_URL, - TOOLBOX_WINDOW_URL, - MANUAL_WINDOW_URL + SETTING_WINDOW_URL, + TRAY_WINDOW_URL, + TOOLBOX_WINDOW_URL } from './constants' -// Custom types/enums -import { IWindowList } from '#/types/enum' - -// External utility functions +import bus from '@core/bus' import { CREATE_APP_MENU } from '@core/bus/constants' +import db from '@core/datastore' + +import { T } from '~/i18n' + import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants' -import { configPaths } from '~/universal/utils/configPaths' +import { IWindowList } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' const windowList = new Map() const handleWindowParams = (windowURL: string) => windowURL const getDefaultWindowSizes = (): { width: number, height: number } => { - const mainWindowWidth = picgo.getConfig(configPaths.settings.mainWindowWidth) - const mainWindowHeight = picgo.getConfig(configPaths.settings.mainWindowHeight) + const [mainWindowWidth, mainWindowHeight] = db.get([configPaths.settings.mainWindowWidth, configPaths.settings.mainWindowHeight]) return { width: mainWindowWidth || 1200, height: mainWindowHeight || 800 } } -const defaultWindowWidth = getDefaultWindowSizes().width -const defaultWindowHeight = getDefaultWindowSizes().height +const { width: defaultWindowWidth, height: defaultWindowHeight } = getDefaultWindowSizes() const trayWindowOptions = { height: 350, diff --git a/src/main/apis/app/window/windowManager.ts b/src/main/apis/app/window/windowManager.ts index 2d23b6c..496ee50 100644 --- a/src/main/apis/app/window/windowManager.ts +++ b/src/main/apis/app/window/windowManager.ts @@ -1,17 +1,12 @@ -// External dependencies -import windowList from './windowList' - -// Electron modules import { BrowserWindow } from 'electron' -// Custom utilities and modules - -// Custom types/enums +import windowList from 'apis/app/window/windowList' import { IWindowList } from '#/types/enum' class WindowManager implements IWindowManager { #windowMap: Map = new Map() #windowIdMap: Map = new Map() + create (name: IWindowList) { const windowConfig: IWindowListItem = windowList.get(name)! if (windowConfig.isValid) { diff --git a/src/main/apis/core/bus/apis.ts b/src/main/apis/core/bus/apis.ts index 5a2bff6..76e7501 100644 --- a/src/main/apis/core/bus/apis.ts +++ b/src/main/apis/core/bus/apis.ts @@ -1,14 +1,14 @@ -import bus from '.' +import bus from '@core/bus/index' import { - UPLOAD_WITH_FILES, - UPLOAD_WITH_FILES_RESPONSE, - UPLOAD_WITH_CLIPBOARD_FILES, - UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, + GET_SETTING_WINDOW_ID, + GET_SETTING_WINDOW_ID_RESPONSE, GET_WINDOW_ID, GET_WINDOW_ID_REPONSE, - GET_SETTING_WINDOW_ID, - GET_SETTING_WINDOW_ID_RESPONSE -} from './constants' + UPLOAD_WITH_CLIPBOARD_FILES, + UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, + UPLOAD_WITH_FILES, + UPLOAD_WITH_FILES_RESPONSE +} from '@core/bus/constants' export const uploadWithClipboardFiles = (): Promise<{ success: boolean, diff --git a/src/main/apis/core/bus/constants.ts b/src/main/apis/core/bus/constants.ts index d22c742..7b47acb 100644 --- a/src/main/apis/core/bus/constants.ts +++ b/src/main/apis/core/bus/constants.ts @@ -1,3 +1,4 @@ +export const CREATE_APP_MENU = 'CREATE_APP_MENU' export const GET_WINDOW_ID = 'GET_WINDOW_ID' // get a current window export const GET_WINDOW_ID_REPONSE = 'GET_WINDOW_ID_REPONSE' export const GET_SETTING_WINDOW_ID = 'GET_SETTING_WINDOW_ID' // get setting window @@ -6,4 +7,3 @@ export const UPLOAD_WITH_FILES = 'UPLOAD_WITH_FILES' export const UPLOAD_WITH_FILES_RESPONSE = 'UPLOAD_WITH_FILES_RESPONSE' export const UPLOAD_WITH_CLIPBOARD_FILES = 'UPLOAD_WITH_CLIPBOARD_FILES' export const UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE = 'UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE' -export const CREATE_APP_MENU = 'CREATE_APP_MENU' diff --git a/src/main/apis/core/datastore/dbChecker.ts b/src/main/apis/core/datastore/dbChecker.ts index c393a1c..c7732e8 100644 --- a/src/main/apis/core/datastore/dbChecker.ts +++ b/src/main/apis/core/datastore/dbChecker.ts @@ -1,28 +1,19 @@ -// External dependencies +import { app } from 'electron' import fs from 'fs-extra' import dayjs from 'dayjs' import path from 'path' - -// Electron modules -import { app } from 'electron' - -// Custom utilities and modules -import { getLogger } from '../utils/localLogger' - -// Custom types/enums - -// External utility functions - -// External utility functions import writeFile from 'write-file-atomic' -// Custom types/enums -import { T } from '~/main/i18n' +import { getLogger } from '@core/utils/localLogger' + +import { T } from '~/i18n' const STORE_PATH = app.getPath('userData') + const configFilePath = path.join(STORE_PATH, 'data.json') const configFileBackupPath = path.join(STORE_PATH, 'data.bak.json') export const defaultConfigPath = configFilePath + let _configFilePath = '' let hasCheckPath = false diff --git a/src/main/apis/core/datastore/index.ts b/src/main/apis/core/datastore/index.ts index 95bf4eb..78888df 100644 --- a/src/main/apis/core/datastore/index.ts +++ b/src/main/apis/core/datastore/index.ts @@ -1,19 +1,12 @@ -// External dependencies import fs from 'fs-extra' - -// Electron modules - -// Custom utilities and modules -import { dbPathChecker, dbPathDir, getGalleryDBPath } from './dbChecker' - -// Custom types/enums - -// External utility functions import { DBStore, JSONStore } from '@picgo/store' -// External utility functions -import { T } from '~/main/i18n' -import { configPaths } from '~/universal/utils/configPaths' +import { dbPathChecker, dbPathDir, getGalleryDBPath } from '@core/datastore/dbChecker' + +import { T } from '~/i18n' +import { configPaths } from '#/utils/configPaths' +import { IJSON } from '@picgo/store/dist/types' +import { IConfig } from 'piclist' const STORE_PATH = dbPathDir() @@ -25,6 +18,7 @@ export const DB_PATH: string = getGalleryDBPath().dbPath class ConfigStore { #db: JSONStore + constructor () { this.#db = new JSONStore(CONFIG_PATH) @@ -49,34 +43,54 @@ class ConfigStore { this.read() } - flush () { - this.#db = new JSONStore(CONFIG_PATH) + read (flush?: boolean): IJSON { + return this.#db.read(flush) } - read () { - this.#db.read() - return this.#db - } - - get (key = ''): any { + getSingle (key = ''): any { if (key === '') { - return this.#db.read() + return this.#db.read(true) } + this.read(true) return this.#db.get(key) } + get (key: string): any + get (key: string[]): any[] + get (key: string | string[] = ''): any { + if (Array.isArray(key)) { + return key.map(k => this.getSingle(k)) + } + return this.getSingle(key) + } + set (key: string, value: any): void { + this.read(true) return this.#db.set(key, value) } has (key: string) { + this.read(true) return this.#db.has(key) } unset (key: string, value: any): boolean { + this.read(true) return this.#db.unset(key, value) } + saveConfig (config: Partial): void { + Object.keys(config).forEach((name: string) => { + this.set(name, config[name]) + }) + } + + removeConfig (config: IConfig): void { + Object.keys(config).forEach((name: string) => { + this.unset(name, config[name]) + }) + } + getConfigPath () { return CONFIG_PATH } diff --git a/src/main/apis/core/picgo/index.ts b/src/main/apis/core/picgo/index.ts index 4f8913f..92d61f5 100644 --- a/src/main/apis/core/picgo/index.ts +++ b/src/main/apis/core/picgo/index.ts @@ -1,17 +1,10 @@ -// External dependencies -import pkg from 'root/package.json' import debounce from 'lodash/debounce' - -// Electron modules - -// Custom utilities and modules import { PicGo } from 'piclist' -import db from 'apis/core/datastore' -import { dbChecker, dbPathChecker } from 'apis/core/datastore/dbChecker' -// Custom types/enums +import db from '@core/datastore' +import { dbChecker, dbPathChecker } from '@core/datastore/dbChecker' -// External utility functions +import pkg from 'root/package.json' const CONFIG_PATH = dbPathChecker() @@ -29,7 +22,7 @@ picgo.GUI_VERSION = global.PICGO_GUI_VERSION const originPicGoSaveConfig = picgo.saveConfig.bind(picgo) function flushDB () { - db.flush() + db.read(true) } const debounced = debounce(flushDB, 1000) diff --git a/src/main/apis/gui/index.ts b/src/main/apis/gui/index.ts index 72e4ed2..4e995f0 100644 --- a/src/main/apis/gui/index.ts +++ b/src/main/apis/gui/index.ts @@ -1,31 +1,27 @@ -// External dependencies +import { + BrowserWindow, + dialog, + ipcMain, + Notification +} from 'electron' import fs from 'fs-extra' import { cloneDeep } from 'lodash' - -// Electron modules -import { - dialog, - BrowserWindow, - Notification, - ipcMain -} from 'electron' - -// Custom utilities and modules -import db, { GalleryDB } from 'apis/core/datastore' -import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from 'apis/core/datastore/dbChecker' -import uploader from 'apis/app/uploader' -import pasteTemplate from '~/main/utils/pasteTemplate' -import { handleCopyUrl } from '~/main/utils/common' -import { getWindowId, getSettingWindowId } from '@core/bus/apis' -import { SHOW_INPUT_BOX } from '~/universal/events/constants' - -// Custom types/enums - -// External utility functions import { DBStore } from '@picgo/store' -import { T } from '~/main/i18n' -import { configPaths } from '~/universal/utils/configPaths' -import { IPasteStyle } from '~/universal/types/enum' + +import { getWindowId, getSettingWindowId } from '@core/bus/apis' + +import db, { GalleryDB } from '@core/datastore' +import { dbPathChecker, defaultConfigPath, getGalleryDBPath } from '@core/datastore/dbChecker' + +import uploader from 'apis/app/uploader' + +import { T } from '~/i18n' +import { handleCopyUrl } from '~/utils/common' +import pasteTemplate from '~/utils/pasteTemplate' + +import { SHOW_INPUT_BOX } from '#/events/constants' +import { IPasteStyle } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' // Cross-process support may be required in the future class GuiApi implements IGuiApi { @@ -69,7 +65,7 @@ class GuiApi implements IGuiApi { await this.showSettingWindow() this.getWebcontentsByWindowId(this.settingWindowId)?.send(SHOW_INPUT_BOX, options) return new Promise((resolve) => { - ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => { + ipcMain.once(SHOW_INPUT_BOX, (_: Event, value: string) => { resolve(value) }) }) diff --git a/src/main/events/busEventList.ts b/src/main/events/busEventList.ts index 8e7d6bc..2fd78a6 100644 --- a/src/main/events/busEventList.ts +++ b/src/main/events/busEventList.ts @@ -1,34 +1,27 @@ -// External dependencies import bus from '@core/bus' - -// Electron modules - -// Custom utilities and modules import { - uploadClipboardFiles, - uploadChoosedFiles -} from 'apis/app/uploader/apis' -import { - createMenu -} from 'apis/app/system' -import windowManager from 'apis/app/window/windowManager' - -// Custom types/enums -import { IWindowList } from '#/types/enum' - -// External utility functions -import { - UPLOAD_WITH_FILES, - UPLOAD_WITH_FILES_RESPONSE, - UPLOAD_WITH_CLIPBOARD_FILES, - UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE, + CREATE_APP_MENU, GET_WINDOW_ID, GET_WINDOW_ID_REPONSE, GET_SETTING_WINDOW_ID, GET_SETTING_WINDOW_ID_RESPONSE, - CREATE_APP_MENU + UPLOAD_WITH_FILES, + UPLOAD_WITH_FILES_RESPONSE, + UPLOAD_WITH_CLIPBOARD_FILES, + UPLOAD_WITH_CLIPBOARD_FILES_RESPONSE } from '@core/bus/constants' +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' + function initEventCenter () { const eventList: any = { 'picgo:upload': uploadClipboardFiles, diff --git a/src/main/events/ipcList.ts b/src/main/events/ipcList.ts deleted file mode 100644 index 016ce7a..0000000 --- a/src/main/events/ipcList.ts +++ /dev/null @@ -1,437 +0,0 @@ -// Electron 相关 -import { - app, - ipcMain, - shell, - Notification, - IpcMainEvent, - BrowserWindow, - screen, - IpcMainInvokeEvent -} from 'electron' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { ILogType, IPasteStyle, IWindowList } from '#/types/enum' - -// 上传器 -import uploader from 'apis/app/uploader' - -// 粘贴模板函数 -import pasteTemplate from '~/main/utils/pasteTemplate' - -// 数据存储库和类型声明 -import db, { GalleryDB } from '~/main/apis/core/datastore' - -// 服务器模块 -import server from '~/main/server' - -// 获取图片床模块 -import getPicBeds from '~/main/utils/getPicBeds' - -// 快捷键处理器 -import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' - -// 全局事件总线 -import bus from '@core/bus' - -// 文件系统库 -import fs from 'fs-extra' - -// 事件常量 -import { - TOGGLE_SHORTKEY_MODIFIED_MODE, - OPEN_DEVTOOLS, - SHOW_MINI_PAGE_MENU, - MINIMIZE_WINDOW, - CLOSE_WINDOW, - SHOW_MAIN_PAGE_MENU, - SHOW_UPLOAD_PAGE_MENU, - OPEN_USER_STORE_FILE, - OPEN_URL, - RELOAD_APP, - SHOW_PLUGIN_PAGE_MENU, - SET_MINI_WINDOW_POS, - GET_PICBEDS, - HIDE_DOCK -} from '#/events/constants' - -// 上传剪贴板文件和已选文件的函数 -import { - uploadClipboardFiles, - uploadChoosedFiles -} from '~/main/apis/app/uploader/apis' - -// 核心 IPC 模块 -import picgoCoreIPC from './picgoCoreIPC' - -// 处理复制的 URL 和生成短链接的函数 -import { handleCopyUrl, generateShortUrl } from '~/main/utils/common' - -// 构建主页面、迷你页面、插件页面、图片床列表的菜单函数 -import { buildMainPageMenu, buildMiniPageMenu, buildPluginPageMenu, buildPicBedListMenu } from './remotes/menu' - -// 路径处理库 -import path from 'path' - -// i18n 模块 -import { T } from '~/main/i18n' - -// 同步设置的上传和下载文件函数 -import { uploadFile, downloadFile } from '../utils/syncSettings' - -// SSH 客户端模块 -import SSHClient from '../utils/sshClient' - -// Sftp 配置类型声明 -import { ISftpPlistConfig } from 'piclist' - -import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc' -import webServer from '../server/webServer' -import { configPaths } from '~/universal/utils/configPaths' -import logger from '../apis/core/picgo/logger' - -const STORE_PATH = app.getPath('userData') -const commonConfigList = ['data.json', 'data.bak.json'] -const manageConfigList = ['manage.json', 'manage.bak.json'] - -export default { - listen () { - picgoCoreIPC.listen() - // Upload Related IPC - // from macOS tray - ipcMain.on('uploadClipboardFiles', async () => { - const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! - // macOS use builtin clipboard is OK - 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) - if (isShowResultNotification) { - const notification = new Notification({ - title: T('UPLOAD_SUCCEED'), - body: img[0].imgUrl! - // icon: file[0] - // icon: img[0].imgUrl - }) - notification.show() - } - await GalleryDB.getInstance().insert(img[0]) - trayWindow.webContents.send('clipboardFiles', []) - if (windowManager.has(IWindowList.SETTING_WINDOW)) { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery') - } - } - trayWindow.webContents.send('uploadFiles') - }) - - ipcMain.on('uploadClipboardFilesFromUploadPage', () => { - uploadClipboardFiles() - }) - - ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: IFileWithPath[]) => { - return uploadChoosedFiles(evt.sender, files) - }) - - // ShortKey Related IPC - ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: IShortKeyConfig, oldKey: string, from: string) => { - const result = shortKeyHandler.updateShortKey(item, oldKey, from) - evt.sender.send('updateShortKeyResponse', result) - if (result) { - const notification = new Notification({ - title: T('OPERATION_SUCCEED'), - body: T('TIPS_SHORTCUT_MODIFIED_SUCCEED') - }) - notification.show() - } else { - const notification = new Notification({ - title: T('OPERATION_FAILED'), - body: T('TIPS_SHORTCUT_MODIFIED_CONFLICT') - }) - notification.show() - } - }) - - ipcMain.on('bindOrUnbindShortKey', (_: IpcMainEvent, item: IShortKeyConfig, from: string) => { - const result = shortKeyHandler.bindOrUnbindShortKey(item, from) - if (result) { - const notification = new Notification({ - title: T('OPERATION_SUCCEED'), - body: T('TIPS_SHORTCUT_MODIFIED_SUCCEED') - }) - notification.show() - } else { - const notification = new Notification({ - title: T('OPERATION_FAILED'), - body: T('TIPS_SHORTCUT_MODIFIED_CONFLICT') - }) - notification.show() - } - }) - - // Gallery image cloud delete IPC - - ipcMain.on('logDeleteMsg', async (_: IpcMainEvent, msg: string, logLevel: ILogType) => { - logger[logLevel](msg) - }) - - ipcMain.handle('delete-sftp-file', async (_: IpcMainInvokeEvent, config: ISftpPlistConfig, fileName: string) => { - try { - const client = SSHClient.instance - await client.connect(config) - const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/') - const remote = path.join(uploadPath, fileName) - const deleteResult = await client.deleteFileSFTP(config, remote) - client.close() - return deleteResult - } catch (err: any) { - logger.error(err) - return false - } - }) - - ipcMain.handle('delete-aws-s3-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => { - const result = await removeFileFromS3InMain(configMap) - return result - }) - - ipcMain.handle('delete-doge-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => { - const result = await removeFileFromDogeInMain(configMap) - return result - }) - - ipcMain.handle('delete-huaweicloud-file', async (_: IpcMainInvokeEvent, configMap: IStringKeyMap) => { - const result = await removeFileFromHuaweiInMain(configMap) - return result - }) - - // migrate from PicGo - - ipcMain.handle('migrateFromPicGo', async () => { - const picGoConfigPath = STORE_PATH.replace('piclist', 'picgo') - const files = [ - 'data.json', - 'data.bak.json', - 'picgo.db', - 'picgo.bak.db' - ] - try { - 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.remove(targetPath) - await fs.copy(sourcePath, targetPath, { overwrite: true }) - } - )) - return true - } catch (err: any) { - logger.error(err) - return false - } - }) - - // PicList Setting page IPC - - ipcMain.on('updateCustomLink', () => { - const notification = new Notification({ - title: T('OPERATION_SUCCEED'), - body: T('TIPS_CUSTOM_LINK_STYLE_MODIFIED_SUCCEED') - }) - notification.show() - }) - - ipcMain.on('autoStart', (_: IpcMainEvent, val: boolean) => { - app.setLoginItemSettings({ - openAtLogin: val - }) - }) - - ipcMain.handle('getShortUrl', async (_: IpcMainInvokeEvent, url: string) => { - const shortUrl = await generateShortUrl(url) - return shortUrl - }) - - ipcMain.handle('uploadCommonConfig', async () => { - return await uploadFile(commonConfigList) - }) - - ipcMain.handle('downloadCommonConfig', async () => { - return await downloadFile(commonConfigList) - }) - - ipcMain.handle('uploadManageConfig', async () => { - return await uploadFile(manageConfigList) - }) - - ipcMain.handle('downloadManageConfig', async () => { - return await downloadFile(manageConfigList) - }) - - ipcMain.handle('uploadAllConfig', async () => { - return await uploadFile([...commonConfigList, ...manageConfigList]) - }) - - ipcMain.handle('downloadAllConfig', async () => { - return await downloadFile([...commonConfigList, ...manageConfigList]) - }) - - ipcMain.on('toggleMainWindowAlwaysOnTop', () => { - const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)! - const isAlwaysOnTop = mainWindow.isAlwaysOnTop() - mainWindow.setAlwaysOnTop(!isAlwaysOnTop) - }) - - // Window operation API - - ipcMain.on('openSettingWindow', () => { - windowManager.get(IWindowList.SETTING_WINDOW)!.show() - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false - if (autoCloseMiniWindow) { - if (windowManager.has(IWindowList.MINI_WINDOW)) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } - }) - - ipcMain.on('openManualWindow', () => { - windowManager.get(IWindowList.MANUAL_WINDOW)!.show() - }) - - ipcMain.on('openMiniWindow', () => { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! - miniWindow.removeAllListeners() - if (db.get(configPaths.settings.miniWindowOntop)) { - miniWindow.setAlwaysOnTop(true) - } - const { width, height } = screen.getPrimaryDisplay().workAreaSize - const lastPosition = db.get(configPaths.settings.miniWindowPosition) - if (lastPosition) { - miniWindow.setPosition(lastPosition[0], lastPosition[1]) - } else { - miniWindow.setPosition(width - 100, height - 100) - } - const setPositionFunc = () => { - const position = miniWindow.getPosition() - db.set(configPaths.settings.miniWindowPosition, position) - } - miniWindow.on('close', setPositionFunc) - miniWindow.on('move', setPositionFunc) - miniWindow.show() - miniWindow.focus() - settingWindow.hide() - }) - - ipcMain.on('updateMiniIcon', (_: IpcMainEvent, iconPath: string) => { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! - miniWindow.webContents.send('updateMiniIcon', iconPath) - }) - - ipcMain.on('miniWindowOntop', (_: IpcMainEvent, val: boolean) => { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! - miniWindow.setAlwaysOnTop(val) - }) - - ipcMain.on('refreshSettingWindow', () => { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! - settingWindow.webContents.reloadIgnoringCache() - }) - - // from mini window - ipcMain.on('syncPicBed', () => { - if (windowManager.has(IWindowList.SETTING_WINDOW)) { - windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed') - } - }) - - ipcMain.on(GET_PICBEDS, (evt: IpcMainEvent) => { - const picBeds = getPicBeds() - evt.sender.send(GET_PICBEDS, picBeds) - evt.returnValue = picBeds - }) - - ipcMain.on(TOGGLE_SHORTKEY_MODIFIED_MODE, (_: IpcMainEvent, val: boolean) => { - bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, val) - }) - - ipcMain.on('updateServer', () => { - server.restart() - }) - ipcMain.on('stopWebServer', () => { - webServer.stop() - }) - ipcMain.on('restartWebServer', () => { - webServer.restart() - }) - ipcMain.on(OPEN_DEVTOOLS, (event: IpcMainEvent) => { - event.sender.openDevTools() - }) - // menu & window methods - ipcMain.on(SHOW_MINI_PAGE_MENU, () => { - const window = windowManager.get(IWindowList.MINI_WINDOW)! - const menu = buildMiniPageMenu() - menu.popup({ - window - }) - }) - ipcMain.on(SHOW_MAIN_PAGE_MENU, () => { - const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const menu = buildMainPageMenu(window) - menu.popup({ - window - }) - }) - ipcMain.on(SHOW_UPLOAD_PAGE_MENU, () => { - const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const menu = buildPicBedListMenu() - menu.popup({ - window - }) - }) - ipcMain.on(SHOW_PLUGIN_PAGE_MENU, (_: IpcMainEvent, plugin: IPicGoPlugin) => { - const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const menu = buildPluginPageMenu(plugin) - menu.popup({ - window - }) - }) - ipcMain.on(MINIMIZE_WINDOW, () => { - const window = BrowserWindow.getFocusedWindow() - window?.minimize() - }) - ipcMain.on(CLOSE_WINDOW, () => { - const window = BrowserWindow.getFocusedWindow() - if (process.platform === 'linux') { - window?.hide() - } else { - window?.close() - } - }) - ipcMain.on(OPEN_USER_STORE_FILE, (_: IpcMainEvent, filePath: string) => { - const abFilePath = path.join(STORE_PATH, filePath) - shell.openPath(abFilePath) - }) - ipcMain.on(OPEN_URL, (_: IpcMainEvent, url: string) => { - shell.openExternal(url) - }) - ipcMain.on(RELOAD_APP, () => { - app.relaunch() - app.exit(0) - }) - ipcMain.on(SET_MINI_WINDOW_POS, (_: IpcMainEvent, pos: IMiniWindowPos) => { - const window = BrowserWindow.getFocusedWindow() - window?.setBounds(pos) - }) - ipcMain.on(HIDE_DOCK, (_: IpcMainEvent, val: boolean) => { - if (val) { - app.dock.hide() - } else { - app.dock.show() - } - }) - }, - dispose () {} -} diff --git a/src/main/events/picgoCoreIPC.ts b/src/main/events/picgoCoreIPC.ts deleted file mode 100644 index 4117c18..0000000 --- a/src/main/events/picgoCoreIPC.ts +++ /dev/null @@ -1,456 +0,0 @@ -// External dependencies -import path from 'path' -import { - dialog, - shell, - IpcMainEvent, - ipcMain, - clipboard -} from 'electron' -import fs from 'fs-extra' - -// Electron modules - -// Custom utilities and modules -import GuiApi from 'apis/gui' -import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' -import picgo from '@core/picgo' -import { handleStreamlinePluginName, simpleClone } from '~/universal/utils/common' -import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist' -import windowManager from 'apis/app/window/windowManager' -import { showNotification } from '~/main/utils/common' -import { dbPathChecker } from 'apis/core/datastore/dbChecker' -import { GalleryDB } from 'apis/core/datastore' -import pasteTemplate from '../utils/pasteTemplate' -import { i18nManager, T } from '~/main/i18n' -import { rpcServer } from './rpc' - -// Custom types/enums -import { IPasteStyle, IPicGoHelperType, IWindowList } from '#/types/enum' -import { IObject, IFilter } from '@picgo/store/dist/types' - -// External utility functions -import { - PICGO_SAVE_CONFIG, - PICGO_GET_CONFIG, - PICGO_GET_DB, - PICGO_INSERT_DB, - PICGO_INSERT_MANY_DB, - PICGO_UPDATE_BY_ID_DB, - PICGO_GET_BY_ID_DB, - PICGO_REMOVE_BY_ID_DB, - PICGO_OPEN_FILE, - PICGO_OPEN_DIRECTORY, - PASTE_TEXT, - OPEN_WINDOW, - GET_LANGUAGE_LIST, - SET_CURRENT_LANGUAGE, - GET_CURRENT_LANGUAGE -} from '#/events/constants' -import { configPaths } from '~/universal/utils/configPaths' - -// eslint-disable-next-line -const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require -// const PluginHandler = requireFunc('picgo/lib/PluginHandler').default -const STORE_PATH = path.dirname(dbPathChecker()) -// const CONFIG_PATH = path.join(STORE_PATH, '/data.json') - -interface GuiMenuItem { - label: string - handle: (arg0: PicGoCore, arg1: GuiApi) => Promise -} - -// get uploader or transformer config -const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => { - let config: any[] = [] - if (name === '') { - return config - } else { - const handler = ctx.helper[type].get(name) - if (handler) { - if (handler.config) { - config = handler.config(ctx) - } - } - return config - } -} - -const handleConfigWithFunction = (config: any[]) => { - for (const i in config) { - if (typeof config[i].default === 'function') { - config[i].default = config[i].default() - } - if (typeof config[i].choices === 'function') { - config[i].choices = config[i].choices() - } - } - return config -} - -const getPluginList = (): IPicGoPlugin[] => { - const pluginList = picgo.pluginLoader.getFullList() - const list = [] - for (const i in pluginList) { - const plugin = picgo.pluginLoader.getPlugin(pluginList[i])! - const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`) - const pluginPKG = requireFunc(path.join(pluginPath, 'package.json')) - const uploaderName = plugin.uploader || '' - const transformerName = plugin.transformer || '' - let menu: Omit[] = [] - if (plugin.guiMenu) { - menu = plugin.guiMenu(picgo).map(item => ({ - label: item.label - })) - } - let gui = false - if (pluginPKG.keywords && pluginPKG.keywords.length > 0) { - if (pluginPKG.keywords.includes('picgo-gui-plugin')) { - gui = true - } - } - const obj: IPicGoPlugin = { - name: handleStreamlinePluginName(pluginList[i]), - fullName: pluginList[i], - author: pluginPKG.author.name || pluginPKG.author, - description: pluginPKG.description, - logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'), - version: pluginPKG.version, - gui, - config: { - plugin: { - fullName: pluginList[i], - name: handleStreamlinePluginName(pluginList[i]), - config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : [] - }, - uploader: { - name: uploaderName, - config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo)) - }, - transformer: { - name: transformerName, - config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo)) - } - }, - enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`), - homepage: pluginPKG.homepage ? pluginPKG.homepage : '', - guiMenu: menu, - ing: false - } - list.push(obj) - } - return list -} - -const handleGetPluginList = () => { - ipcMain.on('getPluginList', (event: IpcMainEvent) => { - try { - const list = simpleClone(getPluginList()) - // here can just send JS Object not function - // or will cause [Failed to serialize arguments] error - event.sender.send('pluginList', list) - } catch (e: any) { - event.sender.send('pluginList', []) - showNotification({ - title: T('TIPS_GET_PLUGIN_LIST_FAILED'), - body: e.message - }) - picgo.log.error(e) - } - }) -} - -const handlePluginInstall = () => { - ipcMain.on('installPlugin', async (event: IpcMainEvent, fullName: string) => { - const dispose = handleNPMError() - const res = await picgo.pluginHandler.install([fullName]) - event.sender.send('installPlugin', { - success: res.success, - body: fullName, - errMsg: res.success ? '' : res.body - }) - if (res.success) { - shortKeyHandler.registerPluginShortKey(res.body[0]) - } else { - showNotification({ - title: T('PLUGIN_INSTALL_FAILED'), - body: res.body as string - }) - } - event.sender.send('hideLoading') - dispose() - }) -} - -const handlePluginUninstall = async (fullName: string) => { - const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const dispose = handleNPMError() - const res = await picgo.pluginHandler.uninstall([fullName]) - if (res.success) { - window.webContents.send('uninstallSuccess', res.body[0]) - shortKeyHandler.unregisterPluginShortKey(res.body[0]) - } else { - showNotification({ - title: T('PLUGIN_UNINSTALL_FAILED'), - body: res.body as string - }) - } - window.webContents.send('hideLoading') - dispose() -} - -const handlePluginUpdate = async (fullName: string | string[]) => { - const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const dispose = handleNPMError() - const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName) - if (res.success) { - window.webContents.send('updateSuccess', res.body[0]) - } else { - showNotification({ - title: T('PLUGIN_UPDATE_FAILED'), - body: res.body as string - }) - } - window.webContents.send('hideLoading') - dispose() -} - -const handleUpdateAllPlugin = () => { - ipcMain.on('updateAllPlugin', async (event: IpcMainEvent, list: string[]) => { - handlePluginUpdate(list) - }) -} - -const handleNPMError = (): IDispose => { - const handler = (msg: string) => { - if (msg === 'NPM is not installed') { - dialog.showMessageBox({ - title: T('TIPS_ERROR'), - message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'), - buttons: ['Yes'] - }).then((res) => { - if (res.response === 0) { - shell.openExternal('https://nodejs.org/') - } - }) - } - } - picgo.once('failed', handler) - return () => picgo.off('failed', handler) -} - -const handleGetPicBedConfig = () => { - ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => { - const name = picgo.helper.uploader.get(type)?.name || type - if (picgo.helper.uploader.get(type)?.config) { - const _config = picgo.helper.uploader.get(type)!.config!(picgo) - const config = handleConfigWithFunction(_config) - event.sender.send('getPicBedConfig', config, name) - } else { - event.sender.send('getPicBedConfig', [], name) - } - }) -} - -// TODO: remove it -const handlePluginActions = () => { - ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => { - const plugin = picgo.pluginLoader.getPlugin(name) - if (plugin?.guiMenu?.(picgo)?.length) { - const menu: GuiMenuItem[] = plugin.guiMenu(picgo) - menu.forEach(item => { - if (item.label === label) { - item.handle(picgo, GuiApi.getInstance()) - } - }) - } - }) -} - -const handleRemoveFiles = () => { - ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => { - setTimeout(() => { - picgo.emit('remove', files, GuiApi.getInstance()) - }, 500) - }) -} - -const handlePicGoSaveConfig = () => { - ipcMain.on(PICGO_SAVE_CONFIG, (event: IpcMainEvent, data: IObj) => { - picgo.saveConfig(data) - }) -} - -const handlePicGoGetConfig = () => { - ipcMain.on(PICGO_GET_CONFIG, (event: IpcMainEvent, key: string | undefined, callbackId: string) => { - const result = picgo.getConfig(key) - event.sender.send(PICGO_GET_CONFIG, result, callbackId) - }) -} - -const handleImportLocalPlugin = () => { - ipcMain.on('importLocalPlugin', async (event: IpcMainEvent) => { - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! - const res = await dialog.showOpenDialog(settingWindow, { - properties: ['openDirectory'] - }) - const filePaths = res.filePaths - if (filePaths.length > 0) { - const res = await picgo.pluginHandler.install(filePaths) - if (res.success) { - try { - const list = simpleClone(getPluginList()) - event.sender.send('pluginList', list) - } catch (e: any) { - event.sender.send('pluginList', []) - showNotification({ - title: T('TIPS_GET_PLUGIN_LIST_FAILED'), - body: e.message - }) - } - showNotification({ - title: T('PLUGIN_IMPORT_SUCCEED'), - body: '' - }) - } else { - showNotification({ - title: T('PLUGIN_IMPORT_FAILED'), - body: res.body as string - }) - } - } - event.sender.send('hideLoading') - }) -} - -const handlePicGoGalleryDB = () => { - ipcMain.on(PICGO_GET_DB, async (event: IpcMainEvent, filter: IFilter, callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.get(filter) - event.sender.send(PICGO_GET_DB, res, callbackId) - }) - - ipcMain.on(PICGO_INSERT_DB, async (event: IpcMainEvent, value: IObject, callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.insert(value) - event.sender.send(PICGO_INSERT_DB, res, callbackId) - }) - - ipcMain.on(PICGO_INSERT_MANY_DB, async (event: IpcMainEvent, value: IObject[], callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.insertMany(value) - event.sender.send(PICGO_INSERT_MANY_DB, res, callbackId) - }) - - ipcMain.on(PICGO_UPDATE_BY_ID_DB, async (event: IpcMainEvent, id: string, value: IObject[], callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.updateById(id, value) - event.sender.send(PICGO_UPDATE_BY_ID_DB, res, callbackId) - }) - - ipcMain.on(PICGO_GET_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.getById(id) - event.sender.send(PICGO_GET_BY_ID_DB, res, callbackId) - }) - - ipcMain.on(PICGO_REMOVE_BY_ID_DB, async (event: IpcMainEvent, id: string, callbackId: string) => { - const dbStore = GalleryDB.getInstance() - const res = await dbStore.removeById(id) - event.sender.send(PICGO_REMOVE_BY_ID_DB, res, callbackId) - }) - - ipcMain.handle(PASTE_TEXT, async (_, item: ImgInfo, copy = true) => { - const pasteStyle = picgo.getConfig(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN - const customLink = picgo.getConfig(configPaths.settings.customLink) - const txt = await pasteTemplate(pasteStyle, item, customLink) - if (copy) { - clipboard.writeText(txt) - } - return txt - }) -} - -const handleOpenFile = () => { - ipcMain.on(PICGO_OPEN_FILE, (event: IpcMainEvent, fileName: string) => { - const abFilePath = path.join(STORE_PATH, fileName) - if (!fs.existsSync(abFilePath)) { - fs.writeFileSync(abFilePath, '') - } - shell.openPath(abFilePath) - }) -} - -const handleOpenDirectory = () => { - ipcMain.on(PICGO_OPEN_DIRECTORY, (event: IpcMainEvent, dirPath?: string, inStorePath: boolean = true) => { - if (inStorePath) { - dirPath = path.join(STORE_PATH, dirPath || '') - } - if (!dirPath || !fs.existsSync(dirPath)) { - return - } - shell.openPath(dirPath) - }) -} - -const handleOpenWindow = () => { - ipcMain.on(OPEN_WINDOW, (event: IpcMainEvent, windowName: IWindowList) => { - const window = windowManager.get(windowName) - if (window) { - window.show() - } - }) -} - -const handleI18n = () => { - ipcMain.on(GET_LANGUAGE_LIST, (event: IpcMainEvent) => { - event.sender.send(GET_LANGUAGE_LIST, i18nManager.languageList) - }) - ipcMain.on(SET_CURRENT_LANGUAGE, (event: IpcMainEvent, language: string) => { - i18nManager.setCurrentLanguage(language) - const { lang, locales } = i18nManager.getCurrentLocales() - picgo.i18n.setLanguage(lang) - if (process.platform === 'darwin') { - const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) - trayWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) - } - const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) - settingWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) - if (windowManager.has(IWindowList.MINI_WINDOW)) { - const miniWindow = windowManager.get(IWindowList.MINI_WINDOW) - miniWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) - } - // event.sender.send(SET_CURRENT_LANGUAGE, lang, locales) - }) - ipcMain.on(GET_CURRENT_LANGUAGE, (event: IpcMainEvent) => { - const { lang, locales } = i18nManager.getCurrentLocales() - event.sender.send(GET_CURRENT_LANGUAGE, lang, locales) - }) -} - -const handleRPCActions = () => { - rpcServer.start() -} - -export default { - listen () { - handleGetPluginList() - handlePluginInstall() - handleGetPicBedConfig() - handlePluginActions() - handleRemoveFiles() - handlePicGoSaveConfig() - handlePicGoGetConfig() - handlePicGoGalleryDB() - handleImportLocalPlugin() - handleUpdateAllPlugin() - handleOpenFile() - handleOpenDirectory() - handleOpenWindow() - handleI18n() - handleRPCActions() - }, - // TODO: separate to single file - handlePluginUninstall, - handlePluginUpdate -} diff --git a/src/main/events/remotes/menu.ts b/src/main/events/remotes/menu.ts index 3ad18f2..96669a5 100644 --- a/src/main/events/remotes/menu.ts +++ b/src/main/events/remotes/menu.ts @@ -1,36 +1,42 @@ -// External dependencies -import pkg from 'root/package.json' +import { + app, + dialog, + BrowserWindow, + Menu, + shell, + MenuItemConstructorOptions, + MenuItem +} from 'electron' +import { PicGo as PicGoCore } from 'piclist' -// Electron modules -import { Menu, BrowserWindow, app, dialog, shell } from 'electron' - -// Custom utilities and modules -import windowManager from 'apis/app/window/windowManager' -import getPicBeds from '~/main/utils/getPicBeds' +import db from '@core/datastore' import picgo from '@core/picgo' -import GuiApi from 'apis/gui' -import picgoCoreIPC from '~/main/events/picgoCoreIPC' -import { changeCurrentUploader } from '~/main/utils/handleUploaderConfig' -import db from '~/main/apis/core/datastore' -import clipboardPoll from '~/main/utils/clipboardPoll' -// Custom types/enums -import { IWindowList } from '#/types/enum' - -// External utility functions import { uploadClipboardFiles -} from '~/main/apis/app/uploader/apis' +} from 'apis/app/uploader/apis' +import windowManager from 'apis/app/window/windowManager' +import GuiApi from 'apis/gui' + +import { handlePluginUninstall, handlePluginUpdate } from '~/events/rpc/routes/plugin/utils' +import { T } from '~/i18n' +import clipboardPoll from '~/utils/clipboardPoll' +import { setTrayToolTip } from '~/utils/common' +import getPicBeds from '~/utils/getPicBeds' +import { changeCurrentUploader } from '~/utils/handleUploaderConfig' + import { PICGO_CONFIG_PLUGIN, PICGO_HANDLE_PLUGIN_DONE, PICGO_HANDLE_PLUGIN_ING, PICGO_TOGGLE_PLUGIN, SHOW_MAIN_PAGE_QRCODE -} from '~/universal/events/constants' -import { PicGo as PicGoCore } from 'piclist' -import { T } from '~/main/i18n' -import { configPaths } from '~/universal/utils/configPaths' +} from '#/events/constants' +import { IWindowList } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' + +import pkg from 'root/package.json' +import { openMainWindow } from '~/utils/windowHelper' interface GuiMenuItem { label: string @@ -41,18 +47,10 @@ const buildMiniPageMenu = () => { const isListeningClipboard = db.get(configPaths.settings.isListeningClipboard) || false const ClipboardWatcher = clipboardPoll const submenu = buildPicBedListMenu() - const template = [ + const template: Array<(MenuItemConstructorOptions) | (MenuItem)> = [ { label: T('OPEN_MAIN_WINDOW'), - click () { - windowManager.get(IWindowList.SETTING_WINDOW)!.show() - const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false - if (autoCloseMiniWindow) { - if (windowManager.has(IWindowList.MINI_WINDOW)) { - windowManager.get(IWindowList.MINI_WINDOW)!.hide() - } - } - } + click: openMainWindow }, { label: T('CHOOSE_DEFAULT_PICBED'), @@ -66,7 +64,7 @@ const buildMiniPageMenu = () => { } }, { - label: T('HIDE_WINDOW'), + label: T('HIDE_MINI_WINDOW'), click () { BrowserWindow.getFocusedWindow()!.hide() } @@ -82,7 +80,7 @@ const buildMiniPageMenu = () => { }) buildMiniPageMenu() }, - enabled: !isListeningClipboard + visible: !isListeningClipboard }, { label: T('STOP_WATCH_CLIPBOARD'), @@ -92,7 +90,7 @@ const buildMiniPageMenu = () => { ClipboardWatcher.removeAllListeners() buildMiniPageMenu() }, - enabled: isListeningClipboard + visible: isListeningClipboard }, { label: T('RELOAD_APP'), @@ -106,7 +104,6 @@ const buildMiniPageMenu = () => { label: T('QUIT') } ] - // @ts-ignore return Menu.buildFromTemplate(template) } @@ -185,6 +182,7 @@ const buildPicBedListMenu = () => { if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed') } + setTrayToolTip(`${item.type} ${config._configName || 'Default'}`) } } }) @@ -198,6 +196,7 @@ const buildPicBedListMenu = () => { if (windowManager.has(IWindowList.SETTING_WINDOW)) { windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed') } + setTrayToolTip(item.type) } : undefined } @@ -264,14 +263,14 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => { click () { const window = windowManager.get(IWindowList.SETTING_WINDOW)! window.webContents.send(PICGO_HANDLE_PLUGIN_ING, plugin.fullName) - picgoCoreIPC.handlePluginUninstall(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) - picgoCoreIPC.handlePluginUpdate(plugin.fullName) + handlePluginUpdate(plugin.fullName) } }] for (const i in plugin.config) { @@ -325,7 +324,6 @@ const buildPluginPageMenu = (plugin: IPicGoPlugin) => { menu.push({ label: i.label, click () { - // ipcRenderer.send('pluginActions', plugin.fullName, i.label) const picgPlugin = picgo.pluginLoader.getPlugin(plugin.fullName) if (picgPlugin?.guiMenu?.(picgo)?.length) { const menu: GuiMenuItem[] = picgPlugin.guiMenu(picgo) diff --git a/src/main/events/rpc/index.ts b/src/main/events/rpc/index.ts index 997fbc0..8b6a889 100644 --- a/src/main/events/rpc/index.ts +++ b/src/main/events/rpc/index.ts @@ -1,52 +1,62 @@ -// External dependencies -import { ipcMain, IpcMainEvent } from 'electron' +import { ipcMain, IpcMainEvent, IpcMainInvokeEvent } from 'electron' -// Electron modules +import logger from '@core/picgo/logger' -// Custom utilities and modules -import { configRouter } from './routes/config' -import { toolboxRouter } from './routes/toolbox' -import { systemRouter } from './routes/system' +import { galleryRouter } from '~/events/rpc/routes/gallery' +import { picbedRouter } from '~/events/rpc/routes/picbed' +import { pluginRouter } from '~/events/rpc/routes/plugin' +import { settingRouter } from '~/events/rpc/routes/setting' +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' -// Custom types/enums -import { IRPCActionType } from '~/universal/types/enum' +import { IRPCActionType, IRPCType } from '#/types/enum' +import { RPC_ACTIONS, RPC_ACTIONS_INVOKE } from '#/events/constants' -// External utility functions -import { RPC_ACTIONS } from '#/events/constants' +const isDevelopment = process.env.NODE_ENV !== 'production' class RPCServer implements IRPCServer { private routes: IRPCRoutes = new Map() + private routesWithResponse: IRPCRoutes = new Map() - private rpcEventHandler = async (event: IpcMainEvent, action: IRPCActionType, args: any[], callbackId: string) => { + private rpcEventHandler = async (event: IpcMainEvent, action: IRPCActionType, args: any[]) => { try { - const handler = this.routes.get(action) - if (!handler) { - return this.sendBack(event, action, null, callbackId) + if (isDevelopment) { + console.log(`action: ${action} args: ${JSON.stringify(args)}`) } - const res = await handler?.(args, event) - this.sendBack(event, action, res, callbackId) - } catch (e) { - this.sendBack(event, action, null, callbackId) + const route = this.routes.get(action) + await route?.handler?.(event, args) + } catch (e: any) { + logger.error(e) } } - /** - * if sendback data is null, then it means that the action is not supported or error occurs - * if there is no callbackId, then do not send back - */ - private sendBack (event: IpcMainEvent, action: IRPCActionType, data: any, callbackId: string) { - if (callbackId) { - event.sender.send(RPC_ACTIONS, data, action, callbackId) + private rpcEventHandlerWithResponse = async (event: IpcMainInvokeEvent, action: IRPCActionType, args: any[]) => { + try { + if (isDevelopment) { + console.log(`action: ${action} args: ${JSON.stringify(args)}`) + } + const route = this.routesWithResponse.get(action) + return await route?.handler?.(event, args) + } catch (e: any) { + logger.error(e) + return undefined } } start () { ipcMain.on(RPC_ACTIONS, this.rpcEventHandler) + ipcMain.handle(RPC_ACTIONS_INVOKE, this.rpcEventHandlerWithResponse) } use (routes: IRPCRoutes) { - for (const [action, handler] of routes) { - this.routes.set(action, handler) + for (const [action, route] of routes) { + if (route.type === IRPCType.SEND) { + this.routes.set(action, route) + } else { + this.routesWithResponse.set(action, route) + } } } @@ -57,9 +67,20 @@ class RPCServer implements IRPCServer { const rpcServer = new RPCServer() -rpcServer.use(configRouter.routes()) -rpcServer.use(toolboxRouter.routes()) -rpcServer.use(systemRouter.routes()) +const routes = [ + galleryRouter.routes(), + picbedRouter.routes(), + pluginRouter.routes(), + settingRouter.routes(), + systemRouter.routes(), + toolboxRouter.routes(), + trayRouter.routes(), + uploadRouter.routes() +] + +for (const route of routes) { + rpcServer.use(route) +} export { rpcServer diff --git a/src/main/events/rpc/router.ts b/src/main/events/rpc/router.ts index b285005..8117e21 100644 --- a/src/main/events/rpc/router.ts +++ b/src/main/events/rpc/router.ts @@ -1,9 +1,22 @@ -import { IRPCActionType } from '~/universal/types/enum' +import { IRPCType, IRPCActionType } from '#/types/enum' + +interface IBatchAddParams { + action: IRPCActionType + handler: IRPCHandler + type?: IRPCType +} export class RPCRouter implements IRPCRouter { private routeMap: IRPCRoutes = new Map() - add = (action: IRPCActionType, handler: IRPCHandler) => { - this.routeMap.set(action, handler) + add = (action: IRPCActionType, handler: IRPCHandler, type: IRPCType = IRPCType.SEND): this => { + this.routeMap.set(action, { handler, type }) + return this + } + + addBatch = (params: IBatchAddParams[]): this => { + for (const { action, handler, type = IRPCType.SEND } of params) { + this.routeMap.set(action, { handler, type }) + } return this } diff --git a/src/main/events/rpc/routes/config.ts b/src/main/events/rpc/routes/config.ts deleted file mode 100644 index 1ebc440..0000000 --- a/src/main/events/rpc/routes/config.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { IRPCActionType } from '~/universal/types/enum' -import { RPCRouter } from '../router' -import { - deleteUploaderConfig, - getUploaderConfigList, - selectUploaderConfig, - updateUploaderConfig, - resetUploaderConfig -} from '~/main/utils/handleUploaderConfig' - -const configRouter = new RPCRouter() - -configRouter - .add(IRPCActionType.GET_PICBED_CONFIG_LIST, async (args) => { - const [type] = args as IGetUploaderConfigListArgs - const config = getUploaderConfigList(type) - return config - }) - .add(IRPCActionType.DELETE_PICBED_CONFIG, async (args) => { - const [type, id] = args as IDeleteUploaderConfigArgs - const config = deleteUploaderConfig(type, id) - return config - }) - .add(IRPCActionType.SELECT_UPLOADER, async (args) => { - const [type, id] = args as ISelectUploaderConfigArgs - selectUploaderConfig(type, id) - return true - }) - .add(IRPCActionType.UPDATE_UPLOADER_CONFIG, async (args) => { - const [type, id, config] = args as IUpdateUploaderConfigArgs - updateUploaderConfig(type, id, config) - return true - }) - .add(IRPCActionType.RESET_UPLOADER_CONFIG, async (args) => { - const [type, id] = args as IUpdateUploaderConfigArgs - resetUploaderConfig(type, id) - return true - }) - -export { - configRouter -} diff --git a/src/main/events/rpc/routes/gallery/index.ts b/src/main/events/rpc/routes/gallery/index.ts new file mode 100644 index 0000000..5ae1e38 --- /dev/null +++ b/src/main/events/rpc/routes/gallery/index.ts @@ -0,0 +1,133 @@ + +import { clipboard } from 'electron' + +import { GalleryDB } from '@core/datastore' +import picgo from '@core/picgo' +import logger from '@core/picgo/logger' +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 pasteTemplate from '~/utils/pasteTemplate' + +import { ICOREBuildInEvent, IPasteStyle, IRPCActionType, IRPCType } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' + +const galleryRouter = new RPCRouter() + +const galleryRoutes = [ + { + action: IRPCActionType.GALLERY_PASTE_TEXT, + handler: async (_: IIPCEvent, args: [ item: ImgInfo, copy?: boolean]) => { + const [item, copy = true] = args + const pasteStyle = picgo.getConfig(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN + const customLink = picgo.getConfig(configPaths.settings.customLink) + const txt = await pasteTemplate(pasteStyle, item, customLink) + if (copy) { + clipboard.writeText(txt) + } + return txt + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_REMOVE_FILES, + handler: async (_: IIPCEvent, args: [files: ImgInfo[]]) => { + setTimeout(() => { + picgo.emit(ICOREBuildInEvent.REMOVE, args[0], GuiApi.getInstance()) + }, 500) + } + }, + { + action: IRPCActionType.GALLERY_GET_DB, + handler: async (_: IIPCEvent, args: [filter: IFilter]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.get(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_GET_BY_ID_DB, + handler: async (_: IIPCEvent, args: [id: string]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.getById(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_UPDATE_BY_ID_DB, + handler: async (_: IIPCEvent, args: [id: string, value: IObject]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.updateById(args[0], args[1]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_REMOVE_BY_ID_DB, + handler: async (_: IIPCEvent, args: [id: string]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.removeById(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_INSERT_DB, + handler: async (_: IIPCEvent, args: [value: IObject]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.insert(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_INSERT_DB_BATCH, + handler: async (_: IIPCEvent, args: [value: IObject[]]) => { + const dbStore = GalleryDB.getInstance() + return await dbStore.insertMany(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_LOG_DELETE_MSG, + handler: async (_: IIPCEvent, args: [msg: string, logLevel: ILogType]) => { + const [msg, logLevel] = args + console.log(msg, logLevel) + logger[logLevel](msg) + } + }, + { + action: IRPCActionType.GALLERY_DELETE_SFTP_FILE, + handler: async (_: IIPCEvent, args: [config: ISftpPlistConfig, fileName: string]) => { + const [config, fileName] = args + return await removeFileFromSFTPInMain(config, fileName) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, + handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => { + return await removeFileFromS3InMain(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_DELETE_DOGE_FILE, + handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => { + return await removeFileFromDogeInMain(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, + handler: async (_: IIPCEvent, args: [configMap: IStringKeyMap]) => { + return await removeFileFromHuaweiInMain(args[0]) + }, + type: IRPCType.INVOKE + } +] + +galleryRouter.addBatch(galleryRoutes) + +export { + galleryRouter +} diff --git a/src/main/events/rpc/routes/picbed/index.ts b/src/main/events/rpc/routes/picbed/index.ts new file mode 100644 index 0000000..5936a86 --- /dev/null +++ b/src/main/events/rpc/routes/picbed/index.ts @@ -0,0 +1,99 @@ +import picgo from '@core/picgo' + +import { RPCRouter } from '~/events/rpc/router' +import { + deleteUploaderConfig, + getUploaderConfigList, + resetUploaderConfig, + selectUploaderConfig, + updateUploaderConfig +} from '~/utils/handleUploaderConfig' +import { IRPCActionType, IRPCType } from '#/types/enum' + +const picbedRouter = new RPCRouter() + +const handleConfigWithFunction = (config: any[]) => { + for (const i in config) { + if (typeof config[i].default === 'function') { + config[i].default = config[i].default() + } + if (typeof config[i].choices === 'function') { + config[i].choices = config[i].choices() + } + } + return config +} + +const picbedRoutes = [ + { + action: IRPCActionType.PICBED_GET_CONFIG_LIST, + handler: async (_: IIPCEvent, args: [type: string]) => { + const config = getUploaderConfigList(args[0]) + return config + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.PICBED_DELETE_CONFIG, + handler: async (_: IIPCEvent, args: [type: string, id: string]) => { + const [type, id] = args + const config = deleteUploaderConfig(type, id) + return config + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.UPLOADER_SELECT, + handler: async (_: IIPCEvent, args: [type: string, id: string]) => { + const [type, id] = args + selectUploaderConfig(type, id) + return true + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.UPLOADER_UPDATE_CONFIG, + handler: async (_: IIPCEvent, args: [type: string, id: string, config: IStringKeyMap]) => { + const [type, id, config] = args + updateUploaderConfig(type, id, config) + return true + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.UPLOADER_RESET_CONFIG, + handler: async (_: IIPCEvent, args: [type: string, id: string]) => { + const [type, id] = args + resetUploaderConfig(type, id) + return true + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.PICBED_GET_PICBED_CONFIG, + handler: async (_: IIPCEvent, args: [type: string]) => { + const type = args[0] + const name = picgo.helper.uploader.get(type)?.name || type + if (picgo.helper.uploader.get(type)?.config) { + const _config = picgo.helper.uploader.get(type)!.config!(picgo) + const config = handleConfigWithFunction(_config) + return { + config, + name + } + } else { + return { + config: [], + name + } + } + }, + type: IRPCType.INVOKE + } +] + +picbedRouter.addBatch(picbedRoutes) + +export { + picbedRouter +} diff --git a/src/main/events/rpc/routes/plugin/index.ts b/src/main/events/rpc/routes/plugin/index.ts new file mode 100644 index 0000000..ea91bc7 --- /dev/null +++ b/src/main/events/rpc/routes/plugin/index.ts @@ -0,0 +1,37 @@ + +import { RPCRouter } from '~/events/rpc/router' +import { + pluginImportLocalFunc, + pluginInstallFunc, + pluginGetListFunc, + pluginUpdateAllFunc +} from '~/events/rpc/routes/plugin/utils' + +import { IRPCActionType } from '#/types/enum' + +const pluginRouter = new RPCRouter() + +const pluginRoutes = [ + { + action: IRPCActionType.PLUGIN_GET_LIST, + handler: pluginGetListFunc + }, + { + action: IRPCActionType.PLUGIN_INSTALL, + handler: pluginInstallFunc + }, + { + action: IRPCActionType.PLUGIN_IMPORT_LOCAL, + handler: pluginImportLocalFunc + }, + { + action: IRPCActionType.PLUGIN_UPDATE_ALL, + handler: pluginUpdateAllFunc + } +] + +pluginRouter.addBatch(pluginRoutes) + +export { + pluginRouter +} diff --git a/src/main/events/rpc/routes/plugin/utils.ts b/src/main/events/rpc/routes/plugin/utils.ts new file mode 100644 index 0000000..3771107 --- /dev/null +++ b/src/main/events/rpc/routes/plugin/utils.ts @@ -0,0 +1,227 @@ +import { dialog, shell } from 'electron' +import { IGuiMenuItem, PicGo as PicGoCore } from 'piclist' +import path from 'path' + +import { dbPathDir } from '@core/datastore/dbChecker' +import picgo from '@core/picgo' + +import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' +import windowManager from 'apis/app/window/windowManager' + +import { T } from '~/i18n' +import { showNotification } from '~/utils/common' + +import { handleStreamlinePluginName, simpleClone } from '#/utils/common' +import { ICOREBuildInEvent, IPicGoHelperType, IWindowList } from '#/types/enum' + +const STORE_PATH = dbPathDir() + +// eslint-disable-next-line +const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require + +// get uploader or transformer config +const getConfig = (name: string, type: IPicGoHelperType, ctx: PicGoCore) => { + let config: any[] = [] + if (name === '') { + return config + } else { + const handler = ctx.helper[type].get(name) + if (handler) { + if (handler.config) { + config = handler.config(ctx) + } + } + return config + } +} + +const handleConfigWithFunction = (config: any[]) => { + for (const i in config) { + if (typeof config[i].default === 'function') { + config[i].default = config[i].default() + } + if (typeof config[i].choices === 'function') { + config[i].choices = config[i].choices() + } + } + return config +} + +const getPluginList = (): IPicGoPlugin[] => { + const pluginList = picgo.pluginLoader.getFullList() + const list = [] + for (const i in pluginList) { + const plugin = picgo.pluginLoader.getPlugin(pluginList[i])! + const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`) + const pluginPKG = requireFunc(path.join(pluginPath, 'package.json')) + const uploaderName = plugin.uploader || '' + const transformerName = plugin.transformer || '' + let menu: Omit[] = [] + if (plugin.guiMenu) { + menu = plugin.guiMenu(picgo).map(item => ({ + label: item.label + })) + } + let gui = false + if (pluginPKG.keywords && pluginPKG.keywords.length > 0) { + if (pluginPKG.keywords.includes('picgo-gui-plugin')) { + gui = true + } + } + const obj: IPicGoPlugin = { + name: handleStreamlinePluginName(pluginList[i]), + fullName: pluginList[i], + author: pluginPKG.author.name || pluginPKG.author, + description: pluginPKG.description, + logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'), + version: pluginPKG.version, + gui, + config: { + plugin: { + fullName: pluginList[i], + name: handleStreamlinePluginName(pluginList[i]), + config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : [] + }, + uploader: { + name: uploaderName, + config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.uploader, picgo)) + }, + transformer: { + name: transformerName, + config: handleConfigWithFunction(getConfig(uploaderName, IPicGoHelperType.transformer, picgo)) + } + }, + enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`), + homepage: pluginPKG.homepage ? pluginPKG.homepage : '', + guiMenu: menu, + ing: false + } + list.push(obj) + } + return list +} + +const handleNPMError = (): IDispose => { + const handler = (msg: string) => { + if (msg === 'NPM is not installed') { + dialog.showMessageBox({ + title: T('TIPS_ERROR'), + message: T('TIPS_INSTALL_NODE_AND_RELOAD_PICGO'), + buttons: ['Yes'] + }).then((res) => { + if (res.response === 0) { + shell.openExternal('https://nodejs.org/') + } + }) + } + } + picgo.once(ICOREBuildInEvent.FAILED, handler) + return () => picgo.off(ICOREBuildInEvent.FAILED, handler) +} + +export const handlePluginUpdate = async (fullName: string | string[]) => { + const window = windowManager.get(IWindowList.SETTING_WINDOW)! + const dispose = handleNPMError() + const res = await picgo.pluginHandler.update(typeof fullName === 'string' ? [fullName] : fullName) + if (res.success) { + window.webContents.send('updateSuccess', res.body[0]) + } else { + showNotification({ + title: T('PLUGIN_UPDATE_FAILED'), + body: res.body as string + }) + } + window.webContents.send('hideLoading') + dispose() +} + +export const handlePluginUninstall = async (fullName: string) => { + const window = windowManager.get(IWindowList.SETTING_WINDOW)! + const dispose = handleNPMError() + const res = await picgo.pluginHandler.uninstall([fullName]) + if (res.success) { + window.webContents.send('uninstallSuccess', res.body[0]) + shortKeyHandler.unregisterPluginShortKey(res.body[0]) + } else { + showNotification({ + title: T('PLUGIN_UNINSTALL_FAILED'), + body: res.body as string + }) + } + window.webContents.send('hideLoading') + dispose() +} + +export const pluginGetListFunc = async (event: IIPCEvent) => { + try { + const list = simpleClone(getPluginList()) + // here can just send JS Object not function + // or will cause [Failed to serialize arguments] error + event.sender.send('pluginList', list) + } catch (e: any) { + event.sender.send('pluginList', []) + showNotification({ + title: T('TIPS_GET_PLUGIN_LIST_FAILED'), + body: e.message + }) + picgo.log.error(e) + } +} + +export const pluginInstallFunc = async (event: IIPCEvent, args: [fullName: string]) => { + const fullName = args[0] + const dispose = handleNPMError() + const res = await picgo.pluginHandler.install([fullName]) + event.sender.send('installPlugin', { + success: res.success, + body: fullName, + errMsg: res.success ? '' : res.body + }) + if (res.success) { + shortKeyHandler.registerPluginShortKey(res.body[0]) + } else { + showNotification({ + title: T('PLUGIN_INSTALL_FAILED'), + body: res.body as string + }) + } + event.sender.send('hideLoading') + dispose() +} + +export const pluginImportLocalFunc = async (event: IIPCEvent) => { + const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! + const res = await dialog.showOpenDialog(settingWindow, { + properties: ['openDirectory'] + }) + const filePaths = res.filePaths + if (filePaths.length > 0) { + const res = await picgo.pluginHandler.install(filePaths) + if (res.success) { + try { + const list = simpleClone(getPluginList()) + event.sender.send('pluginList', list) + } catch (e: any) { + event.sender.send('pluginList', []) + showNotification({ + title: T('TIPS_GET_PLUGIN_LIST_FAILED'), + body: e.message + }) + } + showNotification({ + title: T('PLUGIN_IMPORT_SUCCEED'), + body: '' + }) + } else { + showNotification({ + title: T('PLUGIN_IMPORT_FAILED'), + body: res.body as string + }) + } + } + event.sender.send('hideLoading') +} + +export const pluginUpdateAllFunc = async (_: IIPCEvent, args: [list: string[]]) => { + handlePluginUpdate(args[0]) +} diff --git a/src/main/events/rpc/routes/setting/advanced.ts b/src/main/events/rpc/routes/setting/advanced.ts new file mode 100644 index 0000000..9edef76 --- /dev/null +++ b/src/main/events/rpc/routes/setting/advanced.ts @@ -0,0 +1,25 @@ + +import { IRPCActionType } from '#/types/enum' +import server from '~/server' +import webServer from '~/server/webServer' + +export default [ + { + action: IRPCActionType.ADVANCED_UPDATE_SERVER, + handler: async () => { + server.restart() + } + }, + { + action: IRPCActionType.ADVANCED_STOP_WEB_SERVER, + handler: async () => { + webServer.stop() + } + }, + { + action: IRPCActionType.ADVANCED_RESTART_WEB_SERVER, + handler: async () => { + webServer.restart() + } + } +] diff --git a/src/main/events/rpc/routes/setting/configure.ts b/src/main/events/rpc/routes/setting/configure.ts new file mode 100644 index 0000000..0987cad --- /dev/null +++ b/src/main/events/rpc/routes/setting/configure.ts @@ -0,0 +1,82 @@ +import { app } from 'electron' +import fs from 'fs-extra' +import path from 'path' + +import logger from '@core/picgo/logger' +import { downloadFile, uploadFile } from '~/utils/syncSettings' +import { IRPCActionType, IRPCType } from '#/types/enum' + +const STORE_PATH = app.getPath('userData') + +const commonConfigList = ['data.json', 'data.bak.json'] +const manageConfigList = ['manage.json', 'manage.bak.json'] + +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' + ] + try { + 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) + throw new Error('Migrate failed') + } + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_UPLOAD_COMMON_CONFIG, + handler: async () => { + return await uploadFile(commonConfigList) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_UPLOAD_MANAGE_CONFIG, + handler: async () => { + return await uploadFile(manageConfigList) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_UPLOAD_ALL_CONFIG, + handler: async () => { + return await uploadFile([...commonConfigList, ...manageConfigList]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_DOWNLOAD_COMMON_CONFIG, + handler: async () => { + return await downloadFile(commonConfigList) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_DOWNLOAD_MANAGE_CONFIG, + handler: async () => { + return await downloadFile(manageConfigList) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.CONFIGURE_DOWNLOAD_ALL_CONFIG, + handler: async () => { + return await downloadFile([...commonConfigList, ...manageConfigList]) + }, + type: IRPCType.INVOKE + } +] diff --git a/src/main/events/rpc/routes/setting/index.ts b/src/main/events/rpc/routes/setting/index.ts new file mode 100644 index 0000000..20ecdbe --- /dev/null +++ b/src/main/events/rpc/routes/setting/index.ts @@ -0,0 +1,21 @@ +import { RPCRouter } from '~/events/rpc/router' + +import advancedRoutes from '~/events/rpc/routes/setting/advanced' +import configureRoutes from '~/events/rpc/routes/setting/configure' +import mainAppRoutes from '~/events/rpc/routes/setting/mainApp' +import shortKeyRoutes from '~/events/rpc/routes/setting/shortKey' + +const settingRouter = new RPCRouter() + +const settingRoutes = [ + ...advancedRoutes, + ...configureRoutes, + ...mainAppRoutes, + ...shortKeyRoutes +] + +settingRouter.addBatch(settingRoutes) + +export { + settingRouter +} diff --git a/src/main/events/rpc/routes/setting/mainApp.ts b/src/main/events/rpc/routes/setting/mainApp.ts new file mode 100644 index 0000000..4851e9e --- /dev/null +++ b/src/main/events/rpc/routes/setting/mainApp.ts @@ -0,0 +1,65 @@ +import { app, IpcMainEvent, shell } from 'electron' +import fs from 'fs-extra' +import path from 'path' + +import picgo from '@core/picgo' +import { dbPathDir } from '@core/datastore/dbChecker' + +import { IRPCActionType, IRPCType } from '#/types/enum' + +const STORE_PATH = dbPathDir() + +export default [ + { + action: IRPCActionType.PICLIST_GET_CONFIG, + handler: async (_: IIPCEvent, args: [key?: string]) => { + return picgo.getConfig(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.PICLIST_GET_CONFIG_SYNC, + handler: async (event: IIPCEvent, args: [key?: string]) => { + const result = picgo.getConfig(args[0]) + const eventInstance = event as IpcMainEvent + eventInstance.returnValue = result + } + }, + { + action: IRPCActionType.PICLIST_SAVE_CONFIG, + handler: async (_: IIPCEvent, args: [data: IObj]) => { + picgo.saveConfig(args[0]) + } + }, + { + action: IRPCActionType.PICLIST_OPEN_FILE, + handler: async (_: IIPCEvent, args: [fileName: string]) => { + const abFilePath = path.join(STORE_PATH, args[0]) + if (!fs.existsSync(abFilePath)) { + fs.writeFileSync(abFilePath, '') + } + shell.openPath(abFilePath) + } + }, + { + action: IRPCActionType.PICLIST_OPEN_DIRECTORY, + handler: async (_: IIPCEvent, args: [dirPath?: string, inStorePath?: boolean]) => { + let [dirPath, inStorePath = true] = args + if (inStorePath) { + dirPath = path.join(STORE_PATH, dirPath || '') + } + if (!dirPath || !fs.existsSync(dirPath)) { + return + } + shell.openPath(dirPath) + } + }, + { + action: IRPCActionType.PICLIST_AUTO_START, + handler: async (_: IIPCEvent, args: [val: boolean]) => { + app.setLoginItemSettings({ + openAtLogin: args[0] + }) + } + } +] diff --git a/src/main/events/rpc/routes/setting/shortKey.ts b/src/main/events/rpc/routes/setting/shortKey.ts new file mode 100644 index 0000000..9cf4443 --- /dev/null +++ b/src/main/events/rpc/routes/setting/shortKey.ts @@ -0,0 +1,46 @@ +import { + Notification +} from 'electron' + +import bus from '@core/bus' +import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' +import { T } from '~/i18n' + +import { IRPCActionType, IRPCType } from '#/types/enum' +import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants' + +const notificationFunc = (result: boolean) => { + const notification = new Notification({ + title: T(`OPERATION_${result ? 'SUCCEED' : 'FAILED'}`), + body: T(`TIPS_SHORTCUT_MODIFIED_${result ? 'SUCCEED' : 'CONFLICT'}`) + }) + notification.show() +} + +export default [ + { + action: IRPCActionType.SHORTKEY_UPDATE, + handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, oldKey: string, from: string]) => { + const [item, oldKey, from] = args + const result = shortKeyHandler.updateShortKey(item, oldKey, from) + notificationFunc(result) + return result + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.SHORTKEY_BIND_OR_UNBIND, + handler: async (_: IIPCEvent, args: [item: IShortKeyConfig, from: string]) => { + const [item, from] = args + const result = shortKeyHandler.bindOrUnbindShortKey(item, from) + notificationFunc(result) + } + }, + { + action: IRPCActionType.SHORTKEY_TOGGLE_SHORTKEY_MODIFIED_MODE, + handler: async (_: IIPCEvent, args: [status: boolean]) => { + const [status] = args + bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, status) + } + } +] diff --git a/src/main/events/rpc/routes/system.ts b/src/main/events/rpc/routes/system.ts deleted file mode 100644 index 0de8be8..0000000 --- a/src/main/events/rpc/routes/system.ts +++ /dev/null @@ -1,28 +0,0 @@ -// External dependencies -import { app, clipboard, shell } from 'electron' - -// Electron modules - -// Custom utilities and modules -import { IRPCActionType } from '~/universal/types/enum' -import { RPCRouter } from '../router' - -const systemRouter = new RPCRouter() - -systemRouter - .add(IRPCActionType.RELOAD_APP, async () => { - app.relaunch() - app.exit(0) - }) - .add(IRPCActionType.OPEN_FILE, async (args) => { - const [filePath] = args as IOpenFileArgs - shell.openPath(filePath) - }) - .add(IRPCActionType.COPY_TEXT, async (args) => { - const [text] = args as ICopyTextArgs - return clipboard.writeText(text) - }) - -export { - systemRouter -} diff --git a/src/main/events/rpc/routes/system/app.ts b/src/main/events/rpc/routes/system/app.ts new file mode 100644 index 0000000..d1b2450 --- /dev/null +++ b/src/main/events/rpc/routes/system/app.ts @@ -0,0 +1,62 @@ +import { app, shell } from 'electron' + +import picgo from '@core/picgo' + +import windowManager from 'apis/app/window/windowManager' +import { i18nManager } from '~/i18n' + +import { IRPCActionType, IWindowList } from '#/types/enum' +import { SET_CURRENT_LANGUAGE } from '#/events/constants' + +export default [ + { + action: IRPCActionType.RELOAD_APP, + handler: async () => { + app.relaunch() + app.exit(0) + } + }, + { + action: IRPCActionType.OPEN_FILE, + handler: async (_: IIPCEvent, args: [filePath: string]) => { + shell.openPath(args[0]) + } + }, + { + action: IRPCActionType.OPEN_URL, + handler: async (_: IIPCEvent, args: [url: string]) => { + shell.openExternal(args[0]) + } + }, + { + action: IRPCActionType.GET_LANGUAGE_LIST, + handler: async (event: IIPCEvent) => { + event.returnValue = i18nManager.languageList + } + }, + { + action: IRPCActionType.GET_CURRENT_LANGUAGE, + handler: async (event: IIPCEvent) => { + const { lang, locales } = i18nManager.getCurrentLocales() + event.returnValue = [lang, locales] + } + }, + { + action: IRPCActionType.SET_CURRENT_LANGUAGE, + handler: async (_: IIPCEvent, args: [language: string]) => { + i18nManager.setCurrentLanguage(args[0]) + const { lang, locales } = i18nManager.getCurrentLocales() + picgo.i18n.setLanguage(lang) + if (process.platform === 'darwin') { + const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW) + trayWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) + } + const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) + settingWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) + if (windowManager.has(IWindowList.MINI_WINDOW)) { + const miniWindow = windowManager.get(IWindowList.MINI_WINDOW) + miniWindow?.webContents.send(SET_CURRENT_LANGUAGE, lang, locales) + } + } + } +] diff --git a/src/main/events/rpc/routes/system/index.ts b/src/main/events/rpc/routes/system/index.ts new file mode 100644 index 0000000..9b5a6df --- /dev/null +++ b/src/main/events/rpc/routes/system/index.ts @@ -0,0 +1,16 @@ +import { RPCRouter } from '~/events/rpc/router' +import appRoutes from '~/events/rpc/routes/system/app' +import windowRoutes from '~/events/rpc/routes/system/window' + +const systemRouter = new RPCRouter() + +const systemRoutes = [ + ...appRoutes, + ...windowRoutes +] + +systemRouter.addBatch(systemRoutes) + +export { + systemRouter +} diff --git a/src/main/events/rpc/routes/system/window.ts b/src/main/events/rpc/routes/system/window.ts new file mode 100644 index 0000000..e2a70cd --- /dev/null +++ b/src/main/events/rpc/routes/system/window.ts @@ -0,0 +1,137 @@ +import { app, BrowserWindow } from 'electron' + +import windowManager from 'apis/app/window/windowManager' + +import { + buildMainPageMenu, + buildMiniPageMenu, + buildPicBedListMenu, + buildPluginPageMenu +} from '~/events/remotes/menu' +import { openMiniWindow } from '~/utils/windowHelper' + +import { IRPCActionType, IWindowList } from '#/types/enum' + +export default [ + { + action: IRPCActionType.HIDE_DOCK, + handler: async (_: IIPCEvent, args: [value: boolean]) => { + args[0] ? app.dock.hide() : app.dock.show() + } + }, + { + action: IRPCActionType.OPEN_WINDOW, + handler: async (_: IIPCEvent, args: [windowName: IWindowList]) => { + const window = windowManager.get(args[0]) + if (window) { + window.show() + } + } + }, + { + action: IRPCActionType.OPEN_MANUAL_WINDOW, + handler: async () => { + windowManager.get(IWindowList.MANUAL_WINDOW)!.show() + } + }, + { + action: IRPCActionType.OPEN_MINI_WINDOW, + handler: async () => { + openMiniWindow() + } + }, + { + action: IRPCActionType.CLOSE_WINDOW, + handler: async () => { + const window = BrowserWindow.getFocusedWindow() + if (process.platform === 'linux') { + window?.hide() + } else { + window?.close() + } + } + }, + { + action: IRPCActionType.MINIMIZE_WINDOW, + handler: async () => { + const window = BrowserWindow.getFocusedWindow() + window?.minimize() + } + }, + { + action: IRPCActionType.SHOW_MINI_PAGE_MENU, + handler: async () => { + const window = windowManager.get(IWindowList.MINI_WINDOW)! + const menu = buildMiniPageMenu() + menu.popup({ + window + }) + } + }, + { + action: IRPCActionType.SHOW_MAIN_PAGE_MENU, + handler: async () => { + const window = windowManager.get(IWindowList.SETTING_WINDOW)! + const menu = buildMainPageMenu(window) + menu.popup({ + window + }) + } + }, + { + action: IRPCActionType.SHOW_UPLOAD_PAGE_MENU, + handler: async () => { + const window = windowManager.get(IWindowList.SETTING_WINDOW)! + const menu = buildPicBedListMenu() + menu.popup({ + window + }) + } + }, + { + action: IRPCActionType.SHOW_PLUGIN_PAGE_MENU, + handler: async (_: IIPCEvent, args: [plugin: IPicGoPlugin]) => { + const window = windowManager.get(IWindowList.SETTING_WINDOW)! + const menu = buildPluginPageMenu(args[0]) + menu.popup({ + window + }) + } + }, + { + action: IRPCActionType.SET_MINI_WINDOW_POS, + handler: async (_: IIPCEvent, args: [pos: IMiniWindowPos]) => { + const window = BrowserWindow.getFocusedWindow() + window?.setBounds(args[0]) + } + }, + { + action: IRPCActionType.MINI_WINDOW_ON_TOP, + handler: async (_: IIPCEvent, args: [isOnTop: boolean]) => { + const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! + miniWindow.setAlwaysOnTop(args[0]) + } + }, + { + action: IRPCActionType.MAIN_WINDOW_ON_TOP, + handler: async () => { + const mainWindow = windowManager.get(IWindowList.SETTING_WINDOW)! + const isAlwaysOnTop = mainWindow.isAlwaysOnTop() + mainWindow.setAlwaysOnTop(!isAlwaysOnTop) + } + }, + { + action: IRPCActionType.UPDATE_MINI_WINDOW_ICON, + handler: async (_: IIPCEvent, args: [iconPath: string]) => { + const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! + miniWindow.webContents.send('updateMiniIcon', args[0]) + } + }, + { + action: IRPCActionType.REFRESH_SETTING_WINDOW, + handler: async () => { + const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! + settingWindow.webContents.reloadIgnoringCache() + } + } +] diff --git a/src/main/events/rpc/routes/toolbox/checkClipboardUpload.ts b/src/main/events/rpc/routes/toolbox/checkClipboardUpload.ts index 5404b84..77fefa7 100644 --- a/src/main/events/rpc/routes/toolbox/checkClipboardUpload.ts +++ b/src/main/events/rpc/routes/toolbox/checkClipboardUpload.ts @@ -1,19 +1,14 @@ -// External dependencies import fs from 'fs-extra' import path from 'path' -// Electron modules +import { dbPathChecker, defaultConfigPath } from '@core/datastore/dbChecker' -// Custom utilities and modules -import { dbPathChecker, defaultConfigPath } from '~/main/apis/core/datastore/dbChecker' -import { sendToolboxResWithType } from './utils' +import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils' +import { T } from '~/i18n' -// Custom types/enums -import { IToolboxItemCheckStatus, IToolboxItemType } from '~/universal/types/enum' +import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum' -// External utility functions -import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' -import { T } from '~/main/i18n' +import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static' const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD) diff --git a/src/main/events/rpc/routes/toolbox/checkFile.ts b/src/main/events/rpc/routes/toolbox/checkFile.ts index 7422065..f5a00cb 100644 --- a/src/main/events/rpc/routes/toolbox/checkFile.ts +++ b/src/main/events/rpc/routes/toolbox/checkFile.ts @@ -1,20 +1,14 @@ -// External dependencies +import { IpcMainEvent } from 'electron' import fs from 'fs-extra' import path from 'path' -// Electron modules -import { IpcMainEvent } from 'electron' +import { dbPathChecker } from '@core/datastore/dbChecker' +import { GalleryDB, DB_PATH } from '@core/datastore' -// Custom utilities and modules -import { dbPathChecker } from '~/main/apis/core/datastore/dbChecker' -import { GalleryDB, DB_PATH } from '~/main/apis/core/datastore' -import { sendToolboxResWithType } from './utils' +import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils' +import { T } from '~/i18n' -// Custom types/enums -import { IToolboxItemCheckStatus, IToolboxItemType } from '~/universal/types/enum' - -// External utility functions -import { T } from '~/main/i18n' +import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum' export const checkFileMap: IToolboxCheckerMap< IToolboxItemType.IS_CONFIG_FILE_BROKEN | IToolboxItemType.IS_GALLERY_FILE_BROKEN diff --git a/src/main/events/rpc/routes/toolbox/checkProxy.ts b/src/main/events/rpc/routes/toolbox/checkProxy.ts index 15ba950..5e7b680 100644 --- a/src/main/events/rpc/routes/toolbox/checkProxy.ts +++ b/src/main/events/rpc/routes/toolbox/checkProxy.ts @@ -1,24 +1,17 @@ -// External dependencies -import fs from 'fs-extra' + import axios, { AxiosRequestConfig } from 'axios' +import fs from 'fs-extra' +import { IConfig } from 'piclist' import tunnel from 'tunnel' -// Electron modules +import { dbPathChecker } from '@core/datastore/dbChecker' -// Custom utilities and modules -import { dbPathChecker } from '~/main/apis/core/datastore/dbChecker' -import { sendToolboxResWithType } from './utils' +import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils' +import { T } from '~/i18n' -// Custom types/enums -import { IToolboxItemCheckStatus, IToolboxItemType } from '~/universal/types/enum' +import { IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum' -// External utility functions - -// Custom types/enums -import { IConfig } from 'piclist' -import { T } from '~/main/i18n' - -const getProxy = (proxyStr: string): AxiosRequestConfig['proxy'] | false => { +function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null { if (proxyStr) { try { const proxyOptions = new URL(proxyStr) @@ -27,10 +20,9 @@ const getProxy = (proxyStr: string): AxiosRequestConfig['proxy'] | false => { port: parseInt(proxyOptions.port || '0', 10), protocol: proxyOptions.protocol } - } catch (e) { - } + } catch (e) {} } - return false + return null } const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY) diff --git a/src/main/events/rpc/routes/toolbox/index.ts b/src/main/events/rpc/routes/toolbox/index.ts index d80e0be..dfc7db1 100644 --- a/src/main/events/rpc/routes/toolbox/index.ts +++ b/src/main/events/rpc/routes/toolbox/index.ts @@ -1,8 +1,10 @@ -import { IRPCActionType, IToolboxItemType } from '~/universal/types/enum' -import { RPCRouter } from '../../router' -import { checkFileMap, fixFileMap } from './checkFile' -import { checkClipboardUploadMap, fixClipboardUploadMap } from './checkClipboardUpload' -import { checkProxyMap } from './checkProxy' +import { checkClipboardUploadMap, fixClipboardUploadMap } from '~/events/rpc/routes/toolbox/checkClipboardUpload' +import { checkFileMap, fixFileMap } from '~/events/rpc/routes/toolbox/checkFile' +import { checkProxyMap } from '~/events/rpc/routes/toolbox/checkProxy' +import { RPCRouter } from '~/events/rpc/router' + +import { IRPCActionType, IRPCType, IToolboxItemType } from '#/types/enum' +import { IpcMainEvent } from 'electron' const toolboxRouter = new RPCRouter() @@ -18,30 +20,37 @@ const toolboxFixMap: Partial> = { } toolboxRouter - .add(IRPCActionType.TOOLBOX_CHECK, async (args, event) => { - const [type] = args as IToolboxCheckArgs - if (type) { - const handler = toolboxCheckMap[type] - if (handler) { - handler(event) - } - } else { - // do check all - for (const key in toolboxCheckMap) { - const handler = toolboxCheckMap[key as IToolboxItemType] + .add( + IRPCActionType.TOOLBOX_CHECK, + async (event, args) => { + const [type] = args as IToolboxCheckArgs + if (type) { + const handler = toolboxCheckMap[type] if (handler) { - handler(event) + handler(event as IpcMainEvent) + } + } else { + // do check all + for (const key in toolboxCheckMap) { + const handler = toolboxCheckMap[key as IToolboxItemType] + if (handler) { + handler(event as IpcMainEvent) + } } } - } - }) - .add(IRPCActionType.TOOLBOX_CHECK_FIX, async (args, event) => { - const [type] = args as IToolboxCheckArgs - const handler = toolboxFixMap[type] - if (handler) { - return await handler(event) - } - }) + }, + IRPCType.SEND + ) + .add( + IRPCActionType.TOOLBOX_CHECK_FIX, async (event, args) => { + const [type] = args as IToolboxCheckArgs + const handler = toolboxFixMap[type] + if (handler) { + return await handler(event as IpcMainEvent) + } + }, + IRPCType.INVOKE + ) export { toolboxRouter diff --git a/src/main/events/rpc/routes/toolbox/utils.ts b/src/main/events/rpc/routes/toolbox/utils.ts index ed4befe..4cb3428 100644 --- a/src/main/events/rpc/routes/toolbox/utils.ts +++ b/src/main/events/rpc/routes/toolbox/utils.ts @@ -1,9 +1,11 @@ import { IpcMainEvent } from 'electron' -import { IRPCActionType, IToolboxItemType } from '~/universal/types/enum' +import { IRPCActionType, IToolboxItemType } from '#/types/enum' -export const sendToolboxResWithType = (type: IToolboxItemType) => (event: IpcMainEvent, res?: Omit) => { - return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, { - ...res, - type - }) +export function sendToolboxResWithType (type: IToolboxItemType) { + return (event: IpcMainEvent, res?: Omit) => { + return event.sender.send(IRPCActionType.TOOLBOX_CHECK_RES, { + ...res, + type + }) + } } diff --git a/src/main/events/rpc/routes/tray/index.ts b/src/main/events/rpc/routes/tray/index.ts new file mode 100644 index 0000000..40906dc --- /dev/null +++ b/src/main/events/rpc/routes/tray/index.ts @@ -0,0 +1,71 @@ +import { + Notification +} from 'electron' + +import { RPCRouter } from '~/events/rpc/router' +import { generateShortUrl, setTrayToolTip, handleCopyUrl } from '~/utils/common' + +import { IRPCActionType, IRPCType, IPasteStyle, IWindowList } from '#/types/enum' + +import db, { GalleryDB } from '@core/datastore' + +import uploader from 'apis/app/uploader' +import windowManager from 'apis/app/window/windowManager' + +import { T } from '~/i18n' + +import pasteTemplate from '~/utils/pasteTemplate' + +import { configPaths } from '#/utils/configPaths' + +const trayRouter = new RPCRouter() + +const trayRoutes = [ + { + action: IRPCActionType.TRAY_SET_TOOL_TIP, + handler: async (_: IIPCEvent, args: [text: string]) => { + setTrayToolTip(args[0]) + } + }, + { + action: IRPCActionType.TRAY_GET_SHORT_URL, + handler: async (_: IIPCEvent, args: [url: string]) => { + return await generateShortUrl(args[0]) + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.TRAY_UPLOAD_CLIPBOARD_FILES, + handler: async () => { + const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)! + // macOS use builtin clipboard is OK + 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) + if (isShowResultNotification) { + const notification = new Notification({ + title: T('UPLOAD_SUCCEED'), + body: img[0].imgUrl! + // icon: file[0] + // icon: img[0].imgUrl + }) + notification.show() + } + await GalleryDB.getInstance().insert(img[0]) + trayWindow.webContents.send('clipboardFiles', []) + if (windowManager.has(IWindowList.SETTING_WINDOW)) { + windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery') + } + } + trayWindow.webContents.send('uploadFiles') + } + } +] + +trayRouter.addBatch(trayRoutes) + +export { + trayRouter +} diff --git a/src/main/events/rpc/routes/upload/index.ts b/src/main/events/rpc/routes/upload/index.ts new file mode 100644 index 0000000..740f7cb --- /dev/null +++ b/src/main/events/rpc/routes/upload/index.ts @@ -0,0 +1,35 @@ +import { RPCRouter } from '~/events/rpc/router' +import getPicBeds from '~/utils/getPicBeds' +import { uploadChoosedFiles, uploadClipboardFiles } from '~/apis/app/uploader/apis' + +import { IRPCActionType, IRPCType } from '#/types/enum' + +const uploadRouter = new RPCRouter() + +const uploadRoutes = [ + { + action: IRPCActionType.MAIN_GET_PICBED, + handler: async () => { + return getPicBeds() + }, + type: IRPCType.INVOKE + }, + { + action: IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE, + handler: async () => { + uploadClipboardFiles() + } + }, + { + action: IRPCActionType.UPLOAD_CHOOSED_FILES, + handler: async (evt: IIPCEvent, args: [files: IFileWithPath[]]) => { + return uploadChoosedFiles(evt.sender, args[0]) + } + } +] + +uploadRouter.addBatch(uploadRoutes) + +export { + uploadRouter +} diff --git a/src/main/fileServer/index.ts b/src/main/fileServer/index.ts index 3f51d8a..06ec7ca 100644 --- a/src/main/fileServer/index.ts +++ b/src/main/fileServer/index.ts @@ -1,21 +1,19 @@ -// External dependencies import http from 'http' import fs from 'fs-extra' import path from 'path' -// Electron modules - -// Custom utilities and modules import picgo from '@core/picgo' -import logger from '../apis/core/picgo/logger' +import logger from '@core/picgo/logger' export const imgFilePath = path.join(picgo.baseDir, 'imgTemp') fs.ensureDirSync(imgFilePath) const serverPort = 36699 +let server: http.Server + export function startFileServer () { - const server = http.createServer((req, res) => { + server = http.createServer((req, res) => { const requestPath = req.url?.split('?')[0] const filePath = path.join(imgFilePath, decodeURIComponent(requestPath as string)) @@ -35,3 +33,9 @@ export function startFileServer () { logger.error(err) }) } + +export function stopFileServer () { + server.close(() => { + logger.info('File server is stopped') + }) +} diff --git a/src/main/i18n/index.ts b/src/main/i18n/index.ts index be922de..bf1245f 100644 --- a/src/main/i18n/index.ts +++ b/src/main/i18n/index.ts @@ -1,16 +1,9 @@ -// External dependencies -import yaml from 'js-yaml' -import { ObjectAdapter, I18n } from '@picgo/i18n' -import path from 'path' import fs from 'fs-extra' +import yaml from 'js-yaml' +import path from 'path' -// Electron modules +import { ObjectAdapter, I18n } from '@picgo/i18n' -// Custom utilities and modules - -// Custom types/enums - -// External utility functions import { builtinI18nList } from '#/i18n' class I18nManager { diff --git a/src/main/lifeCycle/errorHandler.ts b/src/main/lifeCycle/errorHandler.ts index b851c4f..ed6bf3f 100644 --- a/src/main/lifeCycle/errorHandler.ts +++ b/src/main/lifeCycle/errorHandler.ts @@ -1,6 +1,8 @@ import path from 'path' import { app } from 'electron' -import { getLogger } from 'apis/core/utils/localLogger' + +import { getLogger } from '@core/utils/localLogger' + const STORE_PATH = app.getPath('userData') const LOG_PATH = path.join(STORE_PATH, 'piclist-gui-local.log') diff --git a/src/main/lifeCycle/index.ts b/src/main/lifeCycle/index.ts index 0ce5304..ccba9f7 100644 --- a/src/main/lifeCycle/index.ts +++ b/src/main/lifeCycle/index.ts @@ -1,4 +1,5 @@ -import './errorHandler' +import axios from 'axios' +import fs from 'fs-extra' import { app, globalShortcut, @@ -8,46 +9,50 @@ import { screen, shell } from 'electron' +import { UpdateInfo, autoUpdater } from 'electron-updater' +import path from 'path' import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' -import beforeOpen from '~/main/utils/beforeOpen' -import ipcList from '~/main/events/ipcList' -import busEventList from '~/main/events/busEventList' -import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum' -import windowManager from 'apis/app/window/windowManager' + +import bus from '@core/bus' +import db from '@core/datastore' +import picgo from '@core/picgo' +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 server from '~/main/server/index' -import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' -import { getUploadFiles } from '~/main/utils/handleArgv' -import db from '~/main/apis/core/datastore' -import bus from '@core/bus' -import logger from 'apis/core/picgo/logger' -import picgo from 'apis/core/picgo' -import fixPath from './fixPath' -import { clearTempFolder } from '../manage/utils/common' -import { initI18n } from '~/main/utils/handleI18n' -import { remoteNoticeHandler } from 'apis/app/remoteNotice' -import { manageIpcList } from '../manage/events/ipcList' -import getManageApi from '../manage/Main' -import UpDownTaskQueue from '../manage/datastore/upDownTaskQueue' -import { T } from '~/main/i18n' -import { UpdateInfo, autoUpdater } from 'electron-updater' -import updateChecker from '../utils/updateChecker' -import clipboardPoll from '../utils/clipboardPoll' -import path from 'path' -import { CLIPBOARD_IMAGE_FOLDER } from '~/universal/utils/static' -import fs from 'fs-extra' -import { startFileServer } from '../fileServer' -import webServer from '../server/webServer' -import axios from 'axios' -import { configPaths } from '~/universal/utils/configPaths' +import windowManager from 'apis/app/window/windowManager' + +import busEventList from '~/events/busEventList' +import { startFileServer, stopFileServer } from '~/fileServer' +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' +import webServer from '~/server/webServer' +import beforeOpen from '~/utils/beforeOpen' +import clipboardPoll from '~/utils/clipboardPoll' +import { getUploadFiles } from '~/utils/handleArgv' +import { initI18n } from '~/utils/handleI18n' +import updateChecker from '~/utils/updateChecker' + +import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' +import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static' +import { rpcServer } from '~/events/rpc' + const isDevelopment = process.env.NODE_ENV !== 'production' const handleStartUpFiles = (argv: string[], cwd: string) => { @@ -141,7 +146,7 @@ class LifeCycle { fixPath() beforeOpen() initI18n() - ipcList.listen() + rpcServer.start() getManageApi() UpDownTaskQueue.getInstance() manageIpcList.listen() @@ -167,11 +172,15 @@ 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') { isHideDock ? app.dock.hide() : setDockMenu() - startMode !== ISartMode.NO_TRAY && createTray() + startMode !== ISartMode.NO_TRAY && createTray(tooltip) } else { - createTray() + createTray(tooltip) } db.set(configPaths.needReload, false) updateChecker() @@ -223,7 +232,7 @@ class LifeCycle { settingWindow.focus() } const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER) - fs.ensureDir(clipboardDir) + fs.emptyDir(clipboardDir) } app.whenReady().then(readyFunction) } @@ -276,6 +285,8 @@ class LifeCycle { globalShortcut.unregisterAll() bus.removeAllListeners() server.shutdown() + webServer.stop() + stopFileServer() }) // Exit cleanly on request from parent process in development mode. if (isDevelopment) { diff --git a/src/main/manage/Main.ts b/src/main/manage/Main.ts index e2419ef..732c543 100644 --- a/src/main/manage/Main.ts +++ b/src/main/manage/Main.ts @@ -1,6 +1,5 @@ -/* eslint-disable */ -import { manageDbChecker } from './datastore/dbChecker' -import { ManageApi } from './manageApi' +import { manageDbChecker } from '~/manage/datastore/dbChecker' +import { ManageApi } from '~/manage/manageApi' manageDbChecker() const getManageApi = (picBedName: string = 'placeholder'): ManageApi => { diff --git a/src/main/manage/apis/aliyun.ts b/src/main/manage/apis/aliyun.ts index ccbe17f..caac669 100644 --- a/src/main/manage/apis/aliyun.ts +++ b/src/main/manage/apis/aliyun.ts @@ -1,38 +1,18 @@ -// Axios -import axios from 'axios' - -// 加密函数、获取文件 MIME 类型、错误格式化函数、新的下载器、并发异步任务池 -import { hmacSha1Base64, getFileMimeType, formatError, NewDownloader, ConcurrencyPromisePool } from '../utils/common' - -// Electron 相关 -import { ipcMain, IpcMainEvent } from 'electron' - -// 快速 XML 解析器 -import { XMLParser } from 'fast-xml-parser' - -// 阿里云 OSS 客户端库 import OSS from 'ali-oss' - -// 路径处理库 +import axios from 'axios' +import { ipcMain, IpcMainEvent } from 'electron' +import { XMLParser } from 'fast-xml-parser' import path from 'path' -// 是否为图片的判断函数 -import { isImage } from '~/renderer/manage/utils/common' - -// 窗口管理器 import windowManager from 'apis/app/window/windowManager' -// 枚举类型声明 -import { IWindowList } from '#/types/enum' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { ManageLogger } from '~/manage/utils/logger' +import { hmacSha1Base64, getFileMimeType, formatError, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common' -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { isImage } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' // 坑爹阿里云 返回数据类型标注和实际各种不一致 class AliyunApi { @@ -53,9 +33,10 @@ class AliyunApi { this.logger = logger } - formatFolder (item: string, slicedPrefix: string) { + formatFolder (item: string, slicedPrefix: string, urlPrefix: string): any { return { key: item, + url: `${urlPrefix}/${item}`, fileSize: 0, formatedTime: '', fileName: item.replace(slicedPrefix, '').replace('/', ''), @@ -288,7 +269,7 @@ class AliyunApi { }) if (res?.res?.statusCode === 200) { res?.prefixes?.forEach((item: string) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) + result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) res?.objects?.forEach((item: OSS.ObjectMeta) => { item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) @@ -348,7 +329,7 @@ class AliyunApi { } } const fullList = [ - ...(res.prefixes?.map((item: string) => this.formatFolder(item, slicedPrefix)) || []), + ...(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)) || []) ] return { diff --git a/src/main/manage/apis/api.ts b/src/main/manage/apis/api.ts index efc9bb5..10b4efe 100644 --- a/src/main/manage/apis/api.ts +++ b/src/main/manage/apis/api.ts @@ -1,14 +1,14 @@ -import AliyunApi from './aliyun' -import GithubApi from './github' -import ImgurApi from './imgur' -import LocalApi from './local' -import QiniuApi from './qiniu' -import S3plistApi from './s3plist' -import SftpApi from './sftp' -import SmmsApi from './smms' -import TcyunApi from './tcyun' -import UpyunApi from './upyun' -import WebdavplistApi from './webdavplist' +import AliyunApi from '~/manage/apis/aliyun' +import GithubApi from '~/manage/apis/github' +import ImgurApi from '~/manage/apis/imgur' +import LocalApi from '~/manage/apis/local' +import QiniuApi from '~/manage/apis/qiniu' +import S3plistApi from '~/manage/apis/s3plist' +import SftpApi from '~/manage/apis/sftp' +import SmmsApi from '~/manage/apis/smms' +import TcyunApi from '~/manage/apis/tcyun' +import UpyunApi from '~/manage/apis/upyun' +import WebdavplistApi from '~/manage/apis/webdavplist' export default { AliyunApi, diff --git a/src/main/manage/apis/github.ts b/src/main/manage/apis/github.ts index f7c4ccc..54d4140 100644 --- a/src/main/manage/apis/github.ts +++ b/src/main/manage/apis/github.ts @@ -1,35 +1,17 @@ -// HTTP 请求库 -import got from 'got' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// HTTP 代理格式化函数、是否为图片的判断函数 -import { formatHttpProxy, isImage } from '~/renderer/manage/utils/common' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { IWindowList } from '#/types/enum' - -// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' - -// got 上传函数、路径处理函数、新的下载器、获取请求代理、获取请求选项、并发异步任务池、错误格式化函数 -import { gotUpload, trimPath, NewDownloader, getAgent, getOptions, ConcurrencyPromisePool, formatError } from '../utils/common' - -// 上传下载任务队列 -import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 文件系统库 import fs from 'fs-extra' - -// 路径处理库 +import got from 'got' import path from 'path' -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +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 { ManageLogger } from '~/manage/utils/logger' + +import { commonTaskStatus, IWindowList } from '#/types/enum' +import { formatHttpProxy, isImage, trimPath } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class GithubApi { token: string @@ -52,11 +34,23 @@ class GithubApi { } } - formatFolder (item: any, slicedPrefix: string) { + formatFolder (item: any, slicedPrefix: string, branch: string, repo: string, cdnUrl: string | undefined) { const key = `${slicedPrefix ? `${slicedPrefix}/` : ''}${item.path}/` + let rawUrl = '' + const placeholders = ['{username}', '{repo}', '{branch}', '{path}'] + 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 : '' + return url.replaceAll(ph, value) + }, cdnUrl) + : `${cdnUrl}/${key}` + : `https://raw.githubusercontent.com/${this.username}/${repo}/${branch}/${key}` + rawUrl = rawUrl.replace(/(? { if (item.type === 'tree') { - result.fullList.push(this.formatFolder(item, slicedPrefix)) + result.fullList.push(this.formatFolder(item, slicedPrefix, branch, repo, cdnUrl)) } else { result.fullList.push(this.formatFile(item, slicedPrefix, branch, repo, cdnUrl)) } diff --git a/src/main/manage/apis/imgur.ts b/src/main/manage/apis/imgur.ts index 3b4b452..377254e 100644 --- a/src/main/manage/apis/imgur.ts +++ b/src/main/manage/apis/imgur.ts @@ -1,14 +1,12 @@ -// External dependencies -import fs from 'fs-extra' +import { ipcMain, IpcMainEvent } from 'electron' import FormData from 'form-data' +import fs from 'fs-extra' import got from 'got' import path from 'path' -// Electron modules -import { ipcMain, IpcMainEvent } from 'electron' +import windowManager from 'apis/app/window/windowManager' -// Custom utilities and modules -import { IWindowList } from '#/types/enum' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' import { ConcurrencyPromisePool, formatError, @@ -17,11 +15,11 @@ import { getAgent, gotUpload, NewDownloader -} from '../utils/common' -import ManageLogger from '../utils/logger' -import windowManager from 'apis/app/window/windowManager' -import { formatHttpProxy, isImage } from '~/renderer/manage/utils/common' -import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' +} from '~/manage/utils/common' +import ManageLogger from '~/manage/utils/logger' + +import { commonTaskStatus, IWindowList } from '#/types/enum' +import { formatHttpProxy, isImage } from '#/utils/common' class ImgurApi { userName: string diff --git a/src/main/manage/apis/local.ts b/src/main/manage/apis/local.ts index de25ffd..37d22d5 100644 --- a/src/main/manage/apis/local.ts +++ b/src/main/manage/apis/local.ts @@ -1,33 +1,16 @@ -// 日志记录器 -import ManageLogger from '../utils/logger' - -// 错误格式化函数、端点地址格式化函数、获取内部代理、新的下载器、并发异步任务池 -import { formatError } from '../utils/common' - -// HTTP 代理格式化函数、是否为图片的判断函数 -import { isImage } from '@/manage/utils/common' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { IWindowList } from '#/types/enum' - -// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' - -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus, downloadTaskSpecialStatus } from '../datastore/upDownTaskQueue' - -// 文件系统库 import fs from 'fs-extra' - -// 路径处理库 import path from 'path' import * as fsWalk from '@nodelib/fs.walk' -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import windowManager from 'apis/app/window/windowManager' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { formatError } from '~/manage/utils/common' +import ManageLogger from '~/manage/utils/logger' + +import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { isImage } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class LocalApi { logger: ManageLogger diff --git a/src/main/manage/apis/qiniu.ts b/src/main/manage/apis/qiniu.ts index 1b64711..eaaea40 100644 --- a/src/main/manage/apis/qiniu.ts +++ b/src/main/manage/apis/qiniu.ts @@ -1,35 +1,18 @@ -// Axios + import axios from 'axios' - -// 加密函数、获取文件 MIME 类型、新的下载器、错误格式化函数、并发异步任务池 -import { hmacSha1Base64, getFileMimeType, NewDownloader, formatError, ConcurrencyPromisePool } from '../utils/common' - -// 七牛云客户端库 +import { ipcMain, IpcMainEvent } from 'electron' +import path from 'path' import qiniu from 'qiniu/index' -// 路径处理库 -import path from 'path' - -// 是否为图片的判断函数 -import { isImage } from '~/renderer/manage/utils/common' - -// 窗口管理器 import windowManager from 'apis/app/window/windowManager' -// 枚举类型声明 -import { IWindowList } from '#/types/enum' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { hmacSha1Base64, getFileMimeType, NewDownloader, formatError, ConcurrencyPromisePool } from '~/manage/utils/common' +import { ManageLogger } from '~/manage/utils/logger' -// Electron 相关 -import { ipcMain, IpcMainEvent } from 'electron' - -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { isImage } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class QiniuApi { mac: qiniu.auth.digest.Mac @@ -52,10 +35,11 @@ class QiniuApi { this.logger = logger } - formatFolder (item: string, slicedPrefix: string) { + formatFolder (item: string, slicedPrefix: string, urlPrefix: string) { return { Key: item, key: item, + url: `${urlPrefix}/${item}`, fileSize: 0, fileName: item.replace(slicedPrefix, '').replace('/', ''), isDir: true, @@ -342,7 +326,7 @@ class QiniuApi { }) if (res && res.respInfo.statusCode === 200) { res.respBody && res.respBody.commonPrefixes && res.respBody.commonPrefixes.forEach((item: any) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) + result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) res.respBody && res.respBody.items && res.respBody.items.forEach((item: any) => { item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) @@ -409,7 +393,7 @@ class QiniuApi { if (res?.respInfo?.statusCode === 200) { if (res.respBody?.commonPrefixes) { res.respBody.commonPrefixes.forEach((item: string) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) + result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) } if (res.respBody?.items) { diff --git a/src/main/manage/apis/s3plist.ts b/src/main/manage/apis/s3plist.ts index 8875516..ca69f41 100644 --- a/src/main/manage/apis/s3plist.ts +++ b/src/main/manage/apis/s3plist.ts @@ -1,4 +1,3 @@ -// AWS S3 相关 import { S3Client, ListBucketsCommand, @@ -12,52 +11,30 @@ import { DeleteObjectCommand, DeleteObjectsCommand, PutObjectCommand, - S3ClientConfig + S3ClientConfig, + CreateBucketCommand, + PutPublicAccessBlockCommand, + PutBucketAclCommand } from '@aws-sdk/client-s3' - -// AWS S3 上传和进度 import { Upload, Progress } from '@aws-sdk/lib-storage' - -// AWS S3 请求签名 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' - -// HTTP 和 HTTPS 模块 -import https from 'https' -import http, { AgentOptions } from 'http' import { NodeHttpHandler } from '@smithy/node-http-handler' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// 端点地址格式化函数、错误格式化函数、获取请求代理、获取文件 MIME 类型、新的下载器、并发异步任务池 -import { formatEndpoint, formatError, getAgent, getFileMimeType, NewDownloader, ConcurrencyPromisePool } from '../utils/common' - -// 是否为图片的判断函数、HTTP 代理格式化函数 -import { isImage, formatHttpProxy } from '@/manage/utils/common' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { IWindowList } from '#/types/enum' - -// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' - -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 文件系统库 import fs from 'fs-extra' - -// 路径处理库 +import http, { AgentOptions } from 'http' +import https from 'https' import path from 'path' -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import windowManager from 'apis/app/window/windowManager' -// dogecloudApi -import { dogecloudApi, DogecloudToken, getTempToken } from '../utils/dogeAPI' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { formatError, getAgent, getFileMimeType, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common' +import { dogecloudApi, DogecloudToken, getTempToken } from '~/manage/utils/dogeAPI' +import { ManageLogger } from '~/manage/utils/logger' + +import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { isImage, formatEndpoint, formatHttpProxy } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class S3plistApi { baseOptions: S3ClientConfig @@ -141,9 +118,10 @@ class S3plistApi { logParam = (error:any, method: string) => this.logger.error(formatError(error, { class: 'S3plistApi', method })) - formatFolder (item: CommonPrefix, slicedPrefix: string): any { + formatFolder (item: CommonPrefix, slicedPrefix: string, urlPrefix: string): any { return { Key: item.Prefix, + url: `${urlPrefix}/${item.Prefix}`, fileSize: 0, formatedTime: '', fileName: item.Prefix?.replace(slicedPrefix, '').replace('/', ''), @@ -171,6 +149,89 @@ class S3plistApi { } } + async putPublicAccess (bucketName: string, client: S3Client) { + const input = { + Bucket: bucketName, + PublicAccessBlockConfiguration: { + BlockPublicAcls: false, + IgnorePublicAcls: false, + BlockPublicPolicy: false, + RestrictPublicBuckets: false + } + } + const command = new PutPublicAccessBlockCommand(input) + const data = await client.send(command) + if (data.$metadata.httpStatusCode !== 200) { + this.logParam(data, 'putPublicAccess') + throw new Error('manage.setting.putPublicAccessError') + } + } + + /** + * 新建存储桶 + * @param {Object} configMap + * configMap = { + * BucketName: string, + * region: string, + * acl: string + * } + */ + async createBucket (configMap: IStringKeyMap): Promise { + const { BucketName, region, acl, endpoint } = configMap + try { + await this.getDogeCloudToken() + const options = Object.assign({}, this.baseOptions) as S3ClientConfig + options.region = String(region) || 'us-east-1' + const client = new S3Client(options) + const command = new ListBucketsCommand({}) + const data = await client.send(command) + if (data.$metadata.httpStatusCode === 200) { + const bucketList = data.Buckets?.map((item) => item.Name) + if (bucketList?.includes(BucketName)) { + return true + } + } + if (endpoint === '' || endpoint.includes('amazonaws')) { + const createCommand = new CreateBucketCommand({ + Bucket: BucketName, + ObjectOwnership: 'BucketOwnerPreferred' + }) + const createData = await client.send(createCommand) + if (createData.$metadata.httpStatusCode === 200) { + if (acl !== 'private') { + await this.putPublicAccess(BucketName, client) + const putACLCommand = new PutBucketAclCommand({ + Bucket: BucketName, + ACL: acl + }) + const putACLData = await client.send(putACLCommand) + if (putACLData.$metadata.httpStatusCode !== 200) { + this.logParam(putACLData, 'createBucket') + return false + } + } + return true + } else { + this.logParam(createData, 'createBucket') + } + } else { + const createCommand = new CreateBucketCommand({ + Bucket: BucketName, + ACL: acl + }) + const createData = await client.send(createCommand) + if (createData.$metadata.httpStatusCode === 200) { + return true + } else { + this.logParam(createData, 'createBucket') + } + } + } catch (error) { + this.logParam(error, 'createBucket') + } + return false + } + /** * 获取存储桶列表 */ @@ -333,7 +394,7 @@ class S3plistApi { res = await client.send(command) if (res.$metadata.httpStatusCode === 200) { res.CommonPrefixes && res.CommonPrefixes.forEach((item: CommonPrefix) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) + result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) res.Contents && res.Contents.forEach((item: _Object) => { result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) @@ -385,7 +446,7 @@ class S3plistApi { const data = await client.send(command) if (data.$metadata.httpStatusCode === 200) { result.fullList = [ - ...(data.CommonPrefixes?.map(item => this.formatFolder(item, slicedPrefix)) || []), + ...(data.CommonPrefixes?.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)) || []), ...(data.Contents?.map(item => this.formatFile(item, slicedPrefix, urlPrefix)) || []) ] result.isTruncated = data.IsTruncated || false diff --git a/src/main/manage/apis/sftp.ts b/src/main/manage/apis/sftp.ts index 802532d..2f2177d 100644 --- a/src/main/manage/apis/sftp.ts +++ b/src/main/manage/apis/sftp.ts @@ -1,33 +1,18 @@ -// 日志记录器 -import ManageLogger from '../utils/logger' - -// SSH 客户端 -import SSHClient from '~/main/utils/sshClient' - -// 错误格式化函数、新的下载器、并发异步任务池 -import { formatError } from '../utils/common' - -// 是否为图片的判断函数 -import { isImage } from '@/manage/utils/common' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { IWindowList } from '#/types/enum' - -// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' - -// 上传下载任务队列 -import UpDownTaskQueue, { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '../datastore/upDownTaskQueue' - -// 路径处理库 import path from 'path' -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' -import { Undefinable } from '~/universal/types/manage' +import windowManager from 'apis/app/window/windowManager' + +import SSHClient from '~/utils/sshClient' + +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { formatError } from '~/manage/utils/common' +import ManageLogger from '~/manage/utils/logger' + +import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { Undefinable } from '#/types/manage' +import { isImage } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' interface listDirResult { permissions: string diff --git a/src/main/manage/apis/smms.ts b/src/main/manage/apis/smms.ts index 3f28427..06b533b 100644 --- a/src/main/manage/apis/smms.ts +++ b/src/main/manage/apis/smms.ts @@ -1,35 +1,17 @@ -// 是否为图片的判断函数 -import { isImage } from '@/manage/utils/common' - -// Axios 和 Axios 实例类型声明 import axios, { AxiosInstance } from 'axios' - -// 窗口管理器 -import windowManager from 'apis/app/window/windowManager' - -// 枚举类型声明 -import { IWindowList } from '#/types/enum' - -// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' - -// 表单数据库 import FormData from 'form-data' - -// 文件系统库 import fs from 'fs-extra' - -// 获取文件 MIME 类型、got 上传函数、新的下载器、并发异步任务池、错误格式化函数 -import { getFileMimeType, gotUpload, NewDownloader, ConcurrencyPromisePool, formatError } from '../utils/common' - -// 路径处理库 import path from 'path' -// 上传下载任务队列 -import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' +import windowManager from 'apis/app/window/windowManager' -// 日志记录器 -import { ManageLogger } from '../utils/logger' +import { getFileMimeType, gotUpload, NewDownloader, ConcurrencyPromisePool, formatError } from '~/manage/utils/common' +import { ManageLogger } from '~/manage/utils/logger' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' + +import { commonTaskStatus, IWindowList } from '#/types/enum' +import { isImage } from '#/utils/common' class SmmsApi { baseUrl = 'https://smms.app/api/v2' diff --git a/src/main/manage/apis/tcyun.ts b/src/main/manage/apis/tcyun.ts index 296ef8b..08509b1 100644 --- a/src/main/manage/apis/tcyun.ts +++ b/src/main/manage/apis/tcyun.ts @@ -1,38 +1,18 @@ -// 腾讯云 COS SDK + import COS from 'cos-nodejs-sdk-v5' - -// 文件系统库 +import { ipcMain, IpcMainEvent } from 'electron' import fs from 'fs-extra' - -// 路径处理库 import path from 'path' -// 是否为图片的判断函数 -import { isImage } from '~/renderer/manage/utils/common' - -// URL 编码处理函数 -import { handleUrlEncode } from '~/universal/utils/common' - -// 窗口管理器 import windowManager from 'apis/app/window/windowManager' -// 枚举类型声明 -import { IWindowList } from '#/types/enum' +import { formatError, getFileMimeType } from '~/manage/utils/common' +import { ManageLogger } from '~/manage/utils/logger' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' -// Electron 相关 -import { ipcMain, IpcMainEvent } from 'electron' - -// 错误格式化函数、获取文件 MIME 类型 -import { formatError, getFileMimeType } from '../utils/common' - -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus, downloadTaskSpecialStatus } from '../datastore/upDownTaskQueue' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import { handleUrlEncode, isImage } from '#/utils/common' +import { commonTaskStatus, downloadTaskSpecialStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class TcyunApi { ctx: COS @@ -46,11 +26,12 @@ class TcyunApi { this.logger = logger } - formatFolder (item: {Prefix: string}, slicedPrefix: string): any { + formatFolder (item: {Prefix: string}, slicedPrefix: string, urlPrefix: string) { return { ...item, key: item.Prefix, fileSize: 0, + url: `${urlPrefix}/${item.Prefix}`, formatedTime: '', fileName: item.Prefix.replace(slicedPrefix, '').replace('/', ''), isDir: true, @@ -108,13 +89,8 @@ class TcyunApi { * acl: private | publicRead | publicReadWrite */ async createBucket (configMap: IStringKeyMap): Promise < boolean > { - const aclTransMap: IStringKeyMap = { - private: 'private', - publicRead: 'public-read', - publicReadWrite: 'public-read-write' - } const res = await this.ctx.putBucket({ - ACL: aclTransMap[configMap.acl], + ACL: configMap.acl, Bucket: configMap.BucketName, Region: configMap.region }) @@ -196,7 +172,7 @@ class TcyunApi { }) if (res?.statusCode === 200) { result.fullList.push( - ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix)), + ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)), ...res.Contents.filter(item => parseInt(item.Size) !== 0) .map(item => this.formatFile(item, slicedPrefix, urlPrefix)) ) @@ -252,7 +228,7 @@ class TcyunApi { } const result = { fullList: [ - ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix)), + ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix, urlPrefix)), ...res.Contents.filter(item => parseInt(item.Size) !== 0) .map(item => this.formatFile(item, slicedPrefix, urlPrefix)) ], diff --git a/src/main/manage/apis/upyun.ts b/src/main/manage/apis/upyun.ts index a961466..1b09fcf 100644 --- a/src/main/manage/apis/upyun.ts +++ b/src/main/manage/apis/upyun.ts @@ -1,40 +1,19 @@ +import axios from 'axios' +import { ipcMain, IpcMainEvent } from 'electron' +import FormData from 'form-data' +import fs from 'fs-extra' +import path from 'path' import Upyun from 'upyun' -// 加密函数、获取文件 MIME 类型、新的下载器、got 上传函数、并发异步任务池、错误格式化函数 -import { md5, hmacSha1Base64, getFileMimeType, NewDownloader, gotUpload, ConcurrencyPromisePool, formatError } from '../utils/common' - -// 是否为图片的判断函数 -import { isImage } from '~/renderer/manage/utils/common' - -// 窗口管理器 import windowManager from 'apis/app/window/windowManager' -// 枚举类型声明 -import { IWindowList } from '#/types/enum' +import { md5, hmacSha1Base64, getFileMimeType, NewDownloader, gotUpload, ConcurrencyPromisePool, formatError } from '~/manage/utils/common' +import { ManageLogger } from '~/manage/utils/logger' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' -// Electron 相关 -import { ipcMain, IpcMainEvent } from 'electron' - -// Axios -import axios from 'axios' - -// 表单数据库 -import FormData from 'form-data' - -// 文件系统库 -import fs from 'fs-extra' - -// 路径处理库 -import path from 'path' - -// 上传下载任务队列 -import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 日志记录器 -import { ManageLogger } from '../utils/logger' - -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import { commonTaskStatus, IWindowList } from '#/types/enum' +import { isImage } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' class UpyunApi { ser: Upyun.Service @@ -67,11 +46,16 @@ class UpyunApi { return `_upt=${upt}` } - formatFolder (item: any, slicedPrefix: string) { + formatFolder (item: any, slicedPrefix: string, urlPrefix: string) { const key = `${slicedPrefix}${item.name}/` + let url = `${urlPrefix}/${key}` + if (this.antiLeechToken) { + url = `${url}?${this.getAntiLeechParam(key)}` + } return { ...item, key, + url, fileSize: 0, formatedTime: '', fileName: item.name, @@ -202,7 +186,7 @@ class UpyunApi { if (res) { res.files?.forEach((item: any) => { item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) - item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix)) + item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) window.webContents.send('refreshFileTransferList', result) } else { @@ -252,7 +236,7 @@ class UpyunApi { if (res) { res.files?.forEach((item: any) => { item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) - item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix)) + item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix, urlPrefix)) }) result.isTruncated = res.next !== this.stopMarker result.nextMarker = res.next diff --git a/src/main/manage/apis/webdavplist.ts b/src/main/manage/apis/webdavplist.ts index 8fd0236..9959e71 100644 --- a/src/main/manage/apis/webdavplist.ts +++ b/src/main/manage/apis/webdavplist.ts @@ -1,41 +1,22 @@ -// 日志记录器 -import ManageLogger from '../utils/logger' - -// WebDAV 客户端库 -import { createClient, WebDAVClient, FileStat, ProgressEvent, AuthType, WebDAVClientOptions } from 'webdav' - -// 错误格式化函数、端点地址格式化函数、获取内部代理、新的下载器、并发异步任务池 -import { formatError, formatEndpoint, getInnerAgent, NewDownloader, ConcurrencyPromisePool } from '../utils/common' - -// HTTP 代理格式化函数、是否为图片的判断函数 -import { formatHttpProxy, isImage } from '@/manage/utils/common' - -// HTTP 和 HTTPS 模块 +import { ipcMain, IpcMainEvent } from 'electron' +import fs from 'fs-extra' import http from 'http' import https from 'https' +import path from 'path' +import { createClient, WebDAVClient, FileStat, ProgressEvent, AuthType, WebDAVClientOptions } from 'webdav' -// 窗口管理器 import windowManager from 'apis/app/window/windowManager' -// 枚举类型声明 -import { IWindowList } from '#/types/enum' +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { formatError, getInnerAgent, NewDownloader, ConcurrencyPromisePool } from '~/manage/utils/common' +import ManageLogger from '~/manage/utils/logger' -// Electron 相关 -import { ipcMain, IpcMainEvent } from 'electron' - -// 上传下载任务队列 -import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' - -// 文件系统库 -import fs from 'fs-extra' - -// 路径处理库 -import path from 'path' - -// 取消下载任务的加载文件列表、刷新下载文件传输列表 -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' import { getAuthHeader } from '@/manage/utils/digestAuth' +import { commonTaskStatus, IWindowList, uploadTaskSpecialStatus } from '#/types/enum' +import { isImage, formatEndpoint, formatHttpProxy } from '#/utils/common' +import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' + class WebdavplistApi { endpoint: string username: string diff --git a/src/main/manage/datastore/db.ts b/src/main/manage/datastore/db.ts index 8a23cde..7c65eef 100644 --- a/src/main/manage/datastore/db.ts +++ b/src/main/manage/datastore/db.ts @@ -1,19 +1,19 @@ -/* eslint-disable */ import { JSONStore } from '@picgo/store' import { IJSON } from '@picgo/store/dist/types' -import { ManageApiType, ManageConfigType } from '~/universal/types/manage' + +import { IManageApiType, IManageConfigType } from '#/types/manage' class ManageDB { - readonly #ctx: ManageApiType + readonly #ctx: IManageApiType readonly #db: JSONStore - constructor (ctx: ManageApiType) { + constructor (ctx: IManageApiType) { this.#ctx = ctx this.#db = new JSONStore(this.#ctx.configPath) - let initParams: IStringKeyMap = { + const initParams: IStringKeyMap = { picBed: {}, settings: {} } - for (let key in initParams) { + for (const key in initParams) { if (!this.#db.has(key)) { try { this.#db.set(key, initParams[key]) @@ -49,13 +49,13 @@ class ManageDB { return this.#db.unset(key, value) } - saveConfig (config: Partial): void { + saveConfig (config: Partial): void { Object.keys(config).forEach((name: string) => { this.set(name, config[name]) }) } - removeConfig (config: ManageConfigType): void { + removeConfig (config: IManageConfigType): void { Object.keys(config).forEach((name: string) => { this.unset(name, config[name]) }) diff --git a/src/main/manage/datastore/dbChecker.ts b/src/main/manage/datastore/dbChecker.ts index 3c81376..789b8db 100644 --- a/src/main/manage/datastore/dbChecker.ts +++ b/src/main/manage/datastore/dbChecker.ts @@ -1,10 +1,12 @@ -import fs from 'fs-extra' -import writeFile from 'write-file-atomic' -import path from 'path' -import { app } from 'electron' -import { getLogger } from '@core/utils/localLogger' import dayjs from 'dayjs' -import { T } from '~/main/i18n' +import { app } from 'electron' +import fs from 'fs-extra' +import path from 'path' +import writeFile from 'write-file-atomic' + +import { getLogger } from '@core/utils/localLogger' + +import { T } from '~/i18n' const STORE_PATH = app.getPath('userData') const manageConfigFilePath = path.join(STORE_PATH, 'manage.json') diff --git a/src/main/manage/datastore/upDownTaskQueue.ts b/src/main/manage/datastore/upDownTaskQueue.ts index fe2b8af..d00e552 100644 --- a/src/main/manage/datastore/upDownTaskQueue.ts +++ b/src/main/manage/datastore/upDownTaskQueue.ts @@ -1,59 +1,12 @@ // a singleton class to manage the up/down task queue // qiniu tcyun aliyun smms imgur github upyun -import path from 'path' import { app } from 'electron' import fs from 'fs-extra' -export enum commonTaskStatus { - queuing = 'queuing', - failed = 'failed', - canceled = 'canceled', - paused = 'paused' -} +import path from 'path' -export enum uploadTaskSpecialStatus { - uploading = 'uploading', - uploaded = 'uploaded' -} - -export enum downloadTaskSpecialStatus { - downloading = 'downloading', - downloaded = 'downloaded', -} - -export type uploadTaskStatus = commonTaskStatus | uploadTaskSpecialStatus -type downloadTaskStatus = commonTaskStatus | downloadTaskSpecialStatus - -export interface IUploadTask { - id: string - progress: number - status: uploadTaskStatus - sourceFilePath: string - sourceFileName: string - targetFilePath: string - targetFileBucket?: string - response?: any - cancelToken?: string - timeConsuming?: number - alias?: string - [other: string]: any -} - -export interface IDownloadTask { - id: string - progress: number - status: downloadTaskStatus - sourceFileUrl?: string - sourceFileName?: string - sourceConfig?: IStringKeyMap - targetFilePath?: string - response?: any - cancelToken?: string - timeConsuming?: number - reseumConfig?: IStringKeyMap - alias?: string - [other: string]: any -} +import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum' +import { IDownloadTask, IUploadTask } from '#/types/manage' class UpDownTaskQueue { /* eslint-disable */ diff --git a/src/main/manage/events/constants.ts b/src/main/manage/events/constants.ts index d1c05f5..7abaf8d 100644 --- a/src/main/manage/events/constants.ts +++ b/src/main/manage/events/constants.ts @@ -1,3 +1,3 @@ export const PICLIST_MANAGE_GET_CONFIG = 'PICLIST_MANAGE_GET_CONFIG' -export const PICLIST_MANAGE_SAVE_CONFIG = 'PICLIST_MANAGE_SAVE_CONFIG' export const PICLIST_MANAGE_REMOVE_CONFIG = 'PICLIST_MANAGE_REMOVE_CONFIG' +export const PICLIST_MANAGE_SAVE_CONFIG = 'PICLIST_MANAGE_SAVE_CONFIG' diff --git a/src/main/manage/events/ipcList.ts b/src/main/manage/events/ipcList.ts index 58bf8c7..dfadd01 100644 --- a/src/main/manage/events/ipcList.ts +++ b/src/main/manage/events/ipcList.ts @@ -1,11 +1,13 @@ -import manageCoreIPC from './manageCoreIPC' -import { ManageApi } from '../manageApi' import { ipcMain, IpcMainInvokeEvent, dialog, app, shell } from 'electron' -import UpDownTaskQueue from '../datastore/upDownTaskQueue' -import { downloadFileFromUrl } from '../utils/common' -import path from 'path' import fs from 'fs-extra' -import { selectDownloadFolder } from '@/manage/utils/static' +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 () { diff --git a/src/main/manage/events/manageCoreIPC.ts b/src/main/manage/events/manageCoreIPC.ts index 696ef0f..04b48f5 100644 --- a/src/main/manage/events/manageCoreIPC.ts +++ b/src/main/manage/events/manageCoreIPC.ts @@ -1,27 +1,28 @@ import { IpcMainEvent, + IpcMainInvokeEvent, ipcMain } from 'electron' -import getManageApi from '../Main' -import { PICLIST_MANAGE_GET_CONFIG, PICLIST_MANAGE_SAVE_CONFIG, PICLIST_MANAGE_REMOVE_CONFIG } from './constants' + +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.on(PICLIST_MANAGE_GET_CONFIG, (event: IpcMainEvent, key: string | undefined, callbackId: string) => { - const result = manageApi.getConfig(key) - event.sender.send(PICLIST_MANAGE_GET_CONFIG, result, callbackId) + ipcMain.handle(PICLIST_MANAGE_GET_CONFIG, (_: IpcMainInvokeEvent, key: string | undefined) => { + return manageApi.getConfig(key) }) } const handleManageSaveConfig = () => { - ipcMain.on(PICLIST_MANAGE_SAVE_CONFIG, (_event: IpcMainEvent, data: any) => { + ipcMain.on(PICLIST_MANAGE_SAVE_CONFIG, (_: IpcMainEvent, data: any) => { manageApi.saveConfig(data) }) } const handleManageRemoveConfig = () => { - ipcMain.on(PICLIST_MANAGE_REMOVE_CONFIG, (_event: IpcMainEvent, key: string, propName: string) => { + ipcMain.on(PICLIST_MANAGE_REMOVE_CONFIG, (_: IpcMainEvent, key: string, propName: string) => { manageApi.removeConfig(key, propName) }) } diff --git a/src/main/manage/manageApi.ts b/src/main/manage/manageApi.ts index 7380029..e614b9e 100644 --- a/src/main/manage/manageApi.ts +++ b/src/main/manage/manageApi.ts @@ -1,33 +1,36 @@ -import fs from 'fs-extra' -import path from 'path' +import { ipcMain } from 'electron' import { EventEmitter } from 'events' -import { managePathChecker } from './datastore/dbChecker' -import { - ManageApiType, - ManageConfigType, - ManageError, - PicBedMangeConfig -} from '~/universal/types/manage' -import ManageDB from './datastore/db' -import { ManageLogger } from './utils/logger' +import fs from 'fs-extra' import { get, set, unset } from 'lodash' import { homedir } from 'os' -import { isInputConfigValid, formatError } from './utils/common' -import API from './apis/api' -import windowManager from 'apis/app/window/windowManager' -import { IWindowList } from '#/types/enum' -import { ipcMain } from 'electron' -import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import path from 'path' -export class ManageApi extends EventEmitter implements ManageApiType { - private _config!: Partial +import windowManager from 'apis/app/window/windowManager' + +import API from '~/manage/apis/api' +import ManageDB from '~/manage/datastore/db' +import { managePathChecker } from '~/manage/datastore/dbChecker' +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 { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static' + +export class ManageApi extends EventEmitter implements IManageApiType { + private _config!: Partial private db!: ManageDB currentPicBed: string configPath: string baseDir!: string logger: ManageLogger - currentPicBedConfig: PicBedMangeConfig + currentPicBedConfig: IPicBedMangeConfig constructor (currentPicBed: string = '') { super() @@ -81,8 +84,8 @@ export class ManageApi extends EventEmitter implements ManageApiType { } } - private getPicBedConfig (picBedName: string): PicBedMangeConfig { - return this.getConfig(`picBed.${picBedName}`) + private getPicBedConfig (picBedName: string): IPicBedMangeConfig { + return this.getConfig(`picBed.${picBedName}`) } private initConfigPath (): void { @@ -102,7 +105,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { private initconfig (): void { this.db = new ManageDB(this) - this._config = this.db.read(true) as ManageConfigType + this._config = this.db.read(true) as IManageConfigType } getConfig (name?: string): T { @@ -190,14 +193,14 @@ export class ManageApi extends EventEmitter implements ManageApiType { async getBucketInfo ( param?: IStringKeyMap | undefined - ): Promise { + ): Promise { console.log(param) return {} } async getBucketDomain ( param: IStringKeyMap - ): Promise { + ): Promise { let client switch (this.currentPicBedConfig.picBedName) { case 'tcyun': @@ -230,6 +233,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { case 'tcyun': case 'aliyun': case 'qiniu': + case 's3plist': try { client = this.createClient() as any return await client.createBucket(param!) @@ -251,7 +255,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { async getOperatorList ( param?: IStringKeyMap - ): Promise { + ): Promise { console.log(param) return [] } @@ -272,7 +276,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { async getBucketAclPolicy ( param?: IStringKeyMap - ): Promise { + ): Promise { console.log(param) return {} } @@ -297,7 +301,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { async getBucketListRecursively ( param?: IStringKeyMap - ): Promise { + ): Promise { let client let window const defaultResult = { @@ -342,7 +346,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { */ async getBucketListBackstage ( param?: IStringKeyMap - ): Promise { + ): Promise { let client let window const defaultResult = { @@ -391,7 +395,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { **/ async getBucketFileList ( param?: IStringKeyMap - ): Promise { + ): Promise { const defaultResponse = { fullList: [], isTruncated: false, diff --git a/src/main/manage/utils/common.ts b/src/main/manage/utils/common.ts index c11031e..16182fb 100644 --- a/src/main/manage/utils/common.ts +++ b/src/main/manage/utils/common.ts @@ -1,24 +1,22 @@ -import fs from 'fs-extra' -import path from 'path' -import mime from 'mime-types' import axios from 'axios' -import { app } from 'electron' import crypto from 'crypto' +import { app } from 'electron' +import fs from 'fs-extra' import got, { OptionsOfTextResponseBody, RequestError } from 'got' -import { Stream } from 'stream' -import { promisify } from 'util' -import UpDownTaskQueue, -{ - uploadTaskSpecialStatus, - commonTaskStatus, - downloadTaskSpecialStatus -} from '../datastore/upDownTaskQueue' -import { ManageLogger } from '../utils/logger' -import { formatHttpProxy, IHTTPProxy } from '@/manage/utils/common' import { HttpsProxyAgent, HttpProxyAgent } from 'hpagent' import http from 'http' import https from 'https' +import mime from 'mime-types' import Downloader from 'nodejs-file-downloader' +import path from 'path' +import { Stream } from 'stream' +import { promisify } from 'util' + +import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue' +import { ManageLogger } from '~/manage/utils/logger' + +import { commonTaskStatus, downloadTaskSpecialStatus, uploadTaskSpecialStatus } from '#/types/enum' +import { formatHttpProxy } from '#/utils/common' export const getFSFile = async ( filePath: string, @@ -216,8 +214,6 @@ export const formatError = (err: any, params:IStringKeyMap) => { return `${String(err)}${JSON.stringify(params)}` } -export const trimPath = (path: string) => path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/') - const commonOptions = { keepAlive: true, keepAliveMsecs: 1000, @@ -307,13 +303,6 @@ export function getOptions ( } } -export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string => - !/^https?:\/\//.test(endpoint) - ? `${sslEnabled ? 'https' : 'http'}://${endpoint}` - : sslEnabled - ? endpoint.replace('http://', 'https://') - : endpoint.replace('https://', 'http://') - export class ConcurrencyPromisePool { limit: number queue: any[] diff --git a/src/main/manage/utils/dogeAPI.ts b/src/main/manage/utils/dogeAPI.ts index 464b99b..f10dcce 100644 --- a/src/main/manage/utils/dogeAPI.ts +++ b/src/main/manage/utils/dogeAPI.ts @@ -1,6 +1,7 @@ import axios from 'axios' import crypto from 'crypto' import querystring from 'querystring' + import picgo from '@core/picgo' export interface DogecloudToken { diff --git a/src/main/manage/utils/logger.ts b/src/main/manage/utils/logger.ts index abab5d6..98bc223 100644 --- a/src/main/manage/utils/logger.ts +++ b/src/main/manage/utils/logger.ts @@ -2,12 +2,13 @@ import chalk from 'chalk' import dayjs from 'dayjs' import fs from 'fs-extra' import path from 'path' -import util from 'util' -import { ILogType } from '#/types/enum' import { ILogColor, ILogger } from 'piclist/dist/types' -import { ManageApiType, Undefinable } from '~/universal/types/manage' +import util from 'util' + +import { ILogType } from '#/types/enum' +import { IManageApiType, Undefinable } from '#/types/manage' import { enforceNumber, isDev } from '#/utils/common' -import { configPaths } from '~/universal/utils/configPaths' +import { configPaths } from '#/utils/configPaths' export class ManageLogger implements ILogger { readonly #level = { @@ -17,11 +18,11 @@ export class ManageLogger implements ILogger { [ILogType.error]: 'red' } - readonly #ctx: ManageApiType + readonly #ctx: IManageApiType #logLevel!: string #logPath!: string - constructor (ctx: ManageApiType) { + constructor (ctx: IManageApiType) { this.#ctx = ctx } diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 5f565cc..5b89e08 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -1,17 +1,20 @@ +import axios from 'axios' +import { app } from 'electron' +import fs from 'fs-extra' import http from 'http' -import routers from './routerManager' +import multer from 'multer' +import path from 'path' + +import picgo from '@core/picgo' +import logger from '@core/picgo/logger' + +import routers from '~/server/routerManager' import { handleResponse, ensureHTTPLink -} from './utils' -import picgo from '@core/picgo' -import logger from '@core/picgo/logger' -import axios from 'axios' -import multer from 'multer' -import { app } from 'electron' -import path from 'path' -import fs from 'fs-extra' -import { configPaths } from '~/universal/utils/configPaths' +} from '~/server/utils' + +import { configPaths } from '#/utils/configPaths' const DEFAULT_PORT = 36677 const DEFAULT_HOST = '0.0.0.0' diff --git a/src/main/server/routerManager.ts b/src/main/server/routerManager.ts index 29881a2..34b7495 100644 --- a/src/main/server/routerManager.ts +++ b/src/main/server/routerManager.ts @@ -1,21 +1,27 @@ -import router from './router' -import { - handleResponse -} from './utils' -import logger from '@core/picgo/logger' -import windowManager from 'apis/app/window/windowManager' -import { uploadChoosedFiles, uploadClipboardFiles, deleteChoosedFiles } from 'apis/app/uploader/apis' -import path from 'path' -import { dbPathDir } from 'apis/core/datastore/dbChecker' -import picgo from '@core/picgo' -import { changeCurrentUploader } from '../utils/handleUploaderConfig' import { app } from 'electron' import fs from 'fs-extra' -import { AESHelper } from '../utils/aesHelper' -import { marked } from 'marked' -import { markdownContent } from './apiDoc' import http from 'http' -import { configPaths } from '~/universal/utils/configPaths' +import { marked } from 'marked' +import path from 'path' + +import { dbPathDir } from '@core/datastore/dbChecker' +import picgo from '@core/picgo' +import logger from '@core/picgo/logger' + +import { AESHelper } from '~/utils/aesHelper' +import { changeCurrentUploader } from '~/utils/handleUploaderConfig' + +import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis' +import windowManager from 'apis/app/window/windowManager' + +import { markdownContent } from '~/server/apiDoc' +import router from '~/server/router' +import { + deleteChoosedFiles, + handleResponse +} from '~/server/utils' + +import { configPaths } from '#/utils/configPaths' const appPath = app.getPath('userData') const serverTempDir = path.join(appPath, 'serverTemp') diff --git a/src/main/server/utils.ts b/src/main/server/utils.ts index ba6c4e7..b1f6de8 100644 --- a/src/main/server/utils.ts +++ b/src/main/server/utils.ts @@ -1,4 +1,20 @@ +import { + Notification +} from 'electron' + +import picgo from '@core/picgo' import logger from '@core/picgo/logger' +import db, { GalleryDB } from '@core/datastore' + +import windowManager from 'apis/app/window/windowManager' + +import GuiApi from '~/apis/gui' +import { T } from '~/i18n/index' +import { configPaths } from '#/utils/configPaths' +import { picBedsCanbeDeleted } from '#/utils/static' +import { ICOREBuildInEvent, IWindowList } from '#/types/enum' + +import ALLApi from '@/apis/allApi' export const handleResponse = ({ response, @@ -31,3 +47,43 @@ export const ensureHTTPLink = (url: string): string => { ? url : `http://${url}` } + +export const deleteChoosedFiles = async (list: ImgInfo[]): Promise => { + const result = [] + for (const item of list) { + if (item.id) { + try { + const dbStore = GalleryDB.getInstance() + const file = await dbStore.getById(item.id) + await dbStore.removeById(item.id) + if (await db.get(configPaths.settings.deleteCloudFile)) { + if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) { + 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' + ) + }) + notification.show() + } + setTimeout(() => { + ALLApi.delete(item).then(noteFunc) + }, 0) + } + } + setTimeout(() => { + picgo.emit(ICOREBuildInEvent.REMOVE, [file], GuiApi.getInstance()) + }, 500) + result.push(true) + } catch (e) { + result.push(false) + } + } + } + if (windowManager.has(IWindowList.SETTING_WINDOW)) { + windowManager.get(IWindowList.SETTING_WINDOW)!.webContents?.send('updateGallery') + } + return result +} diff --git a/src/main/server/webServer/index.ts b/src/main/server/webServer/index.ts index 7c5e27c..dd43984 100644 --- a/src/main/server/webServer/index.ts +++ b/src/main/server/webServer/index.ts @@ -1,10 +1,12 @@ -import http from 'http' import fs from 'fs-extra' +import http from 'http' import path from 'path' + import picgo from '@core/picgo' -import logger from '../../apis/core/picgo/logger' -import { encodeFilePath } from '~/universal/utils/common' -import { configPaths } from '~/universal/utils/configPaths' +import logger from '@core/picgo/logger' + +import { encodeFilePath } from '#/utils/common' +import { configPaths } from '#/utils/configPaths' const defaultPath = process.platform === 'win32' ? 'C:\\Users' : '/' diff --git a/src/main/utils/aesHelper.ts b/src/main/utils/aesHelper.ts index a974f80..79ba22b 100644 --- a/src/main/utils/aesHelper.ts +++ b/src/main/utils/aesHelper.ts @@ -1,7 +1,9 @@ import crypto from 'crypto' + import picgo from '@core/picgo' -import { DEFAULT_AES_PASSWORD } from '~/universal/utils/static' -import { configPaths } from '~/universal/utils/configPaths' + +import { configPaths } from '#/utils/configPaths' +import { DEFAULT_AES_PASSWORD } from '#/utils/static' export class AESHelper { key: Buffer diff --git a/src/main/utils/beforeOpen.ts b/src/main/utils/beforeOpen.ts index 2bdfada..da4addf 100644 --- a/src/main/utils/beforeOpen.ts +++ b/src/main/utils/beforeOpen.ts @@ -1,9 +1,11 @@ import fs from 'fs-extra' +import yaml from 'js-yaml' import path from 'path' import os from 'os' -import { dbPathChecker } from 'apis/core/datastore/dbChecker' -import yaml from 'js-yaml' -import { i18nManager } from '~/main/i18n' + +import { dbPathChecker } from '@core/datastore/dbChecker' + +import { i18nManager } from '~/i18n' const configPath = dbPathChecker() const CONFIG_DIR = path.dirname(configPath) diff --git a/src/main/utils/clipboardPoll.ts b/src/main/utils/clipboardPoll.ts index aa38e99..999dd52 100644 --- a/src/main/utils/clipboardPoll.ts +++ b/src/main/utils/clipboardPoll.ts @@ -1,7 +1,8 @@ +import crypto from 'crypto' import { clipboard } from 'electron' import { EventEmitter } from 'events' -import crypto from 'crypto' -import logger from '../apis/core/picgo/logger' + +import logger from '@core/picgo/logger' class ClipboardWatcher extends EventEmitter { timer: NodeJS.Timeout | null diff --git a/src/main/utils/common.ts b/src/main/utils/common.ts index 8f200e4..f115d2c 100644 --- a/src/main/utils/common.ts +++ b/src/main/utils/common.ts @@ -1,12 +1,26 @@ -import fs from 'fs-extra' -import db from '~/main/apis/core/datastore' -import { clipboard, Notification, dialog } from 'electron' -import { handleUrlEncode } from '~/universal/utils/common' import axios from 'axios' +import { clipboard, Notification, dialog, Tray } from 'electron' import FormData from 'form-data' -import logger from '../apis/core/picgo/logger' -import { configPaths } from '~/universal/utils/configPaths' -import { IShortUrlServer } from '~/universal/types/enum' +import fs from 'fs-extra' + +import db from '@core/datastore' +import logger from '@core/picgo/logger' + +import { IShortUrlServer } from '#/types/enum' +import { handleUrlEncode } from '#/utils/common' +import { configPaths } from '#/utils/configPaths' + +export let tray: Tray + +export const setTray = (t: Tray) => { tray = t } + +export const getTray = () => tray + +export function setTrayToolTip (title: string): void { + if (tray) { + tray.setToolTip(title) + } +} export const handleCopyUrl = (str: string): void => { if (db.get(configPaths.settings.autoCopy) !== false) { diff --git a/src/main/utils/deleteFunc.ts b/src/main/utils/deleteFunc.ts index 648a205..078abe4 100644 --- a/src/main/utils/deleteFunc.ts +++ b/src/main/utils/deleteFunc.ts @@ -1,11 +1,15 @@ -import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3' -import { NodeHttpHandler } from '@smithy/node-http-handler' -import http, { AgentOptions } from 'http' -import https from 'https' -import { getAgent } from '../manage/utils/common' import axios from 'axios' import crypto from 'crypto' +import http, { AgentOptions } from 'http' +import https from 'https' +import path from 'path' +import { ISftpPlistConfig } from 'piclist' import querystring from 'querystring' +import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3' +import { NodeHttpHandler } from '@smithy/node-http-handler' + +import SSHClient from '~/utils/sshClient' +import { getAgent } from '~/manage/utils/common' interface DogecloudTokenFull { Credentials: { @@ -218,3 +222,18 @@ export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) { return false } } + +export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileName: string) { + try { + const client = SSHClient.instance + await client.connect(config) + const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/') + const remote = path.join(uploadPath, fileName) + const deleteResult = await client.deleteFileSFTP(config, remote) + client.close() + return deleteResult + } catch (err: any) { + console.log(err) + return false + } +} diff --git a/src/main/utils/getMacOSVersion.ts b/src/main/utils/getMacOSVersion.ts index ef5776b..bb31634 100644 --- a/src/main/utils/getMacOSVersion.ts +++ b/src/main/utils/getMacOSVersion.ts @@ -1,7 +1,8 @@ // fork from https://github.com/sindresorhus/macos-version // cause I can't change it to common-js module -import process from 'process' + import fs from 'fs' +import process from 'process' import semver from 'semver' export const isMacOS = process.platform === 'darwin' diff --git a/src/main/utils/getPicBeds.ts b/src/main/utils/getPicBeds.ts index efa70aa..2431664 100644 --- a/src/main/utils/getPicBeds.ts +++ b/src/main/utils/getPicBeds.ts @@ -1,5 +1,5 @@ import picgo from '@core/picgo' -import { configPaths } from '~/universal/utils/configPaths' +import { configPaths } from '#/utils/configPaths' const getPicBeds = () => { const picBedTypes = picgo.helper.uploader.getIdList() diff --git a/src/main/utils/handleArgv.ts b/src/main/utils/handleArgv.ts index 3e80c1f..a2cacd9 100644 --- a/src/main/utils/handleArgv.ts +++ b/src/main/utils/handleArgv.ts @@ -1,7 +1,9 @@ -import path from 'path' import fs from 'fs-extra' +import path from 'path' import { Logger } from 'piclist' -import { isUrl } from '~/universal/utils/common' + +import { isUrl } from '#/utils/common' + interface IResultFileObject { path: string } diff --git a/src/main/utils/handleI18n.ts b/src/main/utils/handleI18n.ts index dfdc328..711d40f 100644 --- a/src/main/utils/handleI18n.ts +++ b/src/main/utils/handleI18n.ts @@ -1,7 +1,9 @@ -import db from '~/main/apis/core/datastore' -import { i18nManager } from '~/main/i18n' -import { II18nLanguage } from '~/universal/types/enum' -import { configPaths } from '~/universal/utils/configPaths' +import db from '@core/datastore' + +import { i18nManager } from '~/i18n' + +import { II18nLanguage } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' export const initI18n = () => { const currentLanguage = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN diff --git a/src/main/utils/handleUploaderConfig.ts b/src/main/utils/handleUploaderConfig.ts index eb24f7e..506fa20 100644 --- a/src/main/utils/handleUploaderConfig.ts +++ b/src/main/utils/handleUploaderConfig.ts @@ -1,7 +1,11 @@ import { v4 as uuid } from 'uuid' -import { trimValues } from '#/utils/common' + import picgo from '@core/picgo' -import { configPaths } from '~/universal/utils/configPaths' + +import { setTrayToolTip } from '~/utils/common' + +import { trimValues } from '#/utils/common' +import { configPaths } from '#/utils/configPaths' export const handleConfigWithFunction = (config: IPicGoPluginOriginConfig[]): IPicGoPluginConfig[] => { for (const i in config) { @@ -65,6 +69,7 @@ export const changeCurrentUploader = (type: string, config?: IStringKeyMap, id?: [configPaths.picBed.current]: type, [configPaths.picBed.uploader]: type }) + setTrayToolTip(`${type} ${config?._configName || ''}`) } export const selectUploaderConfig = (type: string, id: string) => { diff --git a/src/main/utils/pasteTemplate.ts b/src/main/utils/pasteTemplate.ts index 4f5391f..605706d 100644 --- a/src/main/utils/pasteTemplate.ts +++ b/src/main/utils/pasteTemplate.ts @@ -1,8 +1,9 @@ +import db from '@core/datastore' + +import { generateShortUrl, handleUrlEncodeWithSetting } from '~/utils/common' + import { IPasteStyle } from '#/types/enum' -import { generateShortUrl } from '~/main/utils/common' -import db from '~/main/apis/core/datastore' -import { handleUrlEncodeWithSetting } from './common' -import { configPaths } from '~/universal/utils/configPaths' +import { configPaths } from '#/utils/configPaths' export const formatCustomLink = (customLink: string, item: ImgInfo) => { const fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '') diff --git a/src/main/utils/sshClient.ts b/src/main/utils/sshClient.ts index 13363bd..e676603 100644 --- a/src/main/utils/sshClient.ts +++ b/src/main/utils/sshClient.ts @@ -1,9 +1,9 @@ // @ts-nocheck +import fs from 'fs-extra' import { NodeSSH, Config, SSHExecCommandResponse } from 'node-ssh-no-cpu-features' import path from 'path' import { ISftpPlistConfig } from 'piclist/dist/types' import { Client } from 'ssh2-no-cpu-features' -import fs from 'fs-extra' class SSHClient { // eslint-disable-next-line no-use-before-define diff --git a/src/main/utils/syncSettings.ts b/src/main/utils/syncSettings.ts index dd74a2b..3edd0bd 100644 --- a/src/main/utils/syncSettings.ts +++ b/src/main/utils/syncSettings.ts @@ -1,12 +1,14 @@ +import axios from 'axios' import { app } from 'electron' import fs from 'fs-extra' -import path from 'path' -import axios from 'axios' -import db from '~/main/apis/core/datastore' import { HttpsProxyAgent } from 'hpagent' +import path from 'path' import { Octokit } from '@octokit/rest' -import logger from 'apis/core/picgo/logger' -import { configPaths } from '~/universal/utils/configPaths' + +import db from '@core/datastore' +import logger from '@core/picgo/logger' + +import { configPaths } from '#/utils/configPaths' const STORE_PATH = app.getPath('userData') diff --git a/src/main/utils/updateChecker.ts b/src/main/utils/updateChecker.ts index 341d2b0..f383b4a 100644 --- a/src/main/utils/updateChecker.ts +++ b/src/main/utils/updateChecker.ts @@ -1,6 +1,8 @@ -import db from '~/main/apis/core/datastore' import { autoUpdater } from 'electron-updater' -import { configPaths } from '~/universal/utils/configPaths' + +import db from '@core/datastore' + +import { configPaths } from '#/utils/configPaths' const updateChecker = async () => { let showTip = db.get(configPaths.settings.showUpdateTip) diff --git a/src/main/utils/windowHelper.ts b/src/main/utils/windowHelper.ts new file mode 100644 index 0000000..37175c4 --- /dev/null +++ b/src/main/utils/windowHelper.ts @@ -0,0 +1,56 @@ +import { screen } from 'electron' + +import db from '@core/datastore' + +import windowManager from 'apis/app/window/windowManager' + +import { IWindowList } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' + +export function openMiniWindow (hideSettingWindow:boolean = true) { + const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)! + miniWindow.removeAllListeners() + if (db.get(configPaths.settings.miniWindowOntop)) { + miniWindow.setAlwaysOnTop(true) + } + const { width, height } = screen.getPrimaryDisplay().workAreaSize + const lastPosition = db.get(configPaths.settings.miniWindowPosition) + if (lastPosition) { + miniWindow.setPosition(lastPosition[0], lastPosition[1]) + } else { + miniWindow.setPosition(width - 100, height - 100) + } + const setPositionFunc = () => { + const position = miniWindow.getPosition() + db.set(configPaths.settings.miniWindowPosition, position) + } + miniWindow.on('close', setPositionFunc) + miniWindow.on('move', setPositionFunc) + miniWindow.show() + miniWindow.focus() + if (hideSettingWindow) { + const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)! + settingWindow.hide() + } else { + const autoCloseMainWindow = db.get(configPaths.settings.autoCloseMainWindow) || false + if (windowManager.has(IWindowList.SETTING_WINDOW) && autoCloseMainWindow) { + windowManager.get(IWindowList.SETTING_WINDOW)!.hide() + } + } +} + +export const openMainWindow = () => { + const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW) + const autoCloseMiniWindow = db.get(configPaths.settings.autoCloseMiniWindow) || false + settingWindow!.show() + settingWindow!.focus() + if (windowManager.has(IWindowList.MINI_WINDOW) && autoCloseMiniWindow) { + windowManager.get(IWindowList.MINI_WINDOW)!.hide() + } +} + +export const hideMiniWindow = () => { + if (windowManager.has(IWindowList.MINI_WINDOW)) { + windowManager.get(IWindowList.MINI_WINDOW)!.hide() + } +} diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 6ea7ee1..7cb4db9 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -1,29 +1,24 @@ diff --git a/src/renderer/components/ImageWebdav.vue b/src/renderer/components/ImageWebdav.vue index b3f95fb..477f98e 100644 --- a/src/renderer/components/ImageWebdav.vue +++ b/src/renderer/components/ImageWebdav.vue @@ -21,34 +21,26 @@ - - diff --git a/src/renderer/hooks/useATagClick.ts b/src/renderer/hooks/useATagClick.ts index 227ac2f..8b6f42b 100644 --- a/src/renderer/hooks/useATagClick.ts +++ b/src/renderer/hooks/useATagClick.ts @@ -1,12 +1,14 @@ -import { openURL } from '@/utils/common' import { onMounted, onUnmounted } from 'vue' +import { sendRPC } from '@/utils/common' +import { IRPCActionType } from 'root/src/universal/types/enum' + export function useATagClick () { const handleATagClick = (e: MouseEvent) => { if (e.target instanceof HTMLAnchorElement) { if (e.target.href) { e.preventDefault() - openURL(e.target.href) + sendRPC(IRPCActionType.OPEN_URL, e.target.href) } } } diff --git a/src/renderer/hooks/useIPC.ts b/src/renderer/hooks/useIPC.ts index 7a2734a..1426a0f 100644 --- a/src/renderer/hooks/useIPC.ts +++ b/src/renderer/hooks/useIPC.ts @@ -1,6 +1,7 @@ import { ipcRenderer } from 'electron' import { onUnmounted } from 'vue' -import { IRPCActionType } from '~/universal/types/enum' + +import { IRPCActionType } from '#/types/enum' export const useIPCOn = (channel: string, listener: IpcRendererListener) => { ipcRenderer.on(channel, listener) diff --git a/src/renderer/i18n/index.ts b/src/renderer/i18n/index.ts index 08bf77a..5f5973c 100644 --- a/src/renderer/i18n/index.ts +++ b/src/renderer/i18n/index.ts @@ -1,26 +1,33 @@ import { ipcRenderer } from 'electron' import { ObjectAdapter, I18n } from '@picgo/i18n' -import { GET_CURRENT_LANGUAGE, SET_CURRENT_LANGUAGE, FORCE_UPDATE, GET_LANGUAGE_LIST } from '#/events/constants' -import bus from '@/utils/bus' + +import { sendRPC, sendRpcSync } from '@/utils/common' + +import { SET_CURRENT_LANGUAGE } from '#/events/constants' import { builtinI18nList } from '#/i18n' +import { IRPCActionType } from '#/types/enum' +import { updatePageReloadCount } from '@/utils/global' export class I18nManager { #i18n: I18n | null = null #i18nFileList: II18nItem[] = builtinI18nList - #getLanguageList () { - ipcRenderer.send(GET_LANGUAGE_LIST) - ipcRenderer.once(GET_LANGUAGE_LIST, (event, list: II18nItem[]) => { - this.#i18nFileList = list + constructor () { + this.#getCurrentLanguage() + this.#getLanguageList() + ipcRenderer.on(SET_CURRENT_LANGUAGE, (_, lang: string, locales: ILocales) => { + this.#setLocales(lang, locales) + updatePageReloadCount() }) } + #getLanguageList () { + this.#i18nFileList = sendRpcSync(IRPCActionType.GET_LANGUAGE_LIST) + } + #getCurrentLanguage () { - ipcRenderer.send(GET_CURRENT_LANGUAGE) - ipcRenderer.once(GET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => { - this.#setLocales(lang, locales) - bus.emit(FORCE_UPDATE) - }) + const [lang, locales] = sendRpcSync(IRPCActionType.GET_CURRENT_LANGUAGE) + this.#setLocales(lang, locales) } #setLocales (lang: string, locales: ILocales) { @@ -33,21 +40,12 @@ export class I18nManager { }) } - constructor () { - this.#getCurrentLanguage() - this.#getLanguageList() - ipcRenderer.on(SET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => { - this.#setLocales(lang, locales) - bus.emit(FORCE_UPDATE) - }) - } - T (key: ILocalesKey, args: IStringKeyMap = {}): string { return this.#i18n?.translate(key, args) || key } setCurrentLanguage (lang: string) { - ipcRenderer.send(SET_CURRENT_LANGUAGE, lang) + sendRPC(IRPCActionType.SET_CURRENT_LANGUAGE, lang) } get languageList () { diff --git a/src/renderer/layouts/Main.vue b/src/renderer/layouts/Main.vue index 075be33..ba72bf8 100644 --- a/src/renderer/layouts/Main.vue +++ b/src/renderer/layouts/Main.vue @@ -7,7 +7,7 @@ PicList - {{ version }}
@@ -101,7 +100,7 @@ {{ $T('PICBEDS_SETTINGS') }} + diff --git a/src/renderer/manage/pages/bucketPage.vue b/src/renderer/manage/pages/BucketPage.vue similarity index 96% rename from src/renderer/manage/pages/bucketPage.vue rename to src/renderer/manage/pages/BucketPage.vue index 6369aac..754defd 100644 --- a/src/renderer/manage/pages/bucketPage.vue +++ b/src/renderer/manage/pages/BucketPage.vue @@ -50,7 +50,10 @@ style="display: flex;" @click="showUploadDialog" > - +
- + - +
- + @@ -207,7 +221,10 @@
- + @@ -230,7 +247,8 @@
@@ -634,46 +641,11 @@ https://www.baidu.com/img/bd_logo1.png" + + diff --git a/src/renderer/manage/pages/manageSetting.vue b/src/renderer/manage/pages/manageSetting.vue deleted file mode 100644 index 30bca98..0000000 --- a/src/renderer/manage/pages/manageSetting.vue +++ /dev/null @@ -1,750 +0,0 @@ - - - - - diff --git a/src/renderer/manage/store/manageStore.ts b/src/renderer/manage/store/manageStore.ts index be8f0dc..dd69c8f 100644 --- a/src/renderer/manage/store/manageStore.ts +++ b/src/renderer/manage/store/manageStore.ts @@ -1,5 +1,6 @@ import { defineStore } from 'pinia' -import { getConfig } from '../utils/dataSender' + +import { getConfig } from '@/manage/utils/dataSender' export const useManageStore = defineStore('manageConfig', { state: () => { diff --git a/src/renderer/manage/utils/common.ts b/src/renderer/manage/utils/common.ts index 584460b..d413685 100644 --- a/src/renderer/manage/utils/common.ts +++ b/src/renderer/manage/utils/common.ts @@ -1,20 +1,11 @@ -// UUID +import crypto from 'crypto' +import path from 'path' import { v4 as uuidv4 } from 'uuid' -// 路径处理库 -import path from 'path' +import { availableIconList } from '@/manage/utils/icon' +import { getConfig } from '@/manage/utils/dataSender' -// 加密库 -import crypto from 'crypto' - -// 可用图标列表 -import { availableIconList } from './icon' - -// 数据发送工具函数 -import { getConfig } from './dataSender' - -// 工具函数 -import { handleUrlEncode, safeSliceF, isNeedToShorten } from '~/universal/utils/common' +import { handleUrlEncode, safeSliceF, isNeedToShorten } from '#/utils/common' export function randomStringGenerator (length: number): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' @@ -127,11 +118,6 @@ export function formatFileName (fileName: string, length: number = 20) { return isNeedToShorten(fileName, length) ? `${safeSliceF(name, length - 3 - ext.length)}...${ext}` : fileName } -export const getExtension = (fileName: string) => path.extname(fileName).slice(1) - -export const isImage = (fileName: string) => - ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg'].includes(getExtension(fileName)) - export function formObjToTableData (obj: any) { const exclude = [undefined, null, '', 'transformedConfig'] return Object.keys(obj).filter(key => !exclude.includes(obj[key])).map(key => ({ @@ -148,34 +134,6 @@ export function isValidUrl (str: string) { } } -export interface IHTTPProxy { - host: string - port: number - protocol: string -} - -export const formatHttpProxy = (proxy: string | undefined, type: 'object' | 'string'): IHTTPProxy | undefined | string => { - if (!proxy) return undefined - if (/^https?:\/\//.test(proxy)) { - const { protocol, hostname, port } = new URL(proxy) - return type === 'string' - ? `${protocol}//${hostname}:${port}` - : { - host: hostname, - port: Number(port), - protocol: protocol.slice(0, -1) - } - } - const [host, port] = proxy.split(':') - return type === 'string' - ? `http://${host}:${port}` - : { - host, - port: port ? Number(port) : 80, - protocol: 'http' - } -} - export const svg = ` -import { getConfig } from '@/utils/dataSender' import { onMounted, ref } from 'vue' -import { II18nLanguage } from '~/universal/types/enum' -import { configPaths } from '~/universal/utils/configPaths' + +import { getConfig } from '@/utils/dataSender' + +import { II18nLanguage } from '#/types/enum' +import { configPaths } from '#/utils/configPaths' const srcUrl = ref('https://piclist.cn/app.html') diff --git a/src/renderer/pages/Gallery.vue b/src/renderer/pages/Gallery.vue index 5564c48..a3a510b 100644 --- a/src/renderer/pages/Gallery.vue +++ b/src/renderer/pages/Gallery.vue @@ -24,7 +24,8 @@ @change="handleDeleteCloudFile" /> - {{ $T('MANAGE_BUCKET_SORT_TYPE') }} + {{ $T('MANAGE_BUCKET_SORT_EXT') }} {{ $T('MANAGE_BUCKET_SORT_TIME') }} - {{ $T('MANAGE_BUCKET_SORT_SELECTED') }} + {{ $T('MANAGE_BUCKET_SORT_CHECK') }} @@ -457,50 +458,30 @@
+ + +