diff --git a/.electron-builder.config.js b/.electron-builder.config.js index a512dcd..9e01beb 100644 --- a/.electron-builder.config.js +++ b/.electron-builder.config.js @@ -31,6 +31,10 @@ module.exports = async function () { from: 'migrations', to: 'app/migrations', }, + { + from: 'assets', + to: 'app/assets', + }, ], extraMetadata: { version: getVersion(), @@ -43,6 +47,9 @@ module.exports = async function () { createDesktopShortcut: true, createStartMenuShortcut: true, shortcutName: 'chrome-power', + win: { + icon: "buildResources/icon.ico", + } }, win: { diff --git a/buildResources/icon.ico b/buildResources/icon.ico new file mode 100644 index 0000000..6b4fd8c Binary files /dev/null and b/buildResources/icon.ico differ diff --git a/packages/main/src/fingerprint/index.ts b/packages/main/src/fingerprint/index.ts index 61e1c98..966993f 100644 --- a/packages/main/src/fingerprint/index.ts +++ b/packages/main/src/fingerprint/index.ts @@ -26,8 +26,6 @@ const logger = createLogger(WINDOW_LOGGER_LABEL); const HOST = '127.0.0.1'; -const settings = getSettings(); - // const HomePath = app.getPath('userData'); // console.log(HomePath); @@ -85,10 +83,13 @@ async function connectBrowser(port: number, ipInfo: IP, windowId: number) { } catch (error) { logger.error(error); } + return data; } } const getDriverPath = () => { + const settings = getSettings(); + if (settings.useLocalChrome) { return settings.localChromePath; } else { @@ -101,6 +102,8 @@ export async function openFingerprintWindow(id: number) { const proxyData = await ProxyDB.getById(windowData.proxy_id); const proxyType = proxyData?.proxy_type?.toLowerCase(); const userDataPath = app.getPath('userData'); + const settings = getSettings(); + let cachePath; if (settings.profileCachePath) { cachePath = settings.profileCachePath; @@ -108,7 +111,7 @@ export async function openFingerprintWindow(id: number) { cachePath = join(userDataPath, 'cache'); } const win = BrowserWindow.getAllWindows()[0]; - const windowDataDir = `${cachePath}\\${id}_${windowData.profile_id}`; + const windowDataDir = `${cachePath}\\${windowData.profile_id}`; const driverPath = getDriverPath(); let ipInfo = {timeZone: '', ip: '', ll: [], country: ''}; @@ -213,13 +216,16 @@ export async function openFingerprintWindow(id: number) { await sleep(1); try { - connectBrowser(chromePort, ipInfo, windowData.id); + return connectBrowser(chromePort, ipInfo, windowData.id); } catch (error) { logger.error(error); execSync(`taskkill /PID ${chromeInstance.pid} /F`); + closeFingerprintWindow(id, true); + return null; } } else { - win.webContents.send('window-opened', null); + logger.error('Driver path is empty'); + return null; } } diff --git a/packages/main/src/server/index.ts b/packages/main/src/server/index.ts index fafc65f..49da705 100644 --- a/packages/main/src/server/index.ts +++ b/packages/main/src/server/index.ts @@ -4,6 +4,7 @@ import express from 'express'; import cors from 'cors'; import IPRouter from './routes/ip'; import WindowRouter from './routes/window'; +import ProfilesRouter from './routes/profiles'; const app: Express = express(); let port: number = 49156; // 初始端口 @@ -11,6 +12,15 @@ let port: number = 49156; // 初始端口 app.use(cors()); app.use('/ip', IPRouter); app.use('/window', WindowRouter); +app.use('/profiles', ProfilesRouter); + +app.get('/status', async (req, res) => { + + res.send({ + status: 'ok', + port, + }); +}); const server: Server = app .listen(port, () => { diff --git a/packages/main/src/server/routes/ip.ts b/packages/main/src/server/routes/ip.ts index 430b28b..4a9d811 100644 --- a/packages/main/src/server/routes/ip.ts +++ b/packages/main/src/server/routes/ip.ts @@ -18,7 +18,7 @@ const getIPInfo = async (ip: string, gateway: 'ip2location' | 'geoip') => { } if (gateway === 'ip2location') { const ip2location = new IP2Location(); - const filePath = path.join('assets', 'IP2LOCATION-LITE-DB11.BIN'); + const filePath = path.join(import.meta.env.MODE === 'development' ? 'assets' : 'resources/app/assets', 'IP2LOCATION-LITE-DB11.BIN'); ip2location.open(filePath); const ipInfo = ip2location.getAll(ip); const {latitude, longitude, countryShort} = ipInfo; diff --git a/packages/main/src/server/routes/profiles.ts b/packages/main/src/server/routes/profiles.ts new file mode 100644 index 0000000..0a4070e --- /dev/null +++ b/packages/main/src/server/routes/profiles.ts @@ -0,0 +1,23 @@ +import express from 'express'; +import {WindowDB} from '/@/db/window'; +import { openFingerprintWindow } from '/@/fingerprint'; + +const router = express.Router(); + +router.get('', async (req, res) => { + const windows = await WindowDB.all(); + res.send(windows); +}); + +router.get('/browser', async (req, res) => { + const windowId = req.query.windowId as unknown as number; + const window = await WindowDB.getById(windowId); + const result = await openFingerprintWindow(windowId); + + res.send({ + window, + browser: result, + }); +}); + +export default router; diff --git a/packages/main/src/services/commonService.ts b/packages/main/src/services/commonService.ts index 8efb558..d610a77 100644 --- a/packages/main/src/services/commonService.ts +++ b/packages/main/src/services/commonService.ts @@ -5,6 +5,8 @@ import {join} from 'path'; import {copyFileSync, writeFileSync, readFileSync, readdir} from 'fs'; import type {SettingOptions} from '../../../shared/types/common'; import {getSettings} from '../utils/get-settings'; +import {getOrigin} from '../server'; +import axios from 'axios'; const logger = createLogger(SERVICE_LOGGER_LABEL); @@ -104,4 +106,13 @@ export const initCommonService = () => { return path.filePaths[0]; }, ); + + ipcMain.handle('common-api', async () => { + const apiUrl = getOrigin(); + const res = await axios.get(`${apiUrl}/status`); + return { + url: apiUrl, + ...(res?.data || {}), + }; + }); }; diff --git a/packages/main/src/services/windowService.ts b/packages/main/src/services/windowService.ts index e9ffc1d..f2cfc01 100644 --- a/packages/main/src/services/windowService.ts +++ b/packages/main/src/services/windowService.ts @@ -87,7 +87,7 @@ export const initWindowService = () => { }; export const randomFingerprint = () => { - const uaPath = path.join('assets', 'ua.txt'); + const uaPath = path.join(import.meta.env.MODE === 'development' ? 'assets' : 'resources/app/assets', 'ua.txt'); const uaFile = readFileSync(uaPath, 'utf-8'); const uaList = uaFile.split('\n'); const randomIndex = Math.floor(Math.random() * uaList.length); diff --git a/packages/preload/src/bridges/common.ts b/packages/preload/src/bridges/common.ts index d714d04..ce77e02 100644 --- a/packages/preload/src/bridges/common.ts +++ b/packages/preload/src/bridges/common.ts @@ -26,4 +26,8 @@ export const CommonBridge = { const result = await ipcRenderer.invoke('common-fetch-logs', logModule); return result; }, + async getApi() { + const result = await ipcRenderer.invoke('common-api'); + return result; + }, }; diff --git a/packages/renderer/src/i18n.ts b/packages/renderer/src/i18n.ts index 8f9a0c3..c11cbf0 100644 --- a/packages/renderer/src/i18n.ts +++ b/packages/renderer/src/i18n.ts @@ -42,6 +42,7 @@ i18n window_column_last_open: 'Last open', window_column_created_at: 'Created at', window_column_action: 'Action', + window_edit_form_profile_id: 'Profile ID', window_edit_form_name: 'Name', window_edit_form_remark: 'Remark', window_edit_form_group: 'Group', @@ -89,7 +90,7 @@ i18n settings_cache_path: 'Cache path', settings_choose_cache_path: 'Choose path', settings_use_local_chrome: 'Use Chrome', - settings_chrome_path: 'Chrome Path', + settings_chrome_path: 'Chrome.exe Path', setting_chromium_path: 'Chromium Path', footer_ok: 'OK', @@ -102,6 +103,7 @@ i18n menu_settings: 'Settings', menu_logs: 'Running Logs', menu_sync: 'Sync', + menu_api: 'API', header_language: 'Language', header_settings: 'Settings', @@ -142,6 +144,7 @@ i18n window_column_last_open: '最后打开', window_column_created_at: '创建时间', window_column_action: '操作', + window_edit_form_profile_id: '缓存目录名', window_edit_form_name: '名称', window_edit_form_remark: '备注', window_edit_form_group: '分组', @@ -189,7 +192,7 @@ i18n settings_cache_path: '缓存目录', settings_choose_cache_path: '选择路径', settings_use_local_chrome: '使用本地 Chrome', - settings_chrome_path: 'Chrome 路径', + settings_chrome_path: 'Chrome.exe 路径', setting_chromium_path: 'Chromium 内核路径', footer_ok: '确定', @@ -202,6 +205,7 @@ i18n menu_sync: '同步操作', menu_logs: '运行日志', menu_settings: '设置', + menu_api: 'API', header_settings: '设置', header_language: '语言', diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index ff66c5c..3e440be 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx @@ -7,7 +7,8 @@ import type {ThemeConfig} from 'antd'; import {ConfigProvider, message} from 'antd'; import {HashRouter as Router} from 'react-router-dom'; import 'dayjs/locale/zh-cn'; -import enUS from 'antd/locale/en_US'; +// import enUS from 'antd/locale/en_US'; +import zhCN from 'antd/locale/zh_CN'; import './i18n'; const rootContainer = document.getElementById('app'); @@ -42,7 +43,7 @@ const root = createRoot(rootContainer!); root.render( diff --git a/packages/renderer/src/pages/api/index.css b/packages/renderer/src/pages/api/index.css new file mode 100644 index 0000000..e69de29 diff --git a/packages/renderer/src/pages/api/index.tsx b/packages/renderer/src/pages/api/index.tsx new file mode 100644 index 0000000..917a88e --- /dev/null +++ b/packages/renderer/src/pages/api/index.tsx @@ -0,0 +1,74 @@ +import {Card} from 'antd'; +import React from 'react'; +import './index.css'; +import {CommonBridge} from '#preload'; +import {useEffect, useState} from 'react'; +import {Divider, Typography, Form} from 'antd'; + +const {Title, Paragraph, Text} = Typography; + +const Api = () => { + const [apiInfo, setApiInfo] = useState({ + url: '', + status: 'ok', + port: undefined, + }); + const fetchApi = async () => { + const apiInfo = await CommonBridge.getApi(); + setApiInfo(apiInfo); + console.log(apiInfo); + }; + + useEffect(() => { + fetchApi(); + }, []); + return ( + <> + + API 详情 + + + API 作用于开发人员通过 Puppeteer / Playwright / Selenium{' '} + 等自动化工具连接浏览器实例,执行自动化脚本,非开发人员无需使用此功能。 + + + +
+ + {apiInfo.status} + + + + {apiInfo.url} + + +
+ + + 已支持接口 + + * 获取所有窗口列表 + + GET /profiles + + * 根据 ID 打开指定窗口(返回值中有调试链接) + + GET /profiles/browser?windowId=xxx + +
+ + ); +}; +export default Api; diff --git a/packages/renderer/src/pages/settings/index.tsx b/packages/renderer/src/pages/settings/index.tsx index 91806c4..7461c69 100644 --- a/packages/renderer/src/pages/settings/index.tsx +++ b/packages/renderer/src/pages/settings/index.tsx @@ -27,13 +27,11 @@ const Settings = () => { const fetchSettings = async () => { const settings = await CommonBridge.getSettings(); - console.log(settings); setFormValue(settings); form.setFieldsValue(settings); }; const handleSave = async (values: SettingOptions) => { - console.log(values); await CommonBridge.saveSettings(values); }; diff --git a/packages/renderer/src/pages/windows/components/edit-form/index.tsx b/packages/renderer/src/pages/windows/components/edit-form/index.tsx index bb2dddd..d3032e6 100644 --- a/packages/renderer/src/pages/windows/components/edit-form/index.tsx +++ b/packages/renderer/src/pages/windows/components/edit-form/index.tsx @@ -4,7 +4,7 @@ import {useEffect, useState} from 'react'; import type {DB} from '../../../../../../shared/types/db'; import {GroupBridge, TagBridge, ProxyBridge} from '#preload'; import {TAG_COLORS} from '/@/constants'; -import { useTranslation } from 'react-i18next'; +import {useTranslation} from 'react-i18next'; const {TextArea} = Input; @@ -144,9 +144,7 @@ const WindowEditForm = ({ name="remark" label={t('window_edit_form_remark')} > -