mirror of
https://github.com/Kuingsmile/PicList.git
synced 2025-03-13 00:18:13 -04:00
Merge branch 'dev' into release
This commit is contained in:
commit
8acbd931a2
21
CHANGELOG.md
21
CHANGELOG.md
@ -1,3 +1,24 @@
|
||||
## :tada: 2.8.4 (2024-04-28)
|
||||
|
||||
|
||||
### :sparkles: Features
|
||||
|
||||
* **custom:** add feedback entry ([2087d9a](https://github.com/Kuingsmile/piclist/commit/2087d9a))
|
||||
* **custom:** auto refresh after change custom url ([939c907](https://github.com/Kuingsmile/piclist/commit/939c907)), closes [#191](https://github.com/Kuingsmile/piclist/issues/191)
|
||||
* **custom:** change timestamp to milliseconds ([25648ea](https://github.com/Kuingsmile/piclist/commit/25648ea)), closes [#194](https://github.com/Kuingsmile/piclist/issues/194)
|
||||
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
* **custom:** fix aws s3 urlprefix bug ([3681681](https://github.com/Kuingsmile/piclist/commit/3681681))
|
||||
|
||||
|
||||
### :pencil: Documentation
|
||||
|
||||
* **custom:** prepare for 2.8.4 ([bcb4760](https://github.com/Kuingsmile/piclist/commit/bcb4760))
|
||||
|
||||
|
||||
|
||||
## :tada: 2.8.3 (2024-04-11)
|
||||
|
||||
|
||||
|
@ -1,9 +1,15 @@
|
||||
✨ Features
|
||||
|
||||
- 现在不再对gif图片进行格式转换
|
||||
### ✨ Features
|
||||
|
||||
🐛 Bug Fixes
|
||||
- 管理功能
|
||||
- 现在修改自定义域名后会自动强制刷新当前页面
|
||||
- 现在第一次进入管理页面时默认获取云端最新文件列表
|
||||
- 现在内置s3图床默认允许自签证书
|
||||
- 现在高级重命名中的时间戳精确到毫秒
|
||||
|
||||
- 修复了重新保存配置后图片水印功能失效的问题
|
||||
- 修复了同时打开主界面和mini窗口时,重命名窗口定位错误的问题
|
||||
- 修复了特殊情况下软件界面异常退出并导致后台CPU占用过高的问题
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- 管理功能
|
||||
- 修复了强制https对本地图床没有生效的问题
|
||||
- 修复了不填写区域时,minio无法正常删除图片的问题
|
||||
- 修复了内置s3图床,配合minio使用时会额外添加桶名的问题
|
||||
|
@ -1,9 +1,14 @@
|
||||
✨ Features
|
||||
### ✨ Features
|
||||
|
||||
- Now no longer convert gif images
|
||||
- Manage
|
||||
- Now, after modifying the custom domain name, the current page will be automatically forced to refresh
|
||||
- Now, the cloud-side latest file list is obtained by default when entering the management page for the first time
|
||||
- Now the built-in s3 image bed defaults to allowing self-signed certificates
|
||||
- Now the timestamp in advanced renaming is accurate to milliseconds
|
||||
|
||||
🐛 Bug Fixes
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Fix the problem that the image watermark function is invalid after re-saving the configuration
|
||||
- Fix the problem that the rename window is positioned incorrectly when the main interface and mini window are opened at the same time
|
||||
- Fix the problem that the software interface exits abnormally under special circumstances and causes high CPU usage in the background
|
||||
- Manage
|
||||
- Fixed the problem that forcing https does not take effect on the local image bed
|
||||
- Fixed the problem that Minio cannot delete images normally when the region is not filled in
|
||||
- Fixed the problem that the built-in s3 image bed will add an additional bucket name when used with Minio
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "piclist",
|
||||
"version": "2.8.3",
|
||||
"version": "2.8.4",
|
||||
"author": {
|
||||
"name": "Kuingsmile",
|
||||
"email": "pkukuing@gmail.com"
|
||||
@ -68,7 +68,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-ssh-no-cpu-features": "^1.0.1",
|
||||
"nodejs-file-downloader": "^4.12.1",
|
||||
"piclist": "^1.8.5",
|
||||
"piclist": "^1.8.7",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"proxy-agent": "^5.0.0",
|
||||
|
@ -22,6 +22,7 @@ CONFIG_THING: Config ${c}
|
||||
FIND_NEW_VERSION: Find New Version
|
||||
NO_MORE_NOTICE: No More Notice
|
||||
SHOW_DEVTOOLS: Show Devtools
|
||||
FEEDBACK: Feedback
|
||||
CURRENT_PICBED: Current Picbed
|
||||
START_WATCH_CLIPBOARD: Start Watch Clipboard
|
||||
STOP_WATCH_CLIPBOARD: Stop Watch Clipboard
|
||||
|
@ -22,6 +22,7 @@ CONFIG_THING: 配置${c}
|
||||
FIND_NEW_VERSION: 发现新版本
|
||||
NO_MORE_NOTICE: 以后不再提醒
|
||||
SHOW_DEVTOOLS: 打开开发者工具
|
||||
FEEDBACK: 反馈问题
|
||||
CURRENT_PICBED: 当前图床
|
||||
START_WATCH_CLIPBOARD: 开始监听剪贴板
|
||||
STOP_WATCH_CLIPBOARD: 停止监听剪贴板
|
||||
|
@ -22,6 +22,7 @@ CONFIG_THING: 設定${c}
|
||||
FIND_NEW_VERSION: 發現新版本
|
||||
NO_MORE_NOTICE: 以後不再提醒
|
||||
SHOW_DEVTOOLS: 開啟開發者工具
|
||||
FEEDBACK: 問題反饋
|
||||
CURRENT_PICBED: 當前圖床
|
||||
START_WATCH_CLIPBOARD: 開始監聽剪貼簿
|
||||
STOP_WATCH_CLIPBOARD: 停止監聽剪貼簿
|
||||
|
@ -10,22 +10,22 @@ import { BrowserWindow } from 'electron'
|
||||
import { IWindowList } from '#/types/enum'
|
||||
|
||||
class WindowManager implements IWindowManager {
|
||||
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||
private windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||
#windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||
#windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||
create (name: IWindowList) {
|
||||
const windowConfig: IWindowListItem = windowList.get(name)!
|
||||
if (windowConfig.isValid) {
|
||||
if (!windowConfig.multiple) {
|
||||
if (this.has(name)) return this.windowMap.get(name)!
|
||||
if (this.has(name)) return this.#windowMap.get(name)!
|
||||
}
|
||||
const window = new BrowserWindow(windowConfig.options())
|
||||
const id = window.id
|
||||
if (windowConfig.multiple) {
|
||||
this.windowMap.set(`${name}_${window.id}`, window)
|
||||
this.windowIdMap.set(window.id, `${name}_${window.id}`)
|
||||
this.#windowMap.set(`${name}_${window.id}`, window)
|
||||
this.#windowIdMap.set(window.id, `${name}_${window.id}`)
|
||||
} else {
|
||||
this.windowMap.set(name, window)
|
||||
this.windowIdMap.set(window.id, name)
|
||||
this.#windowMap.set(name, window)
|
||||
this.#windowIdMap.set(window.id, name)
|
||||
}
|
||||
windowConfig.callback(window, this)
|
||||
window.on('close', () => {
|
||||
@ -39,7 +39,7 @@ class WindowManager implements IWindowManager {
|
||||
|
||||
get (name: IWindowList) {
|
||||
if (this.has(name)) {
|
||||
return this.windowMap.get(name)!
|
||||
return this.#windowMap.get(name)!
|
||||
} else {
|
||||
const window = this.create(name)
|
||||
return window
|
||||
@ -47,24 +47,24 @@ class WindowManager implements IWindowManager {
|
||||
}
|
||||
|
||||
has (name: IWindowList) {
|
||||
return this.windowMap.has(name)
|
||||
return this.#windowMap.has(name)
|
||||
}
|
||||
|
||||
deleteById = (id: number) => {
|
||||
const name = this.windowIdMap.get(id)
|
||||
const name = this.#windowIdMap.get(id)
|
||||
if (name) {
|
||||
this.windowMap.delete(name)
|
||||
this.windowIdMap.delete(id)
|
||||
this.#windowMap.delete(name)
|
||||
this.#windowIdMap.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableWindow (isSkipMiniWindow = false) {
|
||||
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW)
|
||||
const miniWindow = this.#windowMap.get(IWindowList.MINI_WINDOW)
|
||||
if (miniWindow && miniWindow.isVisible() && !isSkipMiniWindow) {
|
||||
return miniWindow
|
||||
} else {
|
||||
const settingWindow = this.windowMap.get(IWindowList.SETTING_WINDOW)
|
||||
const trayWindow = this.windowMap.get(IWindowList.TRAY_WINDOW)
|
||||
const settingWindow = this.#windowMap.get(IWindowList.SETTING_WINDOW)
|
||||
const trayWindow = this.#windowMap.get(IWindowList.TRAY_WINDOW)
|
||||
return settingWindow || trayWindow || this.create(IWindowList.SETTING_WINDOW)!
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ const CONFIG_PATH: string = dbPathChecker()
|
||||
export const DB_PATH: string = getGalleryDBPath().dbPath
|
||||
|
||||
class ConfigStore {
|
||||
private db: JSONStore
|
||||
#db: JSONStore
|
||||
constructor () {
|
||||
this.db = new JSONStore(CONFIG_PATH)
|
||||
this.#db = new JSONStore(CONFIG_PATH)
|
||||
|
||||
if (!this.db.has('picBed')) {
|
||||
this.db.set('picBed', {
|
||||
if (!this.#db.has('picBed')) {
|
||||
this.#db.set('picBed', {
|
||||
current: 'smms', // deprecated
|
||||
uploader: 'smms',
|
||||
smms: {
|
||||
@ -38,8 +38,8 @@ class ConfigStore {
|
||||
})
|
||||
}
|
||||
|
||||
if (!this.db.has(configPaths.settings.shortKey._path)) {
|
||||
this.db.set(configPaths.settings.shortKey['picgo:upload'], {
|
||||
if (!this.#db.has(configPaths.settings.shortKey._path)) {
|
||||
this.#db.set(configPaths.settings.shortKey['picgo:upload'], {
|
||||
enable: true,
|
||||
key: 'CommandOrControl+Shift+P',
|
||||
name: 'upload',
|
||||
@ -50,31 +50,31 @@ class ConfigStore {
|
||||
}
|
||||
|
||||
flush () {
|
||||
this.db = new JSONStore(CONFIG_PATH)
|
||||
this.#db = new JSONStore(CONFIG_PATH)
|
||||
}
|
||||
|
||||
read () {
|
||||
this.db.read()
|
||||
return this.db
|
||||
this.#db.read()
|
||||
return this.#db
|
||||
}
|
||||
|
||||
get (key = ''): any {
|
||||
if (key === '') {
|
||||
return this.db.read()
|
||||
return this.#db.read()
|
||||
}
|
||||
return this.db.get(key)
|
||||
return this.#db.get(key)
|
||||
}
|
||||
|
||||
set (key: string, value: any): void {
|
||||
return this.db.set(key, value)
|
||||
return this.#db.set(key, value)
|
||||
}
|
||||
|
||||
has (key: string) {
|
||||
return this.db.has(key)
|
||||
return this.#db.has(key)
|
||||
}
|
||||
|
||||
unset (key: string, value: any): boolean {
|
||||
return this.db.unset(key, value)
|
||||
return this.#db.unset(key, value)
|
||||
}
|
||||
|
||||
getConfigPath () {
|
||||
@ -88,16 +88,16 @@ export default db
|
||||
|
||||
// v2.3.0 add gallery db
|
||||
class GalleryDB {
|
||||
private static instance: DBStore
|
||||
static #instance: DBStore
|
||||
private constructor () {
|
||||
console.log('init gallery db')
|
||||
}
|
||||
|
||||
public static getInstance (): DBStore {
|
||||
if (!GalleryDB.instance) {
|
||||
GalleryDB.instance = new DBStore(DB_PATH, 'gallery')
|
||||
static getInstance (): DBStore {
|
||||
if (!GalleryDB.#instance) {
|
||||
GalleryDB.#instance = new DBStore(DB_PATH, 'gallery')
|
||||
}
|
||||
return GalleryDB.instance
|
||||
return GalleryDB.#instance
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class GuiApi implements IGuiApi {
|
||||
console.log('init guiapi')
|
||||
}
|
||||
|
||||
public static getInstance (): GuiApi {
|
||||
static getInstance (): GuiApi {
|
||||
if (!GuiApi.instance) {
|
||||
GuiApi.instance = new GuiApi()
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
import pkg from 'root/package.json'
|
||||
|
||||
// Electron modules
|
||||
import { Menu, BrowserWindow, app, dialog } from 'electron'
|
||||
import { Menu, BrowserWindow, app, dialog, shell } from 'electron'
|
||||
|
||||
// Custom utilities and modules
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
@ -140,6 +140,13 @@ const buildMainPageMenu = (win: BrowserWindow) => {
|
||||
click () {
|
||||
win?.webContents?.openDevTools({ mode: 'detach' })
|
||||
}
|
||||
},
|
||||
{
|
||||
label: T('FEEDBACK'),
|
||||
click () {
|
||||
const url = 'https://github.com/Kuingsmile/PicList/issues'
|
||||
shell.openExternal(url)
|
||||
}
|
||||
}
|
||||
]
|
||||
// @ts-ignore
|
||||
|
@ -135,7 +135,7 @@ autoUpdater.on('error', (err) => {
|
||||
})
|
||||
|
||||
class LifeCycle {
|
||||
private async beforeReady () {
|
||||
async #beforeReady () {
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
// fix the $PATH in macOS & linux
|
||||
fixPath()
|
||||
@ -148,7 +148,7 @@ class LifeCycle {
|
||||
busEventList.listen()
|
||||
}
|
||||
|
||||
private onReady () {
|
||||
#onReady () {
|
||||
const readyFunction = async () => {
|
||||
createProtocol('picgo')
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
@ -228,7 +228,7 @@ class LifeCycle {
|
||||
app.whenReady().then(readyFunction)
|
||||
}
|
||||
|
||||
private onRunning () {
|
||||
#onRunning () {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
logger.info('detect second instance')
|
||||
const result = handleStartUpFiles(commandLine, workingDirectory)
|
||||
@ -263,7 +263,7 @@ class LifeCycle {
|
||||
}
|
||||
}
|
||||
|
||||
private onQuit () {
|
||||
#onQuit () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
@ -298,10 +298,10 @@ class LifeCycle {
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
await this.beforeReady()
|
||||
this.onReady()
|
||||
this.onRunning()
|
||||
this.onQuit()
|
||||
await this.#beforeReady()
|
||||
this.#onReady()
|
||||
this.#onRunning()
|
||||
this.#onQuit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,22 +4,22 @@ import { IJSON } from '@picgo/store/dist/types'
|
||||
import { ManageApiType, ManageConfigType } from '~/universal/types/manage'
|
||||
|
||||
class ManageDB {
|
||||
private readonly ctx: ManageApiType
|
||||
private readonly db: JSONStore
|
||||
readonly #ctx: ManageApiType
|
||||
readonly #db: JSONStore
|
||||
constructor (ctx: ManageApiType) {
|
||||
this.ctx = ctx
|
||||
this.db = new JSONStore(this.ctx.configPath)
|
||||
this.#ctx = ctx
|
||||
this.#db = new JSONStore(this.#ctx.configPath)
|
||||
let initParams: IStringKeyMap = {
|
||||
picBed: {},
|
||||
settings: {},
|
||||
currentPicBed: 'placeholder'
|
||||
}
|
||||
for (let key in initParams) {
|
||||
if (!this.db.has(key)) {
|
||||
if (!this.#db.has(key)) {
|
||||
try {
|
||||
this.db.set(key, initParams[key])
|
||||
this.#db.set(key, initParams[key])
|
||||
} catch (e: any) {
|
||||
this.ctx.logger.error(e)
|
||||
this.#ctx.logger.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@ -27,27 +27,27 @@ class ManageDB {
|
||||
}
|
||||
|
||||
read (flush?: boolean): IJSON {
|
||||
return this.db.read(flush)
|
||||
return this.#db.read(flush)
|
||||
}
|
||||
|
||||
get (key: string = ''): any {
|
||||
this.read(true)
|
||||
return this.db.get(key)
|
||||
return this.#db.get(key)
|
||||
}
|
||||
|
||||
set (key: string, value: any): void {
|
||||
this.read(true)
|
||||
return this.db.set(key, value)
|
||||
return this.#db.set(key, value)
|
||||
}
|
||||
|
||||
has (key: string): boolean {
|
||||
this.read(true)
|
||||
return this.db.has(key)
|
||||
return this.#db.has(key)
|
||||
}
|
||||
|
||||
unset (key: string, value: any): boolean {
|
||||
this.read(true)
|
||||
return this.db.unset(key, value)
|
||||
return this.#db.unset(key, value)
|
||||
}
|
||||
|
||||
saveConfig (config: Partial<ManageConfigType>): void {
|
||||
|
@ -69,7 +69,7 @@ class UpDownTaskQueue {
|
||||
this.restore()
|
||||
}
|
||||
|
||||
public static getInstance () {
|
||||
static getInstance () {
|
||||
if (!UpDownTaskQueue.instance) {
|
||||
UpDownTaskQueue.instance = new UpDownTaskQueue()
|
||||
}
|
||||
|
@ -10,49 +10,49 @@ import { enforceNumber, isDev } from '#/utils/common'
|
||||
import { configPaths } from '~/universal/utils/configPaths'
|
||||
|
||||
export class ManageLogger implements ILogger {
|
||||
private readonly level = {
|
||||
readonly #level = {
|
||||
[ILogType.success]: 'green',
|
||||
[ILogType.info]: 'blue',
|
||||
[ILogType.warn]: 'yellow',
|
||||
[ILogType.error]: 'red'
|
||||
}
|
||||
|
||||
private readonly ctx: ManageApiType
|
||||
private logLevel!: string
|
||||
private logPath!: string
|
||||
readonly #ctx: ManageApiType
|
||||
#logLevel!: string
|
||||
#logPath!: string
|
||||
|
||||
constructor (ctx: ManageApiType) {
|
||||
this.ctx = ctx
|
||||
this.#ctx = ctx
|
||||
}
|
||||
|
||||
private handleLog (type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
const logHeader = chalk[this.level[type] as ILogColor](
|
||||
#handleLog (type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
|
||||
const logHeader = chalk[this.#level[type] as ILogColor](
|
||||
`[PicList ${type.toUpperCase()}]`
|
||||
)
|
||||
console.log(logHeader, ...msg)
|
||||
this.logLevel = this.ctx.getConfig(configPaths.settings.logLevel)
|
||||
this.logPath =
|
||||
this.ctx.getConfig<Undefinable<string>>(configPaths.settings.logPath) ||
|
||||
path.join(this.ctx.baseDir, './manage.log')
|
||||
this.#logLevel = this.#ctx.getConfig(configPaths.settings.logLevel)
|
||||
this.#logPath =
|
||||
this.#ctx.getConfig<Undefinable<string>>(configPaths.settings.logPath) ||
|
||||
path.join(this.#ctx.baseDir, './manage.log')
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const result = this.checkLogFileIsLarge(this.logPath)
|
||||
const result = this.#checkLogFileIsLarge(this.#logPath)
|
||||
if (result.isLarge) {
|
||||
const warningMsg = `Log file is too large (> ${
|
||||
result.logFileSizeLimit! / 1024 / 1024 || '10'
|
||||
} MB), recreate log file`
|
||||
console.log(chalk.yellow('[PicList WARN]:'), warningMsg)
|
||||
this.recreateLogFile(this.logPath)
|
||||
this.#recreateLogFile(this.#logPath)
|
||||
msg.unshift(warningMsg)
|
||||
}
|
||||
this.handleWriteLog(this.logPath, type, ...msg)
|
||||
this.#handleWriteLog(this.#logPath, type, ...msg)
|
||||
} catch (e) {
|
||||
console.error('[PicList Error] on checking log file size', e)
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
private checkLogFileIsLarge (logPath: string): {
|
||||
#checkLogFileIsLarge (logPath: string): {
|
||||
isLarge: boolean
|
||||
logFileSize?: number
|
||||
logFileSizeLimit?: number
|
||||
@ -61,7 +61,7 @@ export class ManageLogger implements ILogger {
|
||||
const logFileSize = fs.statSync(logPath).size
|
||||
const logFileSizeLimit =
|
||||
enforceNumber(
|
||||
this.ctx.getConfig<Undefinable<number>>(
|
||||
this.#ctx.getConfig<Undefinable<number>>(
|
||||
configPaths.settings.logFileSizeLimit
|
||||
) || 10
|
||||
) *
|
||||
@ -79,23 +79,23 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
private recreateLogFile (logPath: string): void {
|
||||
#recreateLogFile (logPath: string): void {
|
||||
if (fs.existsSync(logPath)) {
|
||||
fs.unlinkSync(logPath)
|
||||
fs.createFileSync(logPath)
|
||||
}
|
||||
}
|
||||
|
||||
private handleWriteLog (
|
||||
#handleWriteLog (
|
||||
logPath: string,
|
||||
type: string,
|
||||
...msg: ILogArgvTypeWithError[]
|
||||
): void {
|
||||
try {
|
||||
if (this.checkLogLevel(type, this.logLevel)) {
|
||||
if (this.#checkLogLevel(type, this.#logLevel)) {
|
||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicList ${type.toUpperCase()}] `
|
||||
msg.forEach((item: ILogArgvTypeWithError) => {
|
||||
log += this.formatLogItem(item, type)
|
||||
log += this.#formatLogItem(item, type)
|
||||
})
|
||||
log += '\n'
|
||||
fs.appendFileSync(logPath, log)
|
||||
@ -105,7 +105,7 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
}
|
||||
|
||||
private formatLogItem (item: ILogArgvTypeWithError, type: string): string {
|
||||
#formatLogItem (item: ILogArgvTypeWithError, type: string): string {
|
||||
let result = ''
|
||||
if (item instanceof Error && type === 'error') {
|
||||
result += `\n------Error Stack Begin------\n${util.format(item?.stack)}\n-------Error Stack End------- `
|
||||
@ -121,7 +121,7 @@ export class ManageLogger implements ILogger {
|
||||
return result
|
||||
}
|
||||
|
||||
private checkLogLevel (
|
||||
#checkLogLevel (
|
||||
type: string,
|
||||
level: undefined | string | string[]
|
||||
): boolean {
|
||||
@ -135,24 +135,24 @@ export class ManageLogger implements ILogger {
|
||||
}
|
||||
|
||||
success (...msq: ILogArgvType[]): void {
|
||||
return this.handleLog(ILogType.success, ...msq)
|
||||
return this.#handleLog(ILogType.success, ...msq)
|
||||
}
|
||||
|
||||
info (...msq: ILogArgvType[]): void {
|
||||
return this.handleLog(ILogType.info, ...msq)
|
||||
return this.#handleLog(ILogType.info, ...msq)
|
||||
}
|
||||
|
||||
error (...msq: ILogArgvTypeWithError[]): void {
|
||||
return this.handleLog(ILogType.error, ...msq)
|
||||
return this.#handleLog(ILogType.error, ...msq)
|
||||
}
|
||||
|
||||
warn (...msq: ILogArgvType[]): void {
|
||||
return this.handleLog(ILogType.warn, ...msq)
|
||||
return this.#handleLog(ILogType.warn, ...msq)
|
||||
}
|
||||
|
||||
debug (...msq: ILogArgvType[]): void {
|
||||
if (isDev) {
|
||||
this.handleLog(ILogType.info, ...msq)
|
||||
this.#handleLog(ILogType.info, ...msq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
import { Worker, WorkerOptions } from 'worker_threads'
|
||||
|
||||
interface Task {
|
||||
data: any
|
||||
workerOptions: WorkerOptions | undefined
|
||||
resolve: (result: any) => void
|
||||
reject: (error: any) => void
|
||||
}
|
||||
|
||||
class ThreadPool {
|
||||
private size: number
|
||||
private workerPath: string
|
||||
private availablePool: Worker[]
|
||||
private taskQueue: Task[]
|
||||
private busyPool: Worker[]
|
||||
private callBackList: any[]
|
||||
|
||||
constructor (size: number, workerPath: string) {
|
||||
this.size = size
|
||||
this.workerPath = workerPath
|
||||
this.availablePool = []
|
||||
this.busyPool = []
|
||||
for (let i = 0; i < this.size; i++) {
|
||||
this.availablePool.push(new Worker(this.workerPath))
|
||||
}
|
||||
this.taskQueue = []
|
||||
this.callBackList = []
|
||||
this.init()
|
||||
}
|
||||
|
||||
private init () {
|
||||
for (const worker of this.availablePool) {
|
||||
worker.on('message', (result) => {
|
||||
const { data } = result
|
||||
this.callBackList.shift()(data)
|
||||
this.busyPool = this.busyPool.filter((w) => w.threadId !== worker.threadId)
|
||||
this.availablePool.push(worker)
|
||||
this.processQueue()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private processQueue () {
|
||||
if (this.taskQueue.length === 0) return
|
||||
if (this.availablePool.length === 0) return
|
||||
const task = this.taskQueue.shift()
|
||||
const worker = this.availablePool.pop()
|
||||
if (worker && task) {
|
||||
this.callBackList.push(task.resolve)
|
||||
this.busyPool.push(worker)
|
||||
worker.postMessage(task.data)
|
||||
}
|
||||
}
|
||||
|
||||
public async addTask (data: any, workerOptions?: WorkerOptions): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.taskQueue.push({ data, workerOptions, resolve, reject })
|
||||
this.processQueue()
|
||||
})
|
||||
}
|
||||
|
||||
public async destroy (): Promise<void> {
|
||||
const terminatePromises = this.availablePool.map((worker) => new Promise((resolve) => {
|
||||
worker.terminate()
|
||||
worker.on('exit', () => {
|
||||
resolve(true)
|
||||
})
|
||||
}))
|
||||
await Promise.all(terminatePromises)
|
||||
this.availablePool = []
|
||||
this.taskQueue = []
|
||||
}
|
||||
}
|
||||
|
||||
export default ThreadPool
|
@ -41,17 +41,17 @@ const uploadMulter = multer({
|
||||
})
|
||||
|
||||
class Server {
|
||||
private httpServer: http.Server
|
||||
private config: IServerConfig
|
||||
#httpServer: http.Server
|
||||
#config: IServerConfig
|
||||
|
||||
constructor () {
|
||||
this.config = this.getConfigWithDefaults()
|
||||
this.httpServer = http.createServer(this.handleRequest)
|
||||
this.#config = this.getConfigWithDefaults()
|
||||
this.#httpServer = http.createServer(this.#handleRequest)
|
||||
}
|
||||
|
||||
getConfigWithDefaults () {
|
||||
let config = picgo.getConfig<IServerConfig>(configPaths.settings.server)
|
||||
if (!this.isValidConfig(config)) {
|
||||
if (!this.#isValidConfig(config)) {
|
||||
config = { port: DEFAULT_PORT, host: DEFAULT_HOST, enable: true }
|
||||
picgo.saveConfig({ [configPaths.settings.server]: config })
|
||||
}
|
||||
@ -59,20 +59,20 @@ class Server {
|
||||
return config
|
||||
}
|
||||
|
||||
private isValidConfig (config: IObj | undefined) {
|
||||
#isValidConfig (config: IObj | undefined) {
|
||||
return config && config.port && config.host && (config.enable !== undefined)
|
||||
}
|
||||
|
||||
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
#handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
switch (request.method) {
|
||||
case 'OPTIONS':
|
||||
handleResponse({ response })
|
||||
break
|
||||
case 'POST':
|
||||
this.handlePostRequest(request, response)
|
||||
this.#handlePostRequest(request, response)
|
||||
break
|
||||
case 'GET':
|
||||
this.handleGetRequest(request, response)
|
||||
this.#handleGetRequest(request, response)
|
||||
break
|
||||
default:
|
||||
logger.warn(`[PicList Server] don't support [${request.method}] method`)
|
||||
@ -81,7 +81,7 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
private handlePostRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
#handlePostRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
const [url, query] = (request.url || '').split('?')
|
||||
if (!routers.getHandler(url, 'POST')) {
|
||||
logger.warn(`[PicList Server] don't support [${url}] endpoint`)
|
||||
@ -160,7 +160,7 @@ class Server {
|
||||
}
|
||||
}
|
||||
|
||||
private handleGetRequest = (_request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
#handleGetRequest = (_request: http.IncomingMessage, response: http.ServerResponse) => {
|
||||
const [url, query] = (_request.url || '').split('?')
|
||||
if (!routers.getHandler(url, 'GET')) {
|
||||
logger.info(`[PicList Server] don't support [${url}] endpoint`)
|
||||
@ -178,23 +178,23 @@ class Server {
|
||||
}
|
||||
|
||||
// port as string is a bug
|
||||
private listen = (port: number | string) => {
|
||||
logger.info(`[PicList Server] is listening at ${port} of ${this.config.host}`)
|
||||
#listen = (port: number | string) => {
|
||||
logger.info(`[PicList Server] is listening at ${port} of ${this.#config.host}`)
|
||||
if (typeof port === 'string') {
|
||||
port = parseInt(port, 10)
|
||||
}
|
||||
this.httpServer.listen(port, this.config.host).on('error', async (err: ErrnoException) => {
|
||||
this.#httpServer.listen(port, this.#config.host).on('error', async (err: ErrnoException) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
try {
|
||||
// make sure the system has a PicGo Server instance
|
||||
await axios.post(ensureHTTPLink(`${this.config.host}:${port}/heartbeat`))
|
||||
await axios.post(ensureHTTPLink(`${this.#config.host}:${port}/heartbeat`))
|
||||
logger.info(`[PicList Server] server is already running at ${port}`)
|
||||
this.shutdown(true)
|
||||
} catch (e) {
|
||||
logger.warn(`[PicList Server] ${port} is busy, trying with port ${(port as number) + 1}`)
|
||||
// fix a bug: not write an increase number to config file
|
||||
// to solve the auto number problem
|
||||
this.listen((port as number) + 1)
|
||||
this.#listen((port as number) + 1)
|
||||
}
|
||||
} else {
|
||||
logger.error('[PicList Server]', err)
|
||||
@ -203,13 +203,13 @@ class Server {
|
||||
}
|
||||
|
||||
startup () {
|
||||
if (this.config.enable) {
|
||||
this.listen(this.config.port)
|
||||
if (this.#config.enable) {
|
||||
this.#listen(this.#config.port)
|
||||
}
|
||||
}
|
||||
|
||||
shutdown (hasStarted?: boolean) {
|
||||
this.httpServer.close()
|
||||
this.#httpServer.close()
|
||||
if (!hasStarted) {
|
||||
logger.info('[PicList Server] shutdown')
|
||||
}
|
||||
@ -217,7 +217,7 @@ class Server {
|
||||
|
||||
restart () {
|
||||
this.shutdown()
|
||||
this.config = this.getConfigWithDefaults()
|
||||
this.#config = this.getConfigWithDefaults()
|
||||
this.startup()
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
type HttpMethod = 'GET' | 'POST'
|
||||
|
||||
class Router {
|
||||
private router = new Map<string, Map<HttpMethod, {handler: routeHandler, urlparams?: URLSearchParams}>>()
|
||||
#router = new Map<string, Map<HttpMethod, {handler: routeHandler, urlparams?: URLSearchParams}>>()
|
||||
|
||||
private addRoute (method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
if (!this.router.has(url)) {
|
||||
this.router.set(url, new Map())
|
||||
#addRoute (method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
if (!this.#router.has(url)) {
|
||||
this.#router.set(url, new Map())
|
||||
}
|
||||
this.router.get(url)!.set(method, { handler: callback, urlparams })
|
||||
this.#router.get(url)!.set(method, { handler: callback, urlparams })
|
||||
}
|
||||
|
||||
get (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.addRoute('GET', url, callback, urlparams)
|
||||
this.#addRoute('GET', url, callback, urlparams)
|
||||
}
|
||||
|
||||
post (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.addRoute('POST', url, callback, urlparams)
|
||||
this.#addRoute('POST', url, callback, urlparams)
|
||||
}
|
||||
|
||||
any (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
|
||||
this.addRoute('GET', url, callback, urlparams)
|
||||
this.addRoute('POST', url, callback, urlparams)
|
||||
this.#addRoute('GET', url, callback, urlparams)
|
||||
this.#addRoute('POST', url, callback, urlparams)
|
||||
}
|
||||
|
||||
getHandler (url: string, method: HttpMethod) {
|
||||
if (this.router.has(url)) {
|
||||
const methods = this.router.get(url)!
|
||||
if (this.#router.has(url)) {
|
||||
const methods = this.#router.get(url)!
|
||||
if (methods.has(method)) {
|
||||
return methods.get(method)
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ function serveFile (res:http.ServerResponse, filePath: fs.PathLike) {
|
||||
}
|
||||
|
||||
class WebServer {
|
||||
private server!: http.Server
|
||||
private config!: IStringKeyMap
|
||||
#server!: http.Server
|
||||
#config!: IStringKeyMap
|
||||
|
||||
constructor () {
|
||||
this.loadConfig()
|
||||
@ -49,7 +49,7 @@ class WebServer {
|
||||
}
|
||||
|
||||
loadConfig (): void {
|
||||
this.config = {
|
||||
this.#config = {
|
||||
enableWebServer: picgo.getConfig<boolean>(configPaths.settings.enableWebServer) || false,
|
||||
webServerHost: picgo.getConfig<string>(configPaths.settings.webServerHost) || '0.0.0.0',
|
||||
webServerPort: picgo.getConfig<number>(configPaths.settings.webServerPort) || 37777,
|
||||
@ -58,9 +58,9 @@ class WebServer {
|
||||
}
|
||||
|
||||
initServer (): void {
|
||||
this.server = http.createServer((req, res) => {
|
||||
this.#server = http.createServer((req, res) => {
|
||||
const requestPath = req.url?.split('?')[0]
|
||||
const filePath = path.join(this.config.webServerPath, decodeURIComponent(requestPath || ''))
|
||||
const filePath = path.join(this.#config.webServerPath, decodeURIComponent(requestPath || ''))
|
||||
|
||||
try {
|
||||
const stats = fs.statSync(filePath)
|
||||
@ -77,12 +77,12 @@ class WebServer {
|
||||
}
|
||||
|
||||
start () {
|
||||
if (this.config.enableWebServer) {
|
||||
this.server
|
||||
if (this.#config.enableWebServer) {
|
||||
this.#server
|
||||
.listen(
|
||||
this.config.webServerPort === 36699 ? 37777 : this.config.webServerPort,
|
||||
this.config.webServerHost, () => {
|
||||
logger.info(`Web server is running at http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`)
|
||||
this.#config.webServerPort === 36699 ? 37777 : this.#config.webServerPort,
|
||||
this.#config.webServerHost, () => {
|
||||
logger.info(`Web server is running at http://${this.#config.webServerHost}:${this.#config.webServerPort}, root path is ${this.#config.webServerPath}`)
|
||||
})
|
||||
.on('error', (err) => {
|
||||
logger.error(err)
|
||||
@ -93,7 +93,7 @@ class WebServer {
|
||||
}
|
||||
|
||||
stop () {
|
||||
this.server.close(() => {
|
||||
this.#server.close(() => {
|
||||
logger.info('Web server is stopped')
|
||||
})
|
||||
}
|
||||
|
@ -3,18 +3,15 @@ import picgo from '@core/picgo'
|
||||
import { DEFAULT_AES_PASSWORD } from '~/universal/utils/static'
|
||||
import { configPaths } from '~/universal/utils/configPaths'
|
||||
|
||||
function getDerivedKey (): Buffer {
|
||||
const userPassword = picgo.getConfig<string>(configPaths.settings.aesPassword) || DEFAULT_AES_PASSWORD
|
||||
const fixedSalt = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex')
|
||||
const fixedIterations = 100000
|
||||
const keyLength = 32
|
||||
return crypto.pbkdf2Sync(userPassword, fixedSalt, fixedIterations, keyLength, 'sha512')
|
||||
}
|
||||
|
||||
export class AESHelper {
|
||||
key: Buffer
|
||||
|
||||
constructor () {
|
||||
this.key = getDerivedKey()
|
||||
const userPassword = picgo.getConfig<string>(configPaths.settings.aesPassword) || DEFAULT_AES_PASSWORD
|
||||
const fixedSalt = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex')
|
||||
const fixedIterations = 100000
|
||||
const keyLength = 32
|
||||
this.key = crypto.pbkdf2Sync(userPassword, fixedSalt, fixedIterations, keyLength, 'sha512')
|
||||
}
|
||||
|
||||
encrypt (plainText: string) {
|
||||
@ -22,17 +19,15 @@ export class AESHelper {
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv)
|
||||
let encrypted = cipher.update(plainText, 'utf8', 'hex')
|
||||
encrypted += cipher.final('hex')
|
||||
const encryptedData = `${iv.toString('hex')}:${encrypted}`
|
||||
return encryptedData
|
||||
return `${iv.toString('hex')}:${encrypted}`
|
||||
}
|
||||
|
||||
decrypt (encryptedData: string) {
|
||||
const parts = encryptedData.split(':')
|
||||
if (parts.length !== 2) {
|
||||
const [ivHex, encryptedText] = encryptedData.split(':')
|
||||
if (!ivHex || !encryptedText) {
|
||||
return '{}'
|
||||
}
|
||||
const iv = Buffer.from(parts[0], 'hex')
|
||||
const encryptedText = parts[1]
|
||||
const iv = Buffer.from(ivHex, 'hex')
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv)
|
||||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
|
||||
decrypted += decipher.final('utf8')
|
||||
|
@ -134,12 +134,23 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode
|
||||
sessionToken: configMap.config.sessionToken
|
||||
}
|
||||
}
|
||||
const client = new S3Client(s3Options)
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: fileKey
|
||||
})
|
||||
const result = await client.send(command)
|
||||
let result: any
|
||||
try {
|
||||
const client = new S3Client(s3Options)
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: fileKey
|
||||
})
|
||||
result = await client.send(command)
|
||||
} catch (err: any) {
|
||||
s3Options.region = 'us-east-1'
|
||||
const client = new S3Client(s3Options)
|
||||
const command = new DeleteObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: fileKey
|
||||
})
|
||||
result = await client.send(command)
|
||||
}
|
||||
return result.$metadata.httpStatusCode === 204
|
||||
} catch (err: any) {
|
||||
console.log(err)
|
||||
|
@ -11,11 +11,11 @@ class SSHClient {
|
||||
private static _client: NodeSSH
|
||||
private _isConnected = false
|
||||
|
||||
public static get instance (): SSHClient {
|
||||
static get instance (): SSHClient {
|
||||
return this._instance || (this._instance = new this())
|
||||
}
|
||||
|
||||
public static get client (): NodeSSH {
|
||||
static get client (): NodeSSH {
|
||||
return this._client || (this._client = new NodeSSH())
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ class SSHClient {
|
||||
return path.replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
public async connect (config: ISftpPlistConfig): Promise<boolean> {
|
||||
async connect (config: ISftpPlistConfig): Promise<boolean> {
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
const loginInfo: Config = privateKey
|
||||
? { username, privateKeyPath: privateKey, passphrase: passphrase || undefined }
|
||||
@ -41,7 +41,7 @@ class SSHClient {
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteFileSFTP (config: ISftpPlistConfig, remote: string): Promise<boolean> {
|
||||
async deleteFileSFTP (config: ISftpPlistConfig, remote: string): Promise<boolean> {
|
||||
try {
|
||||
const client = new Client()
|
||||
const { username, password, privateKey, passphrase } = config
|
||||
@ -162,7 +162,7 @@ class SSHClient {
|
||||
return SSHClient.client.isConnected()
|
||||
}
|
||||
|
||||
public close (): void {
|
||||
close (): void {
|
||||
SSHClient.client.dispose()
|
||||
this._isConnected = false
|
||||
}
|
||||
|
@ -6,17 +6,7 @@ interface IConfigMap {
|
||||
}
|
||||
|
||||
export default class AliyunApi {
|
||||
private static createClient (config: IConfigMap['config']): OSS {
|
||||
const { accessKeyId, accessKeySecret, bucket, area } = config
|
||||
return new OSS({
|
||||
accessKeyId,
|
||||
accessKeySecret,
|
||||
bucket,
|
||||
region: area
|
||||
})
|
||||
}
|
||||
|
||||
private static getKey (fileName: string, path?: string): string {
|
||||
static #getKey (fileName: string, path?: string): string {
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
|
||||
: fileName
|
||||
@ -25,8 +15,8 @@ export default class AliyunApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config } = configMap
|
||||
try {
|
||||
const client = AliyunApi.createClient(config)
|
||||
const key = AliyunApi.getKey(fileName, config.path)
|
||||
const client = new OSS({ ...config, region: config.area })
|
||||
const key = AliyunApi.#getKey(fileName, config.path)
|
||||
const result = await client.delete(key)
|
||||
return result.res.status === 204
|
||||
} catch (error) {
|
||||
|
@ -7,13 +7,13 @@ interface IConfigMap {
|
||||
}
|
||||
|
||||
export default class GithubApi {
|
||||
private static createOctokit (token: string) {
|
||||
static #createOctokit (token: string) {
|
||||
return new Octokit({
|
||||
auth: token
|
||||
})
|
||||
}
|
||||
|
||||
private static createKey (path: string | undefined, fileName: string): string {
|
||||
static #createKey (path: string | undefined, fileName: string): string {
|
||||
const formatedFileName = fileName.replace(/%2F/g, '/')
|
||||
return path && path !== '/'
|
||||
? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}`
|
||||
@ -23,8 +23,8 @@ export default class GithubApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, hash, config: { repo, token, branch, path } } = configMap
|
||||
const [owner, repoName] = repo.split('/')
|
||||
const octokit = GithubApi.createOctokit(token)
|
||||
const key = GithubApi.createKey(path, fileName)
|
||||
const octokit = GithubApi.#createOctokit(token)
|
||||
const key = GithubApi.#createKey(path, fileName)
|
||||
try {
|
||||
const { status } = await octokit.rest.repos.deleteFile({
|
||||
owner,
|
||||
|
@ -5,28 +5,8 @@ interface IConfigMap {
|
||||
hash?: string
|
||||
}
|
||||
|
||||
interface IConfig {
|
||||
headers: {
|
||||
Authorization: string
|
||||
}
|
||||
timeout: number
|
||||
}
|
||||
|
||||
export default class ImgurApi {
|
||||
static baseUrl = 'https://api.imgur.com/3'
|
||||
private static async makeRequest (
|
||||
method: 'delete',
|
||||
url: string,
|
||||
config: IConfig
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response: AxiosResponse = await axios[method](url, config)
|
||||
return response.status === 200
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
static #baseUrl = 'https://api.imgur.com/3'
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const {
|
||||
@ -37,17 +17,22 @@ export default class ImgurApi {
|
||||
|
||||
if (username && accessToken) {
|
||||
Authorization = `Bearer ${accessToken}`
|
||||
apiUrl = `${ImgurApi.baseUrl}/account/${username}/image/${hash}`
|
||||
apiUrl = `${ImgurApi.#baseUrl}/account/${username}/image/${hash}`
|
||||
} else if (clientId) {
|
||||
Authorization = `Client-ID ${clientId}`
|
||||
apiUrl = `${ImgurApi.baseUrl}/image/${hash}`
|
||||
apiUrl = `${ImgurApi.#baseUrl}/image/${hash}`
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
const requestConfig: IConfig = {
|
||||
headers: { Authorization },
|
||||
timeout: 30000
|
||||
try {
|
||||
const response: AxiosResponse = await axios.delete(apiUrl, {
|
||||
headers: { Authorization },
|
||||
timeout: 30000
|
||||
})
|
||||
return response.status === 200
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
return ImgurApi.makeRequest('delete', apiUrl, requestConfig)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ interface IConfigMap {
|
||||
}
|
||||
|
||||
export default class SmmsApi {
|
||||
private static readonly baseUrl = 'https://smms.app/api/v2'
|
||||
static readonly #baseUrl = 'https://smms.app/api/v2'
|
||||
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { hash, config } = configMap
|
||||
@ -19,7 +19,7 @@ export default class SmmsApi {
|
||||
|
||||
try {
|
||||
const response: AxiosResponse = await axios.get(
|
||||
`${SmmsApi.baseUrl}/delete/${hash}`, {
|
||||
`${SmmsApi.#baseUrl}/delete/${hash}`, {
|
||||
headers: {
|
||||
Authorization: token
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ interface IConfigMap {
|
||||
}
|
||||
|
||||
export default class TcyunApi {
|
||||
private static createCOS (SecretId: string, SecretKey: string): COS {
|
||||
static #createCOS (SecretId: string, SecretKey: string): COS {
|
||||
return new COS({
|
||||
SecretId,
|
||||
SecretKey
|
||||
@ -16,7 +16,7 @@ export default class TcyunApi {
|
||||
static async delete (configMap: IConfigMap): Promise<boolean> {
|
||||
const { fileName, config: { secretId, secretKey, bucket, area, path } } = configMap
|
||||
try {
|
||||
const cos = TcyunApi.createCOS(secretId, secretKey)
|
||||
const cos = TcyunApi.#createCOS(secretId, secretKey)
|
||||
let key
|
||||
if (path === '/' || !path) {
|
||||
key = `/${fileName}`
|
||||
|
@ -5,45 +5,45 @@ import bus from '@/utils/bus'
|
||||
import { builtinI18nList } from '#/i18n'
|
||||
|
||||
export class I18nManager {
|
||||
private i18n: I18n | null = null
|
||||
private i18nFileList: II18nItem[] = builtinI18nList
|
||||
#i18n: I18n | null = null
|
||||
#i18nFileList: II18nItem[] = builtinI18nList
|
||||
|
||||
private getLanguageList () {
|
||||
#getLanguageList () {
|
||||
ipcRenderer.send(GET_LANGUAGE_LIST)
|
||||
ipcRenderer.once(GET_LANGUAGE_LIST, (event, list: II18nItem[]) => {
|
||||
this.i18nFileList = list
|
||||
this.#i18nFileList = list
|
||||
})
|
||||
}
|
||||
|
||||
private getCurrentLanguage () {
|
||||
#getCurrentLanguage () {
|
||||
ipcRenderer.send(GET_CURRENT_LANGUAGE)
|
||||
ipcRenderer.once(GET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => {
|
||||
this.setLocales(lang, locales)
|
||||
this.#setLocales(lang, locales)
|
||||
bus.emit(FORCE_UPDATE)
|
||||
})
|
||||
}
|
||||
|
||||
private setLocales (lang: string, locales: ILocales) {
|
||||
#setLocales (lang: string, locales: ILocales) {
|
||||
const objectAdapter = new ObjectAdapter({
|
||||
[lang]: locales
|
||||
})
|
||||
this.i18n = new I18n({
|
||||
this.#i18n = new I18n({
|
||||
adapter: objectAdapter,
|
||||
defaultLanguage: lang
|
||||
})
|
||||
}
|
||||
|
||||
constructor () {
|
||||
this.getCurrentLanguage()
|
||||
this.getLanguageList()
|
||||
this.#getCurrentLanguage()
|
||||
this.#getLanguageList()
|
||||
ipcRenderer.on(SET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => {
|
||||
this.setLocales(lang, locales)
|
||||
this.#setLocales(lang, locales)
|
||||
bus.emit(FORCE_UPDATE)
|
||||
})
|
||||
}
|
||||
|
||||
T (key: ILocalesKey, args: IStringKeyMap = {}): string {
|
||||
return this.i18n?.translate(key, args) || key
|
||||
return this.#i18n?.translate(key, args) || key
|
||||
}
|
||||
|
||||
setCurrentLanguage (lang: string) {
|
||||
@ -51,7 +51,7 @@ export class I18nManager {
|
||||
}
|
||||
|
||||
get languageList () {
|
||||
return this.i18nFileList
|
||||
return this.#i18nFileList
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
style="width: 200px;"
|
||||
:persistent="false"
|
||||
teleported
|
||||
@change="handleChangeCustomUrl"
|
||||
@change="handleChangeCustomUrlInput"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in customDomainList"
|
||||
@ -35,7 +35,7 @@
|
||||
v-model="currentCustomDomain"
|
||||
:placeholder="$T('MANAGE_BUCKET_PAGE_CUSTOM_URL_INPUT_PLACEHOLDER')"
|
||||
style="width: 200px;"
|
||||
@blur="handleChangeCustomUrl"
|
||||
@blur="handleChangeCustomUrlInput"
|
||||
/>
|
||||
<el-link
|
||||
v-else
|
||||
@ -2063,23 +2063,14 @@ async function handleClickFile (item: any) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleChangeCustomUrlInput () {
|
||||
await handleChangeCustomUrl()
|
||||
await forceRefreshFileList()
|
||||
}
|
||||
// 自定义域名相关
|
||||
|
||||
async function handleChangeCustomUrl () {
|
||||
if (currentPicBedName.value === 'github') {
|
||||
isShowLoadingPage.value = true
|
||||
if (isLoadingData.value) {
|
||||
ElNotification({
|
||||
title: $T('MANAGE_BUCKET_CHANGE_CUSTOM_URL_TITLE'),
|
||||
message: $T('MANAGE_BUCKET_CHANGE_CUSTOM_URL_MSG'),
|
||||
type: 'error',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
isShowLoadingPage.value = true
|
||||
await resetParam(true)
|
||||
isShowLoadingPage.value = false
|
||||
} else if (['aliyun', 'tcyun', 'qiniu', 's3plist', 'webdavplist', 'local', 'sftp'].includes(currentPicBedName.value)) {
|
||||
if (['aliyun', 'tcyun', 'qiniu', 's3plist', 'webdavplist', 'local', 'sftp'].includes(currentPicBedName.value)) {
|
||||
const currentConfigs = await getConfig<any>('picBed')
|
||||
const currentConfig = currentConfigs[configMap.alias]
|
||||
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
|
||||
@ -2174,7 +2165,7 @@ async function initCustomDomainList () {
|
||||
currentCustomDomain.value = `https://${configMap.bucketName}.s3.amazonaws.com`
|
||||
}
|
||||
}
|
||||
handleChangeCustomUrl()
|
||||
await handleChangeCustomUrl()
|
||||
} else if (currentPicBedName.value === 'webdavplist') {
|
||||
const currentConfigs = await getConfig<any>('picBed')
|
||||
const currentConfig = currentConfigs[configMap.alias]
|
||||
@ -2188,17 +2179,20 @@ async function initCustomDomainList () {
|
||||
}
|
||||
currentCustomDomain.value = endpoint
|
||||
}
|
||||
handleChangeCustomUrl()
|
||||
await handleChangeCustomUrl()
|
||||
} else if (currentPicBedName.value === 'local' || currentPicBedName.value === 'sftp') {
|
||||
const currentConfigs = await getConfig<any>('picBed')
|
||||
const currentConfig = currentConfigs[configMap.alias]
|
||||
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
|
||||
if (currentTransformedConfig[configMap.bucketName] && currentTransformedConfig[configMap.bucketName]?.customUrl) {
|
||||
currentCustomDomain.value = currentTransformedConfig[configMap.bucketName].customUrl ?? ''
|
||||
if (manageStore.config.settings.isForceCustomUrlHttps && currentCustomDomain.value.startsWith('http://')) {
|
||||
currentCustomDomain.value = currentCustomDomain.value.replace('http://', 'https://')
|
||||
}
|
||||
} else {
|
||||
currentCustomDomain.value = ''
|
||||
}
|
||||
handleChangeCustomUrl()
|
||||
await handleChangeCustomUrl()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2232,6 +2226,7 @@ async function resetParam (force: boolean = false) {
|
||||
fileSortSizeReverse.value = false
|
||||
fileSortTimeReverse.value = false
|
||||
if (!isAutoRefresh.value && !force && !paging.value) {
|
||||
console.log('use cache')
|
||||
const cachedData = await searchExistFileList()
|
||||
if (cachedData.length > 0) {
|
||||
currentPageFilesInfo.push(...cachedData[0].value.fullList)
|
||||
@ -2283,7 +2278,7 @@ watch(route, async (newRoute) => {
|
||||
const parsedConfigMap = JSON.parse(queryConfigMap)
|
||||
Object.assign(configMap, parsedConfigMap)
|
||||
await initCustomDomainList()
|
||||
await resetParam(false)
|
||||
await resetParam(true)
|
||||
isShowLoadingPage.value = false
|
||||
}
|
||||
})
|
||||
@ -2837,9 +2832,8 @@ async function getBucketFileListBackStage () {
|
||||
isLoadingData.value = true
|
||||
const fileTransferStore = useFileTransferStore()
|
||||
fileTransferStore.resetFileTransferList()
|
||||
if (currentPicBedName.value === 'webdavplist' ||
|
||||
currentPicBedName.value === 'local' ||
|
||||
currentPicBedName.value === 'sftp') {
|
||||
const picBedNamesArr = ['webdavplist', 'local', 'sftp']
|
||||
if (picBedNamesArr.includes(currentPicBedName.value)) {
|
||||
param.baseDir = configMap.baseDir
|
||||
param.webPath = configMap.webPath
|
||||
}
|
||||
@ -3683,7 +3677,7 @@ onBeforeMount(async () => {
|
||||
await manageStore.refreshConfig()
|
||||
isShowLoadingPage.value = true
|
||||
await initCustomDomainList()
|
||||
await resetParam(false)
|
||||
await resetParam(true)
|
||||
isShowLoadingPage.value = false
|
||||
document.addEventListener('keydown', handleDetectShiftKey)
|
||||
document.addEventListener('keyup', handleDetectShiftKey)
|
||||
|
@ -618,7 +618,7 @@ async function initData () {
|
||||
form.isShowThumbnail = config.settings.isShowThumbnail ?? false
|
||||
form.isShowList = config.settings.isShowList ?? false
|
||||
form.isIgnoreCase = config.settings.isIgnoreCase ?? false
|
||||
form.isForceCustomUrlHttps = config.settings.isForceCustomUrlHttps ?? true
|
||||
form.isForceCustomUrlHttps = config.settings.isForceCustomUrlHttps ?? false
|
||||
form.isEncodeUrl = config.settings.isEncodeUrl ?? false
|
||||
form.isUploadKeepDirStructure = config.settings.isUploadKeepDirStructure ?? true
|
||||
form.isDownloadFileKeepDirStructure = config.settings.isDownloadKeepDirStructure ?? false
|
||||
|
@ -25,36 +25,36 @@ import { getRawData } from './common'
|
||||
|
||||
export class GalleryDB implements IGalleryDB {
|
||||
async get<T> (filter?: IFilter): Promise<IGetResult<T>> {
|
||||
const res = await this.msgHandler<IGetResult<T>>(PICGO_GET_DB, filter)
|
||||
const res = await this.#msgHandler<IGetResult<T>>(PICGO_GET_DB, filter)
|
||||
return res
|
||||
}
|
||||
|
||||
async insert<T> (value: T): Promise<IResult<T>> {
|
||||
const res = await this.msgHandler<IResult<T>>(PICGO_INSERT_DB, value)
|
||||
const res = await this.#msgHandler<IResult<T>>(PICGO_INSERT_DB, value)
|
||||
return res
|
||||
}
|
||||
|
||||
async insertMany<T> (value: T[]): Promise<IResult<T>[]> {
|
||||
const res = await this.msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value)
|
||||
const res = await this.#msgHandler<IResult<T>[]>(PICGO_INSERT_MANY_DB, value)
|
||||
return res
|
||||
}
|
||||
|
||||
async updateById (id: string, value: IObject): Promise<boolean> {
|
||||
const res = await this.msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value)
|
||||
const res = await this.#msgHandler<boolean>(PICGO_UPDATE_BY_ID_DB, id, value)
|
||||
return res
|
||||
}
|
||||
|
||||
async getById<T> (id: string): Promise<IResult<T> | undefined> {
|
||||
const res = await this.msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id)
|
||||
const res = await this.#msgHandler<IResult<T> | undefined>(PICGO_GET_BY_ID_DB, id)
|
||||
return res
|
||||
}
|
||||
|
||||
async removeById (id: string): Promise<void> {
|
||||
const res = await this.msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id)
|
||||
const res = await this.#msgHandler<void>(PICGO_REMOVE_BY_ID_DB, id)
|
||||
return res
|
||||
}
|
||||
|
||||
private msgHandler<T> (method: string, ...args: any[]): Promise<T> {
|
||||
#msgHandler<T> (method: string, ...args: any[]): Promise<T> {
|
||||
return new Promise((resolve) => {
|
||||
const callbackId = uuid()
|
||||
const callback = (event: IpcRendererEvent, data: T, returnCallbackId: string) => {
|
||||
|
1
src/universal/types/i18n.d.ts
vendored
1
src/universal/types/i18n.d.ts
vendored
@ -23,6 +23,7 @@ interface ILocales {
|
||||
FIND_NEW_VERSION: string
|
||||
NO_MORE_NOTICE: string
|
||||
SHOW_DEVTOOLS: string
|
||||
FEEDBACK: string
|
||||
CURRENT_PICBED: string
|
||||
START_WATCH_CLIPBOARD: string
|
||||
STOP_WATCH_CLIPBOARD: string
|
||||
|
@ -12397,10 +12397,10 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
||||
|
||||
piclist@^1.8.5:
|
||||
version "1.8.5"
|
||||
resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.5.tgz#982f481420c83cb42d6c1ff07bd2ad467ed71539"
|
||||
integrity sha512-RrSWViLbgTb3VeZ5Poajo6oyRbpFovF3g48/JiyMG6XChdt7BZ1dD/URmPzfrQNZM+bwunSwJr1yjhK87cSvwA==
|
||||
piclist@^1.8.7:
|
||||
version "1.8.7"
|
||||
resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.8.7.tgz#aa40af42762b857ac0c45a097421278a301320cf"
|
||||
integrity sha512-asSc588Fh1aMpIq/guqqHGhZb0wsLn+wZllKbtznDasbh4zNZvQECNDxRGVtmvsSYJlR+V+yyA2Z85AW/aQqyA==
|
||||
dependencies:
|
||||
"@aws-sdk/client-s3" "3.421.0"
|
||||
"@aws-sdk/lib-storage" "3.421.0"
|
||||
|
Loading…
Reference in New Issue
Block a user