🔨 Refactor(custom): refactor private to sharp in es6

This commit is contained in:
Kuingsmile 2024-04-15 23:12:02 +08:00
parent 4aec41a3c9
commit 0f7b043da3
19 changed files with 173 additions and 273 deletions

View File

@ -10,22 +10,22 @@ import { BrowserWindow } from 'electron'
import { IWindowList } from '#/types/enum' import { IWindowList } from '#/types/enum'
class WindowManager implements IWindowManager { class WindowManager implements IWindowManager {
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map() #windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
private windowIdMap: Map<number, IWindowList | string> = new Map() #windowIdMap: Map<number, IWindowList | string> = new Map()
create (name: IWindowList) { create (name: IWindowList) {
const windowConfig: IWindowListItem = windowList.get(name)! const windowConfig: IWindowListItem = windowList.get(name)!
if (windowConfig.isValid) { if (windowConfig.isValid) {
if (!windowConfig.multiple) { 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 window = new BrowserWindow(windowConfig.options())
const id = window.id const id = window.id
if (windowConfig.multiple) { if (windowConfig.multiple) {
this.windowMap.set(`${name}_${window.id}`, window) this.#windowMap.set(`${name}_${window.id}`, window)
this.windowIdMap.set(window.id, `${name}_${window.id}`) this.#windowIdMap.set(window.id, `${name}_${window.id}`)
} else { } else {
this.windowMap.set(name, window) this.#windowMap.set(name, window)
this.windowIdMap.set(window.id, name) this.#windowIdMap.set(window.id, name)
} }
windowConfig.callback(window, this) windowConfig.callback(window, this)
window.on('close', () => { window.on('close', () => {
@ -39,7 +39,7 @@ class WindowManager implements IWindowManager {
get (name: IWindowList) { get (name: IWindowList) {
if (this.has(name)) { if (this.has(name)) {
return this.windowMap.get(name)! return this.#windowMap.get(name)!
} else { } else {
const window = this.create(name) const window = this.create(name)
return window return window
@ -47,24 +47,24 @@ class WindowManager implements IWindowManager {
} }
has (name: IWindowList) { has (name: IWindowList) {
return this.windowMap.has(name) return this.#windowMap.has(name)
} }
deleteById = (id: number) => { deleteById = (id: number) => {
const name = this.windowIdMap.get(id) const name = this.#windowIdMap.get(id)
if (name) { if (name) {
this.windowMap.delete(name) this.#windowMap.delete(name)
this.windowIdMap.delete(id) this.#windowIdMap.delete(id)
} }
} }
getAvailableWindow (isSkipMiniWindow = false) { getAvailableWindow (isSkipMiniWindow = false) {
const miniWindow = this.windowMap.get(IWindowList.MINI_WINDOW) const miniWindow = this.#windowMap.get(IWindowList.MINI_WINDOW)
if (miniWindow && miniWindow.isVisible() && !isSkipMiniWindow) { if (miniWindow && miniWindow.isVisible() && !isSkipMiniWindow) {
return miniWindow return miniWindow
} else { } else {
const settingWindow = this.windowMap.get(IWindowList.SETTING_WINDOW) const settingWindow = this.#windowMap.get(IWindowList.SETTING_WINDOW)
const trayWindow = this.windowMap.get(IWindowList.TRAY_WINDOW) const trayWindow = this.#windowMap.get(IWindowList.TRAY_WINDOW)
return settingWindow || trayWindow || this.create(IWindowList.SETTING_WINDOW)! return settingWindow || trayWindow || this.create(IWindowList.SETTING_WINDOW)!
} }
} }

View File

@ -24,12 +24,12 @@ const CONFIG_PATH: string = dbPathChecker()
export const DB_PATH: string = getGalleryDBPath().dbPath export const DB_PATH: string = getGalleryDBPath().dbPath
class ConfigStore { class ConfigStore {
private db: JSONStore #db: JSONStore
constructor () { constructor () {
this.db = new JSONStore(CONFIG_PATH) this.#db = new JSONStore(CONFIG_PATH)
if (!this.db.has('picBed')) { if (!this.#db.has('picBed')) {
this.db.set('picBed', { this.#db.set('picBed', {
current: 'smms', // deprecated current: 'smms', // deprecated
uploader: 'smms', uploader: 'smms',
smms: { smms: {
@ -38,8 +38,8 @@ class ConfigStore {
}) })
} }
if (!this.db.has(configPaths.settings.shortKey._path)) { if (!this.#db.has(configPaths.settings.shortKey._path)) {
this.db.set(configPaths.settings.shortKey['picgo:upload'], { this.#db.set(configPaths.settings.shortKey['picgo:upload'], {
enable: true, enable: true,
key: 'CommandOrControl+Shift+P', key: 'CommandOrControl+Shift+P',
name: 'upload', name: 'upload',
@ -50,31 +50,31 @@ class ConfigStore {
} }
flush () { flush () {
this.db = new JSONStore(CONFIG_PATH) this.#db = new JSONStore(CONFIG_PATH)
} }
read () { read () {
this.db.read() this.#db.read()
return this.db return this.#db
} }
get (key = ''): any { get (key = ''): any {
if (key === '') { 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 { set (key: string, value: any): void {
return this.db.set(key, value) return this.#db.set(key, value)
} }
has (key: string) { has (key: string) {
return this.db.has(key) return this.#db.has(key)
} }
unset (key: string, value: any): boolean { unset (key: string, value: any): boolean {
return this.db.unset(key, value) return this.#db.unset(key, value)
} }
getConfigPath () { getConfigPath () {
@ -88,16 +88,16 @@ export default db
// v2.3.0 add gallery db // v2.3.0 add gallery db
class GalleryDB { class GalleryDB {
private static instance: DBStore static #instance: DBStore
private constructor () { private constructor () {
console.log('init gallery db') console.log('init gallery db')
} }
public static getInstance (): DBStore { static getInstance (): DBStore {
if (!GalleryDB.instance) { if (!GalleryDB.#instance) {
GalleryDB.instance = new DBStore(DB_PATH, 'gallery') GalleryDB.#instance = new DBStore(DB_PATH, 'gallery')
} }
return GalleryDB.instance return GalleryDB.#instance
} }
} }

View File

@ -37,7 +37,7 @@ class GuiApi implements IGuiApi {
console.log('init guiapi') console.log('init guiapi')
} }
public static getInstance (): GuiApi { static getInstance (): GuiApi {
if (!GuiApi.instance) { if (!GuiApi.instance) {
GuiApi.instance = new GuiApi() GuiApi.instance = new GuiApi()
} }

View File

@ -135,7 +135,7 @@ autoUpdater.on('error', (err) => {
}) })
class LifeCycle { class LifeCycle {
private async beforeReady () { async #beforeReady () {
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }]) protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
// fix the $PATH in macOS & linux // fix the $PATH in macOS & linux
fixPath() fixPath()
@ -148,7 +148,7 @@ class LifeCycle {
busEventList.listen() busEventList.listen()
} }
private onReady () { #onReady () {
const readyFunction = async () => { const readyFunction = async () => {
createProtocol('picgo') createProtocol('picgo')
windowManager.create(IWindowList.TRAY_WINDOW) windowManager.create(IWindowList.TRAY_WINDOW)
@ -228,7 +228,7 @@ class LifeCycle {
app.whenReady().then(readyFunction) app.whenReady().then(readyFunction)
} }
private onRunning () { #onRunning () {
app.on('second-instance', (event, commandLine, workingDirectory) => { app.on('second-instance', (event, commandLine, workingDirectory) => {
logger.info('detect second instance') logger.info('detect second instance')
const result = handleStartUpFiles(commandLine, workingDirectory) const result = handleStartUpFiles(commandLine, workingDirectory)
@ -263,7 +263,7 @@ class LifeCycle {
} }
} }
private onQuit () { #onQuit () {
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
@ -298,10 +298,10 @@ class LifeCycle {
if (!gotTheLock) { if (!gotTheLock) {
app.quit() app.quit()
} else { } else {
await this.beforeReady() await this.#beforeReady()
this.onReady() this.#onReady()
this.onRunning() this.#onRunning()
this.onQuit() this.#onQuit()
} }
} }
} }

View File

@ -4,22 +4,22 @@ import { IJSON } from '@picgo/store/dist/types'
import { ManageApiType, ManageConfigType } from '~/universal/types/manage' import { ManageApiType, ManageConfigType } from '~/universal/types/manage'
class ManageDB { class ManageDB {
private readonly ctx: ManageApiType readonly #ctx: ManageApiType
private readonly db: JSONStore readonly #db: JSONStore
constructor (ctx: ManageApiType) { constructor (ctx: ManageApiType) {
this.ctx = ctx this.#ctx = ctx
this.db = new JSONStore(this.ctx.configPath) this.#db = new JSONStore(this.#ctx.configPath)
let initParams: IStringKeyMap = { let initParams: IStringKeyMap = {
picBed: {}, picBed: {},
settings: {}, settings: {},
currentPicBed: 'placeholder' currentPicBed: 'placeholder'
} }
for (let key in initParams) { for (let key in initParams) {
if (!this.db.has(key)) { if (!this.#db.has(key)) {
try { try {
this.db.set(key, initParams[key]) this.#db.set(key, initParams[key])
} catch (e: any) { } catch (e: any) {
this.ctx.logger.error(e) this.#ctx.logger.error(e)
throw e throw e
} }
} }
@ -27,27 +27,27 @@ class ManageDB {
} }
read (flush?: boolean): IJSON { read (flush?: boolean): IJSON {
return this.db.read(flush) return this.#db.read(flush)
} }
get (key: string = ''): any { get (key: string = ''): any {
this.read(true) this.read(true)
return this.db.get(key) return this.#db.get(key)
} }
set (key: string, value: any): void { set (key: string, value: any): void {
this.read(true) this.read(true)
return this.db.set(key, value) return this.#db.set(key, value)
} }
has (key: string): boolean { has (key: string): boolean {
this.read(true) this.read(true)
return this.db.has(key) return this.#db.has(key)
} }
unset (key: string, value: any): boolean { unset (key: string, value: any): boolean {
this.read(true) this.read(true)
return this.db.unset(key, value) return this.#db.unset(key, value)
} }
saveConfig (config: Partial<ManageConfigType>): void { saveConfig (config: Partial<ManageConfigType>): void {

View File

@ -69,7 +69,7 @@ class UpDownTaskQueue {
this.restore() this.restore()
} }
public static getInstance () { static getInstance () {
if (!UpDownTaskQueue.instance) { if (!UpDownTaskQueue.instance) {
UpDownTaskQueue.instance = new UpDownTaskQueue() UpDownTaskQueue.instance = new UpDownTaskQueue()
} }

View File

@ -10,49 +10,49 @@ import { enforceNumber, isDev } from '#/utils/common'
import { configPaths } from '~/universal/utils/configPaths' import { configPaths } from '~/universal/utils/configPaths'
export class ManageLogger implements ILogger { export class ManageLogger implements ILogger {
private readonly level = { readonly #level = {
[ILogType.success]: 'green', [ILogType.success]: 'green',
[ILogType.info]: 'blue', [ILogType.info]: 'blue',
[ILogType.warn]: 'yellow', [ILogType.warn]: 'yellow',
[ILogType.error]: 'red' [ILogType.error]: 'red'
} }
private readonly ctx: ManageApiType readonly #ctx: ManageApiType
private logLevel!: string #logLevel!: string
private logPath!: string #logPath!: string
constructor (ctx: ManageApiType) { constructor (ctx: ManageApiType) {
this.ctx = ctx this.#ctx = ctx
} }
private handleLog (type: ILogType, ...msg: ILogArgvTypeWithError[]): void { #handleLog (type: ILogType, ...msg: ILogArgvTypeWithError[]): void {
const logHeader = chalk[this.level[type] as ILogColor]( const logHeader = chalk[this.#level[type] as ILogColor](
`[PicList ${type.toUpperCase()}]` `[PicList ${type.toUpperCase()}]`
) )
console.log(logHeader, ...msg) console.log(logHeader, ...msg)
this.logLevel = this.ctx.getConfig(configPaths.settings.logLevel) this.#logLevel = this.#ctx.getConfig(configPaths.settings.logLevel)
this.logPath = this.#logPath =
this.ctx.getConfig<Undefinable<string>>(configPaths.settings.logPath) || this.#ctx.getConfig<Undefinable<string>>(configPaths.settings.logPath) ||
path.join(this.ctx.baseDir, './manage.log') path.join(this.#ctx.baseDir, './manage.log')
setTimeout(() => { setTimeout(() => {
try { try {
const result = this.checkLogFileIsLarge(this.logPath) const result = this.#checkLogFileIsLarge(this.#logPath)
if (result.isLarge) { if (result.isLarge) {
const warningMsg = `Log file is too large (> ${ const warningMsg = `Log file is too large (> ${
result.logFileSizeLimit! / 1024 / 1024 || '10' result.logFileSizeLimit! / 1024 / 1024 || '10'
} MB), recreate log file` } MB), recreate log file`
console.log(chalk.yellow('[PicList WARN]:'), warningMsg) console.log(chalk.yellow('[PicList WARN]:'), warningMsg)
this.recreateLogFile(this.logPath) this.#recreateLogFile(this.#logPath)
msg.unshift(warningMsg) msg.unshift(warningMsg)
} }
this.handleWriteLog(this.logPath, type, ...msg) this.#handleWriteLog(this.#logPath, type, ...msg)
} catch (e) { } catch (e) {
console.error('[PicList Error] on checking log file size', e) console.error('[PicList Error] on checking log file size', e)
} }
}, 0) }, 0)
} }
private checkLogFileIsLarge (logPath: string): { #checkLogFileIsLarge (logPath: string): {
isLarge: boolean isLarge: boolean
logFileSize?: number logFileSize?: number
logFileSizeLimit?: number logFileSizeLimit?: number
@ -61,7 +61,7 @@ export class ManageLogger implements ILogger {
const logFileSize = fs.statSync(logPath).size const logFileSize = fs.statSync(logPath).size
const logFileSizeLimit = const logFileSizeLimit =
enforceNumber( enforceNumber(
this.ctx.getConfig<Undefinable<number>>( this.#ctx.getConfig<Undefinable<number>>(
configPaths.settings.logFileSizeLimit configPaths.settings.logFileSizeLimit
) || 10 ) || 10
) * ) *
@ -79,23 +79,23 @@ export class ManageLogger implements ILogger {
} }
} }
private recreateLogFile (logPath: string): void { #recreateLogFile (logPath: string): void {
if (fs.existsSync(logPath)) { if (fs.existsSync(logPath)) {
fs.unlinkSync(logPath) fs.unlinkSync(logPath)
fs.createFileSync(logPath) fs.createFileSync(logPath)
} }
} }
private handleWriteLog ( #handleWriteLog (
logPath: string, logPath: string,
type: string, type: string,
...msg: ILogArgvTypeWithError[] ...msg: ILogArgvTypeWithError[]
): void { ): void {
try { 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()}] ` let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicList ${type.toUpperCase()}] `
msg.forEach((item: ILogArgvTypeWithError) => { msg.forEach((item: ILogArgvTypeWithError) => {
log += this.formatLogItem(item, type) log += this.#formatLogItem(item, type)
}) })
log += '\n' log += '\n'
fs.appendFileSync(logPath, log) 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 = '' let result = ''
if (item instanceof Error && type === 'error') { if (item instanceof Error && type === 'error') {
result += `\n------Error Stack Begin------\n${util.format(item?.stack)}\n-------Error Stack End------- ` 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 return result
} }
private checkLogLevel ( #checkLogLevel (
type: string, type: string,
level: undefined | string | string[] level: undefined | string | string[]
): boolean { ): boolean {
@ -135,24 +135,24 @@ export class ManageLogger implements ILogger {
} }
success (...msq: ILogArgvType[]): void { success (...msq: ILogArgvType[]): void {
return this.handleLog(ILogType.success, ...msq) return this.#handleLog(ILogType.success, ...msq)
} }
info (...msq: ILogArgvType[]): void { info (...msq: ILogArgvType[]): void {
return this.handleLog(ILogType.info, ...msq) return this.#handleLog(ILogType.info, ...msq)
} }
error (...msq: ILogArgvTypeWithError[]): void { error (...msq: ILogArgvTypeWithError[]): void {
return this.handleLog(ILogType.error, ...msq) return this.#handleLog(ILogType.error, ...msq)
} }
warn (...msq: ILogArgvType[]): void { warn (...msq: ILogArgvType[]): void {
return this.handleLog(ILogType.warn, ...msq) return this.#handleLog(ILogType.warn, ...msq)
} }
debug (...msq: ILogArgvType[]): void { debug (...msq: ILogArgvType[]): void {
if (isDev) { if (isDev) {
this.handleLog(ILogType.info, ...msq) this.#handleLog(ILogType.info, ...msq)
} }
} }
} }

View File

@ -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

View File

@ -41,17 +41,17 @@ const uploadMulter = multer({
}) })
class Server { class Server {
private httpServer: http.Server #httpServer: http.Server
private config: IServerConfig #config: IServerConfig
constructor () { constructor () {
this.config = this.getConfigWithDefaults() this.#config = this.getConfigWithDefaults()
this.httpServer = http.createServer(this.handleRequest) this.#httpServer = http.createServer(this.#handleRequest)
} }
getConfigWithDefaults () { getConfigWithDefaults () {
let config = picgo.getConfig<IServerConfig>(configPaths.settings.server) 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 } config = { port: DEFAULT_PORT, host: DEFAULT_HOST, enable: true }
picgo.saveConfig({ [configPaths.settings.server]: config }) picgo.saveConfig({ [configPaths.settings.server]: config })
} }
@ -59,20 +59,20 @@ class Server {
return config return config
} }
private isValidConfig (config: IObj | undefined) { #isValidConfig (config: IObj | undefined) {
return config && config.port && config.host && (config.enable !== 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) { switch (request.method) {
case 'OPTIONS': case 'OPTIONS':
handleResponse({ response }) handleResponse({ response })
break break
case 'POST': case 'POST':
this.handlePostRequest(request, response) this.#handlePostRequest(request, response)
break break
case 'GET': case 'GET':
this.handleGetRequest(request, response) this.#handleGetRequest(request, response)
break break
default: default:
logger.warn(`[PicList Server] don't support [${request.method}] method`) 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('?') const [url, query] = (request.url || '').split('?')
if (!routers.getHandler(url, 'POST')) { if (!routers.getHandler(url, 'POST')) {
logger.warn(`[PicList Server] don't support [${url}] endpoint`) 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('?') const [url, query] = (_request.url || '').split('?')
if (!routers.getHandler(url, 'GET')) { if (!routers.getHandler(url, 'GET')) {
logger.info(`[PicList Server] don't support [${url}] endpoint`) logger.info(`[PicList Server] don't support [${url}] endpoint`)
@ -178,23 +178,23 @@ class Server {
} }
// port as string is a bug // port as string is a bug
private listen = (port: number | string) => { #listen = (port: number | string) => {
logger.info(`[PicList Server] is listening at ${port} of ${this.config.host}`) logger.info(`[PicList Server] is listening at ${port} of ${this.#config.host}`)
if (typeof port === 'string') { if (typeof port === 'string') {
port = parseInt(port, 10) 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') { if (err.code === 'EADDRINUSE') {
try { try {
// make sure the system has a PicGo Server instance // 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}`) logger.info(`[PicList Server] server is already running at ${port}`)
this.shutdown(true) this.shutdown(true)
} catch (e) { } catch (e) {
logger.warn(`[PicList Server] ${port} is busy, trying with port ${(port as number) + 1}`) 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 // fix a bug: not write an increase number to config file
// to solve the auto number problem // to solve the auto number problem
this.listen((port as number) + 1) this.#listen((port as number) + 1)
} }
} else { } else {
logger.error('[PicList Server]', err) logger.error('[PicList Server]', err)
@ -203,13 +203,13 @@ class Server {
} }
startup () { startup () {
if (this.config.enable) { if (this.#config.enable) {
this.listen(this.config.port) this.#listen(this.#config.port)
} }
} }
shutdown (hasStarted?: boolean) { shutdown (hasStarted?: boolean) {
this.httpServer.close() this.#httpServer.close()
if (!hasStarted) { if (!hasStarted) {
logger.info('[PicList Server] shutdown') logger.info('[PicList Server] shutdown')
} }
@ -217,7 +217,7 @@ class Server {
restart () { restart () {
this.shutdown() this.shutdown()
this.config = this.getConfigWithDefaults() this.#config = this.getConfigWithDefaults()
this.startup() this.startup()
} }
} }

View File

@ -1,31 +1,31 @@
type HttpMethod = 'GET' | 'POST' type HttpMethod = 'GET' | 'POST'
class Router { 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 { #addRoute (method: HttpMethod, url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
if (!this.router.has(url)) { if (!this.#router.has(url)) {
this.router.set(url, new Map()) 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 { 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 { 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 { any (url: string, callback: routeHandler, urlparams?: URLSearchParams): void {
this.addRoute('GET', url, callback, urlparams) this.#addRoute('GET', url, callback, urlparams)
this.addRoute('POST', url, callback, urlparams) this.#addRoute('POST', url, callback, urlparams)
} }
getHandler (url: string, method: HttpMethod) { getHandler (url: string, method: HttpMethod) {
if (this.router.has(url)) { if (this.#router.has(url)) {
const methods = this.router.get(url)! const methods = this.#router.get(url)!
if (methods.has(method)) { if (methods.has(method)) {
return methods.get(method) return methods.get(method)
} }

View File

@ -40,8 +40,8 @@ function serveFile (res:http.ServerResponse, filePath: fs.PathLike) {
} }
class WebServer { class WebServer {
private server!: http.Server #server!: http.Server
private config!: IStringKeyMap #config!: IStringKeyMap
constructor () { constructor () {
this.loadConfig() this.loadConfig()
@ -49,7 +49,7 @@ class WebServer {
} }
loadConfig (): void { loadConfig (): void {
this.config = { this.#config = {
enableWebServer: picgo.getConfig<boolean>(configPaths.settings.enableWebServer) || false, enableWebServer: picgo.getConfig<boolean>(configPaths.settings.enableWebServer) || false,
webServerHost: picgo.getConfig<string>(configPaths.settings.webServerHost) || '0.0.0.0', webServerHost: picgo.getConfig<string>(configPaths.settings.webServerHost) || '0.0.0.0',
webServerPort: picgo.getConfig<number>(configPaths.settings.webServerPort) || 37777, webServerPort: picgo.getConfig<number>(configPaths.settings.webServerPort) || 37777,
@ -58,9 +58,9 @@ class WebServer {
} }
initServer (): void { initServer (): void {
this.server = http.createServer((req, res) => { this.#server = http.createServer((req, res) => {
const requestPath = req.url?.split('?')[0] 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 { try {
const stats = fs.statSync(filePath) const stats = fs.statSync(filePath)
@ -77,12 +77,12 @@ class WebServer {
} }
start () { start () {
if (this.config.enableWebServer) { if (this.#config.enableWebServer) {
this.server this.#server
.listen( .listen(
this.config.webServerPort === 36699 ? 37777 : this.config.webServerPort, this.#config.webServerPort === 36699 ? 37777 : this.#config.webServerPort,
this.config.webServerHost, () => { this.#config.webServerHost, () => {
logger.info(`Web server is running at http://${this.config.webServerHost}:${this.config.webServerPort}, root path is ${this.config.webServerPath}`) logger.info(`Web server is running at http://${this.#config.webServerHost}:${this.#config.webServerPort}, root path is ${this.#config.webServerPath}`)
}) })
.on('error', (err) => { .on('error', (err) => {
logger.error(err) logger.error(err)
@ -93,7 +93,7 @@ class WebServer {
} }
stop () { stop () {
this.server.close(() => { this.#server.close(() => {
logger.info('Web server is stopped') logger.info('Web server is stopped')
}) })
} }

View File

@ -11,11 +11,11 @@ class SSHClient {
private static _client: NodeSSH private static _client: NodeSSH
private _isConnected = false private _isConnected = false
public static get instance (): SSHClient { static get instance (): SSHClient {
return this._instance || (this._instance = new this()) return this._instance || (this._instance = new this())
} }
public static get client (): NodeSSH { static get client (): NodeSSH {
return this._client || (this._client = new NodeSSH()) return this._client || (this._client = new NodeSSH())
} }
@ -23,7 +23,7 @@ class SSHClient {
return path.replace(/\\/g, '/') return path.replace(/\\/g, '/')
} }
public async connect (config: ISftpPlistConfig): Promise<boolean> { async connect (config: ISftpPlistConfig): Promise<boolean> {
const { username, password, privateKey, passphrase } = config const { username, password, privateKey, passphrase } = config
const loginInfo: Config = privateKey const loginInfo: Config = privateKey
? { username, privateKeyPath: privateKey, passphrase: passphrase || undefined } ? { 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 { try {
const client = new Client() const client = new Client()
const { username, password, privateKey, passphrase } = config const { username, password, privateKey, passphrase } = config
@ -162,7 +162,7 @@ class SSHClient {
return SSHClient.client.isConnected() return SSHClient.client.isConnected()
} }
public close (): void { close (): void {
SSHClient.client.dispose() SSHClient.client.dispose()
this._isConnected = false this._isConnected = false
} }

View File

@ -6,17 +6,7 @@ interface IConfigMap {
} }
export default class AliyunApi { export default class AliyunApi {
private static createClient (config: IConfigMap['config']): OSS { static #getKey (fileName: string, path?: string): string {
const { accessKeyId, accessKeySecret, bucket, area } = config
return new OSS({
accessKeyId,
accessKeySecret,
bucket,
region: area
})
}
private static getKey (fileName: string, path?: string): string {
return path && path !== '/' return path && path !== '/'
? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
: fileName : fileName
@ -25,8 +15,8 @@ export default class AliyunApi {
static async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, config } = configMap const { fileName, config } = configMap
try { try {
const client = AliyunApi.createClient(config) const client = new OSS({ ...config, region: config.area })
const key = AliyunApi.getKey(fileName, config.path) const key = AliyunApi.#getKey(fileName, config.path)
const result = await client.delete(key) const result = await client.delete(key)
return result.res.status === 204 return result.res.status === 204
} catch (error) { } catch (error) {

View File

@ -7,13 +7,13 @@ interface IConfigMap {
} }
export default class GithubApi { export default class GithubApi {
private static createOctokit (token: string) { static #createOctokit (token: string) {
return new Octokit({ return new Octokit({
auth: token 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, '/') const formatedFileName = fileName.replace(/%2F/g, '/')
return path && path !== '/' return path && path !== '/'
? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}`
@ -23,8 +23,8 @@ export default class GithubApi {
static async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, hash, config: { repo, token, branch, path } } = configMap const { fileName, hash, config: { repo, token, branch, path } } = configMap
const [owner, repoName] = repo.split('/') const [owner, repoName] = repo.split('/')
const octokit = GithubApi.createOctokit(token) const octokit = GithubApi.#createOctokit(token)
const key = GithubApi.createKey(path, fileName) const key = GithubApi.#createKey(path, fileName)
try { try {
const { status } = await octokit.rest.repos.deleteFile({ const { status } = await octokit.rest.repos.deleteFile({
owner, owner,

View File

@ -5,28 +5,8 @@ interface IConfigMap {
hash?: string hash?: string
} }
interface IConfig {
headers: {
Authorization: string
}
timeout: number
}
export default class ImgurApi { export default class ImgurApi {
static baseUrl = 'https://api.imgur.com/3' 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 async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { const {
@ -37,17 +17,22 @@ export default class ImgurApi {
if (username && accessToken) { if (username && accessToken) {
Authorization = `Bearer ${accessToken}` Authorization = `Bearer ${accessToken}`
apiUrl = `${ImgurApi.baseUrl}/account/${username}/image/${hash}` apiUrl = `${ImgurApi.#baseUrl}/account/${username}/image/${hash}`
} else if (clientId) { } else if (clientId) {
Authorization = `Client-ID ${clientId}` Authorization = `Client-ID ${clientId}`
apiUrl = `${ImgurApi.baseUrl}/image/${hash}` apiUrl = `${ImgurApi.#baseUrl}/image/${hash}`
} else { } else {
return false return false
} }
const requestConfig: IConfig = { try {
headers: { Authorization }, const response: AxiosResponse = await axios.delete(apiUrl, {
timeout: 30000 headers: { Authorization },
timeout: 30000
})
return response.status === 200
} catch (error) {
console.error(error)
return false
} }
return ImgurApi.makeRequest('delete', apiUrl, requestConfig)
} }
} }

View File

@ -6,7 +6,7 @@ interface IConfigMap {
} }
export default class SmmsApi { 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> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { hash, config } = configMap const { hash, config } = configMap
@ -19,7 +19,7 @@ export default class SmmsApi {
try { try {
const response: AxiosResponse = await axios.get( const response: AxiosResponse = await axios.get(
`${SmmsApi.baseUrl}/delete/${hash}`, { `${SmmsApi.#baseUrl}/delete/${hash}`, {
headers: { headers: {
Authorization: token Authorization: token
}, },

View File

@ -6,7 +6,7 @@ interface IConfigMap {
} }
export default class TcyunApi { export default class TcyunApi {
private static createCOS (SecretId: string, SecretKey: string): COS { static #createCOS (SecretId: string, SecretKey: string): COS {
return new COS({ return new COS({
SecretId, SecretId,
SecretKey SecretKey
@ -16,7 +16,7 @@ export default class TcyunApi {
static async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, config: { secretId, secretKey, bucket, area, path } } = configMap const { fileName, config: { secretId, secretKey, bucket, area, path } } = configMap
try { try {
const cos = TcyunApi.createCOS(secretId, secretKey) const cos = TcyunApi.#createCOS(secretId, secretKey)
let key let key
if (path === '/' || !path) { if (path === '/' || !path) {
key = `/${fileName}` key = `/${fileName}`

View File

@ -5,45 +5,45 @@ import bus from '@/utils/bus'
import { builtinI18nList } from '#/i18n' import { builtinI18nList } from '#/i18n'
export class I18nManager { export class I18nManager {
private i18n: I18n | null = null #i18n: I18n | null = null
private i18nFileList: II18nItem[] = builtinI18nList #i18nFileList: II18nItem[] = builtinI18nList
private getLanguageList () { #getLanguageList () {
ipcRenderer.send(GET_LANGUAGE_LIST) ipcRenderer.send(GET_LANGUAGE_LIST)
ipcRenderer.once(GET_LANGUAGE_LIST, (event, list: II18nItem[]) => { ipcRenderer.once(GET_LANGUAGE_LIST, (event, list: II18nItem[]) => {
this.i18nFileList = list this.#i18nFileList = list
}) })
} }
private getCurrentLanguage () { #getCurrentLanguage () {
ipcRenderer.send(GET_CURRENT_LANGUAGE) ipcRenderer.send(GET_CURRENT_LANGUAGE)
ipcRenderer.once(GET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => { ipcRenderer.once(GET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => {
this.setLocales(lang, locales) this.#setLocales(lang, locales)
bus.emit(FORCE_UPDATE) bus.emit(FORCE_UPDATE)
}) })
} }
private setLocales (lang: string, locales: ILocales) { #setLocales (lang: string, locales: ILocales) {
const objectAdapter = new ObjectAdapter({ const objectAdapter = new ObjectAdapter({
[lang]: locales [lang]: locales
}) })
this.i18n = new I18n({ this.#i18n = new I18n({
adapter: objectAdapter, adapter: objectAdapter,
defaultLanguage: lang defaultLanguage: lang
}) })
} }
constructor () { constructor () {
this.getCurrentLanguage() this.#getCurrentLanguage()
this.getLanguageList() this.#getLanguageList()
ipcRenderer.on(SET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => { ipcRenderer.on(SET_CURRENT_LANGUAGE, (event, lang: string, locales: ILocales) => {
this.setLocales(lang, locales) this.#setLocales(lang, locales)
bus.emit(FORCE_UPDATE) bus.emit(FORCE_UPDATE)
}) })
} }
T (key: ILocalesKey, args: IStringKeyMap = {}): string { T (key: ILocalesKey, args: IStringKeyMap = {}): string {
return this.i18n?.translate(key, args) || key return this.#i18n?.translate(key, args) || key
} }
setCurrentLanguage (lang: string) { setCurrentLanguage (lang: string) {
@ -51,7 +51,7 @@ export class I18nManager {
} }
get languageList () { get languageList () {
return this.i18nFileList return this.#i18nFileList
} }
} }

View File

@ -25,36 +25,36 @@ import { getRawData } from './common'
export class GalleryDB implements IGalleryDB { export class GalleryDB implements IGalleryDB {
async get<T> (filter?: IFilter): Promise<IGetResult<T>> { 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 return res
} }
async insert<T> (value: T): Promise<IResult<T>> { 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 return res
} }
async insertMany<T> (value: T[]): Promise<IResult<T>[]> { 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 return res
} }
async updateById (id: string, value: IObject): Promise<boolean> { 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 return res
} }
async getById<T> (id: string): Promise<IResult<T> | undefined> { 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 return res
} }
async removeById (id: string): Promise<void> { 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 return res
} }
private msgHandler<T> (method: string, ...args: any[]): Promise<T> { #msgHandler<T> (method: string, ...args: any[]): Promise<T> {
return new Promise((resolve) => { return new Promise((resolve) => {
const callbackId = uuid() const callbackId = uuid()
const callback = (event: IpcRendererEvent, data: T, returnCallbackId: string) => { const callback = (event: IpcRendererEvent, data: T, returnCallbackId: string) => {