From 9d4e280b3da182bc4544c5f1cc27156462d09212 Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:25:20 +0800 Subject: [PATCH] :bug: Fix(custom): fix titlebar in macos --- .../events/rpc/routes/toolbox/checkProxy.ts | 179 ++++---- src/main/lifeCycle/index.ts | 4 +- src/main/utils/deleteFunc.ts | 13 +- src/main/utils/sshClient.ts | 387 +++++++++--------- src/renderer/components/ui/TitleBar.vue | 6 +- src/renderer/manage/pages/BucketPage.vue | 1 - src/renderer/manage/pages/LogInPage.vue | 2 - src/renderer/manage/pages/ManageMain.vue | 2 - src/renderer/utils/global.ts | 39 +- 9 files changed, 316 insertions(+), 317 deletions(-) diff --git a/src/main/events/rpc/routes/toolbox/checkProxy.ts b/src/main/events/rpc/routes/toolbox/checkProxy.ts index 055d6c50..2b66be70 100644 --- a/src/main/events/rpc/routes/toolbox/checkProxy.ts +++ b/src/main/events/rpc/routes/toolbox/checkProxy.ts @@ -1,90 +1,89 @@ -import { dbPathChecker } from '@core/datastore/dbChecker' -import axios, { AxiosRequestConfig } from 'axios' -import fs from 'fs-extra' -import { IConfig } from 'piclist' -import tunnel from 'tunnel' - -import type { IToolboxCheckerMap } from '#/types/rpc' -import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils' -import { T as $t } from '~/i18n' -import { IToolboxItemCheckStatus, IToolboxItemType } from '~/utils/enum' - -function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null { - if (proxyStr) { - try { - const proxyOptions = new URL(proxyStr) - return { - host: proxyOptions.hostname, - port: parseInt(proxyOptions.port || '0', 10), - protocol: proxyOptions.protocol - } - } catch (e) {} - } - return null -} - -const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY) - -export const checkProxyMap: IToolboxCheckerMap = { - [IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async event => { - sendToolboxRes(event, { - status: IToolboxItemCheckStatus.LOADING - }) - const configFilePath = dbPathChecker() - if (fs.existsSync(configFilePath)) { - let config: IConfig | undefined - try { - config = (await fs.readJSON(configFilePath)) as IConfig - } catch (e) {} - if (!config) { - return sendToolboxRes(event, { - status: IToolboxItemCheckStatus.SUCCESS, - msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') - }) - } - - const proxy = config.picBed?.proxy - if (!proxy) { - return sendToolboxRes(event, { - status: IToolboxItemCheckStatus.SUCCESS, - msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') - }) - } else { - const proxyOptions = getProxy(proxy) - if (!proxyOptions) { - return sendToolboxRes(event, { - status: IToolboxItemCheckStatus.ERROR, - msg: $t('TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_CORRECT') - }) - } else { - const httpsAgent = tunnel.httpsOverHttp({ - proxy: { - host: proxyOptions.host, - port: proxyOptions.port - } - }) - try { - await axios.get('https://www.google.com', { - httpsAgent - }) - return sendToolboxRes(event, { - status: IToolboxItemCheckStatus.SUCCESS, - msg: $t('TOOLBOX_CHECK_PROXY_SUCCESS_TIPS') - }) - } catch (e) { - console.log(e) - return sendToolboxRes(event, { - status: IToolboxItemCheckStatus.ERROR, - msg: $t('TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_WORKING') - }) - } - } - } - } - - sendToolboxRes(event, { - status: IToolboxItemCheckStatus.SUCCESS, - msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') - }) - } -} +import { dbPathChecker } from '@core/datastore/dbChecker' +import axios, { AxiosRequestConfig } from 'axios' +import fs from 'fs-extra' +import { IConfig } from 'piclist' +import tunnel from 'tunnel' + +import type { IToolboxCheckerMap } from '#/types/rpc' +import { sendToolboxResWithType } from '~/events/rpc/routes/toolbox/utils' +import { T as $t } from '~/i18n' +import { IToolboxItemCheckStatus, IToolboxItemType } from '~/utils/enum' + +function getProxy (proxyStr: string): AxiosRequestConfig['proxy'] | null { + if (proxyStr) { + try { + const proxyOptions = new URL(proxyStr) + return { + host: proxyOptions.hostname, + port: parseInt(proxyOptions.port || '0', 10), + protocol: proxyOptions.protocol + } + } catch (e) {} + } + return null +} + +const sendToolboxRes = sendToolboxResWithType(IToolboxItemType.HAS_PROBLEM_WITH_PROXY) + +export const checkProxyMap: IToolboxCheckerMap = { + [IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: async event => { + sendToolboxRes(event, { + status: IToolboxItemCheckStatus.LOADING + }) + const configFilePath = dbPathChecker() + if (fs.existsSync(configFilePath)) { + let config: IConfig | undefined + try { + config = (await fs.readJSON(configFilePath)) as IConfig + } catch (e) {} + if (!config) { + return sendToolboxRes(event, { + status: IToolboxItemCheckStatus.SUCCESS, + msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') + }) + } + + const proxy = config.picBed?.proxy + if (!proxy) { + return sendToolboxRes(event, { + status: IToolboxItemCheckStatus.SUCCESS, + msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') + }) + } else { + const proxyOptions = getProxy(proxy) + if (!proxyOptions) { + return sendToolboxRes(event, { + status: IToolboxItemCheckStatus.ERROR, + msg: $t('TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_CORRECT') + }) + } else { + const httpsAgent = tunnel.httpsOverHttp({ + proxy: { + host: proxyOptions.host, + port: proxyOptions.port + } + }) + try { + await axios.get('https://www.google.com', { + httpsAgent + }) + return sendToolboxRes(event, { + status: IToolboxItemCheckStatus.SUCCESS, + msg: $t('TOOLBOX_CHECK_PROXY_SUCCESS_TIPS') + }) + } catch (e) { + return sendToolboxRes(event, { + status: IToolboxItemCheckStatus.ERROR, + msg: $t('TOOLBOX_CHECK_PROXY_PROXY_IS_NOT_WORKING') + }) + } + } + } + } + + sendToolboxRes(event, { + status: IToolboxItemCheckStatus.SUCCESS, + msg: $t('TOOLBOX_CHECK_PROXY_NO_PROXY_TIPS') + }) + } +} diff --git a/src/main/lifeCycle/index.ts b/src/main/lifeCycle/index.ts index 5f898b1a..9f57e514 100644 --- a/src/main/lifeCycle/index.ts +++ b/src/main/lifeCycle/index.ts @@ -151,7 +151,7 @@ updater.autoUpdater.on('update-downloaded', () => { }) updater.autoUpdater.on('error', err => { - console.log(err) + logger.error(err) }) class LifeCycle { @@ -175,7 +175,7 @@ class LifeCycle { const readyFunction = async () => { if (process.env.NODE_ENV !== 'production') { installExtension(VUEJS_DEVTOOLS).catch(err => { - console.log('An error occurred: ', err) + logger.error('An error occurred: ', err) }) } windowManager.create(IWindowList.TRAY_WINDOW) diff --git a/src/main/utils/deleteFunc.ts b/src/main/utils/deleteFunc.ts index e668ad8c..d2c230e3 100644 --- a/src/main/utils/deleteFunc.ts +++ b/src/main/utils/deleteFunc.ts @@ -6,6 +6,7 @@ import querystring from 'node:querystring' import type { S3ClientConfig } from '@aws-sdk/client-s3' import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3' +import logger from '@core/picgo/logger' import { NodeHttpHandler } from '@smithy/node-http-handler' import axios from 'axios' import type { ISftpPlistConfig } from 'piclist' @@ -82,7 +83,7 @@ async function getDogeToken (accessKey: string, secretKey: string): Promise { - const { username, password, privateKey, passphrase } = config - const loginInfo: Config = privateKey - ? { - username, - privateKeyPath: privateKey, - passphrase: passphrase || undefined - } - : { username, password } - try { - await SSHClient.client.connect({ - host: config.host, - port: Number(config.port) || 22, - ...loginInfo - }) - this._isConnected = true - return true - } catch (err: any) { - throw new Error(err) - } - } - - async deleteFileSFTP (config: ISftpPlistConfig, remote: string): Promise { - try { - const client = new Client() - const { username, password, privateKey, passphrase } = config - const loginInfo: Config = privateKey - ? { - username, - privateKey: fs.readFileSync(privateKey), - passphrase: passphrase || undefined - } - : { username, password } - remote = this.changeWinStylePathToUnix(remote) - if (remote === '/' || remote.includes('*')) return false - const promise = new Promise((resolve, reject) => { - client - .on('ready', () => { - client.sftp( - ( - err: any, - sftp: { - unlink: (arg0: string, arg1: (err: any) => void) => void - } - ) => { - // eslint-disable-next-line prefer-promise-reject-errors - if (err) reject(false) - sftp.unlink(remote, (err: any) => { - // eslint-disable-next-line prefer-promise-reject-errors - if (err) reject(false) - client.end() - resolve(true) - }) - } - ) - }) - .connect({ - host: config.host, - port: Number(config.port) || 22, - ...loginInfo - }) - }) - return (await promise) as boolean - } catch (err: any) { - console.log(err) - return false - } - } - - private async exec (script: string): Promise { - const execResult = await SSHClient.client.execCommand(script) - return execResult.code === 0 - } - - async execCommand (script: string): Promise { - const execResult = await SSHClient.client.execCommand(script) - return execResult || { code: 1, stdout: '', stderr: '' } - } - - async getFile (local: string, remote: string): Promise { - if (!this._isConnected) { - throw new Error('SSH 未连接') - } - try { - remote = this.changeWinStylePathToUnix(remote) - local = this.changeWinStylePathToUnix(local) - await SSHClient.client.getFile(local, remote, undefined, { - concurrency: 1 - }) - return true - } catch (err: any) { - console.log(err) - return false - } - } - - async putFile ( - local: string, - remote: string, - config: { - fileMode?: string - dirMode?: string - } = {} - ): Promise { - if (!this._isConnected) { - throw new Error('SSH 未连接') - } - try { - remote = this.changeWinStylePathToUnix(remote) - await this.mkdir(path.dirname(remote).replace(/^\/+|\/+$/g, ''), config) - await SSHClient.client.putFile(local, remote) - const fileMode = config.fileMode || '0644' - if (fileMode !== '0644') { - const script = `chmod ${fileMode} "${remote}"` - return await this.exec(script) - } - return true - } catch (err: any) { - console.log(err) - return false - } - } - - async mkdir ( - dirPath: string, - config: { - dirMode?: string - } = {} - ): Promise { - if (!this._isConnected) { - throw new Error('SSH 未连接') - } - try { - const directoryMode = config.dirMode || '0755' - if (directoryMode === '0755') { - const script = `mkdir -p "${dirPath}"` - return await this.exec(script) - } else { - const dirs = dirPath.split('/') - let currentPath = '' - for (const dir of dirs) { - if (dir) { - currentPath += `/${dir}` - const script = `mkdir "${currentPath}" && chmod ${directoryMode} "${currentPath}"` - const result = await this.exec(script) - if (!result) { - return false - } - } - } - return true - } - } catch (err: any) { - console.log(err) - return false - } - } - - get isConnected (): boolean { - return SSHClient.client.isConnected() - } - - close (): void { - SSHClient.client.dispose() - this._isConnected = false - } -} - -export default SSHClient +import path from 'node:path' + +import logger from '@core/picgo/logger' +import fs from 'fs-extra' +import { Config, NodeSSH, SSHExecCommandResponse } from 'node-ssh-no-cpu-features' +import { ISftpPlistConfig } from 'piclist/dist/types' +import { Client } from 'ssh2-no-cpu-features' + +class SSHClient { + private static _instance: SSHClient + private static _client: NodeSSH + private _isConnected = false + + static get instance (): SSHClient { + return this._instance || (this._instance = new this()) + } + + static get client (): NodeSSH { + return this._client || (this._client = new NodeSSH()) + } + + private changeWinStylePathToUnix (path: string): string { + return path.replace(/\\/g, '/') + } + + async connect (config: ISftpPlistConfig): Promise { + const { username, password, privateKey, passphrase } = config + const loginInfo: Config = privateKey + ? { + username, + privateKeyPath: privateKey, + passphrase: passphrase || undefined + } + : { username, password } + try { + await SSHClient.client.connect({ + host: config.host, + port: Number(config.port) || 22, + ...loginInfo + }) + this._isConnected = true + return true + } catch (err: any) { + throw new Error(err) + } + } + + async deleteFileSFTP (config: ISftpPlistConfig, remote: string): Promise { + try { + const client = new Client() + const { username, password, privateKey, passphrase } = config + const loginInfo: Config = privateKey + ? { + username, + privateKey: fs.readFileSync(privateKey), + passphrase: passphrase || undefined + } + : { username, password } + remote = this.changeWinStylePathToUnix(remote) + if (remote === '/' || remote.includes('*')) return false + const promise = new Promise((resolve, reject) => { + client + .on('ready', () => { + client.sftp( + ( + err: any, + sftp: { + unlink: (arg0: string, arg1: (err: any) => void) => void + } + ) => { + // eslint-disable-next-line prefer-promise-reject-errors + if (err) reject(false) + sftp.unlink(remote, (err: any) => { + // eslint-disable-next-line prefer-promise-reject-errors + if (err) reject(false) + client.end() + resolve(true) + }) + } + ) + }) + .connect({ + host: config.host, + port: Number(config.port) || 22, + ...loginInfo + }) + }) + return (await promise) as boolean + } catch (err: any) { + logger.error(err) + return false + } + } + + private async exec (script: string): Promise { + const execResult = await SSHClient.client.execCommand(script) + return execResult.code === 0 + } + + async execCommand (script: string): Promise { + const execResult = await SSHClient.client.execCommand(script) + return execResult || { code: 1, stdout: '', stderr: '' } + } + + async getFile (local: string, remote: string): Promise { + if (!this._isConnected) { + throw new Error('SSH 未连接') + } + try { + remote = this.changeWinStylePathToUnix(remote) + local = this.changeWinStylePathToUnix(local) + await SSHClient.client.getFile(local, remote, undefined, { + concurrency: 1 + }) + return true + } catch (err: any) { + logger.error(err) + return false + } + } + + async putFile ( + local: string, + remote: string, + config: { + fileMode?: string + dirMode?: string + } = {} + ): Promise { + if (!this._isConnected) { + throw new Error('SSH 未连接') + } + try { + remote = this.changeWinStylePathToUnix(remote) + await this.mkdir(path.dirname(remote).replace(/^\/+|\/+$/g, ''), config) + await SSHClient.client.putFile(local, remote) + const fileMode = config.fileMode || '0644' + if (fileMode !== '0644') { + const script = `chmod ${fileMode} "${remote}"` + return await this.exec(script) + } + return true + } catch (err: any) { + logger.error(err) + return false + } + } + + async mkdir ( + dirPath: string, + config: { + dirMode?: string + } = {} + ): Promise { + if (!this._isConnected) { + throw new Error('SSH 未连接') + } + try { + const directoryMode = config.dirMode || '0755' + if (directoryMode === '0755') { + const script = `mkdir -p "${dirPath}"` + return await this.exec(script) + } else { + const dirs = dirPath.split('/') + let currentPath = '' + for (const dir of dirs) { + if (dir) { + currentPath += `/${dir}` + const script = `mkdir "${currentPath}" && chmod ${directoryMode} "${currentPath}"` + const result = await this.exec(script) + if (!result) { + return false + } + } + } + return true + } + } catch (err: any) { + logger.error(err) + return false + } + } + + get isConnected (): boolean { + return SSHClient.client.isConnected() + } + + close (): void { + SSHClient.client.dispose() + this._isConnected = false + } +} + +export default SSHClient diff --git a/src/renderer/components/ui/TitleBar.vue b/src/renderer/components/ui/TitleBar.vue index 5e2a4945..48a8c279 100644 --- a/src/renderer/components/ui/TitleBar.vue +++ b/src/renderer/components/ui/TitleBar.vue @@ -4,7 +4,10 @@ data-drag-region >
-
+
0) { currentPageFilesInfo.push(...cachedData[0].value.fullList) diff --git a/src/renderer/manage/pages/LogInPage.vue b/src/renderer/manage/pages/LogInPage.vue index 2097fa8d..65240d41 100644 --- a/src/renderer/manage/pages/LogInPage.vue +++ b/src/renderer/manage/pages/LogInPage.vue @@ -585,8 +585,6 @@ async function handleConfigChange (name: string) { for (const key of allKeys) { const resultKey = name + '.' + key - console.log('Config change detected for:', resultKey) - if (key === 'customUrl' && configResult[resultKey] !== undefined && configResult[resultKey] !== '') { if (name !== 'upyun') { configResult[resultKey] = formatEndpoint(configResult[resultKey], false) diff --git a/src/renderer/manage/pages/ManageMain.vue b/src/renderer/manage/pages/ManageMain.vue index c8722d18..7ddd803b 100644 --- a/src/renderer/manage/pages/ManageMain.vue +++ b/src/renderer/manage/pages/ManageMain.vue @@ -371,7 +371,6 @@ const picBedSwitchDialogVisible = ref(false) watch(route, async (newRoute) => { if (newRoute.fullPath.split('?')[0] === '/main-page/manage-main-page') { - console.log('route changed') currentAlias.value = newRoute.query.alias as string currentPicBedName.value = newRoute.query.picBedName as string allPicBedConfigure = JSON.parse(newRoute.query.allPicBedConfigure as string) @@ -553,7 +552,6 @@ function changePicBed () { } function openBucketPageSetting () { - console.log('Open Bucket Page Setting') router.push({ path: '/main-page/manage-main-page/manage-setting-page', query: { diff --git a/src/renderer/utils/global.ts b/src/renderer/utils/global.ts index aa542c8e..5eae6aac 100644 --- a/src/renderer/utils/global.ts +++ b/src/renderer/utils/global.ts @@ -1,20 +1,19 @@ -import { ref } from 'vue' - -import { IRPCActionType } from '@/utils/enum' -import type { IPicBedType } from '#/types/types' - -console.log('global.ts loaded', window.electron.platform) -const osGlobal = ref(window.electron.platform) - -const picBedGlobal = ref([]) -const pageReloadCount = ref(0) - -async function updatePicBedGlobal () { - picBedGlobal.value = (await window.electron.triggerRPC(IRPCActionType.MAIN_GET_PICBED))! -} - -async function updatePageReloadCount () { - pageReloadCount.value++ -} - -export { osGlobal, pageReloadCount, picBedGlobal, updatePageReloadCount, updatePicBedGlobal } +import { ref } from 'vue' + +import { IRPCActionType } from '@/utils/enum' +import type { IPicBedType } from '#/types/types' + +const osGlobal = ref(window.electron.platform) + +const picBedGlobal = ref([]) +const pageReloadCount = ref(0) + +async function updatePicBedGlobal () { + picBedGlobal.value = (await window.electron.triggerRPC(IRPCActionType.MAIN_GET_PICBED))! +} + +async function updatePageReloadCount () { + pageReloadCount.value++ +} + +export { osGlobal, pageReloadCount, picBedGlobal, updatePageReloadCount, updatePicBedGlobal }