Initial commit

This commit is contained in:
2025-06-26 11:28:55 +08:00
commit e11c59cdc2
167 changed files with 6029 additions and 0 deletions

612
src/main/window.js Normal file
View File

@@ -0,0 +1,612 @@
import { BrowserWindow, screen, Menu, shell, session, app, globalShortcut, dialog ,systemPreferences } from 'electron'
import { join } from 'path'
import { is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import { isApiDatasetsDocumentsPath, extractIdFromPath } from './utils/difyUtils.js'
import XEUtils from 'xe-utils'
import logger from './utils/logger'
import { createMenu } from './menu.js'
import { setStoreValue, getStoreValue,deleteStore } from './store.js'
import {checkForUpdates} from "./utils/updateUtils"
import dayjs from 'dayjs'
let mainWindow = null
let difyfullScreenWindow = null
let drageWindow = null
let apiConfigWindow = null
let configWindow = null
// 权限授权
async function checkMediaAccess(mediaType){
const result = systemPreferences.getMediaAccessStatus(mediaType)
logger.info(`=====================systemPreferences.getMediaAccessStatus:${mediaType} result:${result}`)
if(result !== "granted"){
await systemPreferences.askForMediaAccess(mediaType)
}
}
async function checkAndApplyDeviceAccessPrivilege() {
if (process.platform === "darwin" || process.platform === 'win32') {
// 检查并申请摄像头权限
const cameraPrivilege = systemPreferences.getMediaAccessStatus("camera");
if (cameraPrivilege !== "granted") {
await systemPreferences.askForMediaAccess("camera");
}
// 检查并申请麦克风权限
const micPrivilege = systemPreferences.getMediaAccessStatus("microphone");
if (micPrivilege !== "granted") {
await systemPreferences.askForMediaAccess("microphone");
}
// 检查访问屏幕权限
const screenPrivilege = systemPreferences.getMediaAccessStatus("screen");
if (screenPrivilege !== 'granted') {
// 没有屏幕访问权限,做后续处理...
}
}
}
export async function createWindow() {
// 如果窗口已经存在,直接显示并返回
if (mainWindow) {
mainWindow.show()
return
}
logger.info(`启动主窗口`)
Menu.setApplicationMenu(null)
// Create the browser window.
mainWindow = new BrowserWindow({
width: 420,
height: 900,
show: false,
media: {
audio: true,
video: true
},
icon: icon,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true,
preload: join(__dirname, '../preload/index.js'),
// sandbox: false
}
})
// 创建菜单
createMenu(mainWindow, difyfullScreenWindow)
let code = `localStorage.setItem("IsHsAiApp","IsHsAiApp");localStorage.setItem("HsAppCode",${import.meta.env.VITE_HsAppCode});`
const isAdmin = getStoreValue("isAdmin")
let display='';
await checkAndApplyDeviceAccessPrivilege()
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.webContents
.executeJavaScript(code)
.then((result) => {
})
.catch((error) => {
console.error('Error:', error)
})
})
// 监听快捷键 F12 来打开/关闭 DevTools
mainWindow.webContents.on('before-input-event', (event, input) => {
if (input.key === 'F12') {
mainWindow.webContents.toggleDevTools()
}
})
mainWindow.on('resize',async () => {
// logger.info('窗口大小发生变化...');
// 触发窗口内容重新布局,而不是重新加载
mainWindow.webContents.executeJavaScript(`
if (document.body) {
// 触发窗口重排
window.dispatchEvent(new Event('resize'));
}
`).catch(err => {
console.error('执行布局调整时出错:', err);
});
});
mainWindow.on('ready-to-show', () => {
mainWindow.show()
})
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
mainWindow.on('close', (event) => {
if (!app.isQuiting) {
event.preventDefault()
mainWindow.hide()
return false
}
})
// setStoreValue("h5_client_url", "")
// 检查本地存储中是否已有 h5_client_url
const existingH5ClientUrl = getStoreValue("h5_client_url")
if (!existingH5ClientUrl || existingH5ClientUrl.trim() === '') {
// 只有当值不存在或为空时,才设置默认值
// setStoreValue("h5_client_url", "http://work.ii999.live:20040")
// setStoreValue("h5_client_url", "http://10.102.8.56:18900")
// setStoreValue("h5_client_url", "http://68.66.24.160:18900")
setStoreValue("h5_client_url", import.meta.env.VITE_h5_client_url)
}
const h5_client_url=getStoreValue("h5_client_url")+"/h5_client/"
logger.info("==================================== mainWindow.loadURL:"+h5_client_url)
// 加载存储的 URL
mainWindow.loadURL(h5_client_url)
// 超过30分钟不活动则退出登录
await tokenExpireTimer()
setTimeout(()=>{
try {
// 注册全局快捷键
registerShortcuts(mainWindow)
checkForUpdates({},false)
}catch (e) {
logger.info(e)
}
},1500)
session.defaultSession.setPermissionRequestHandler((webContents, permission, callback) => {
if (permission === 'microphone') {
// 用户是否允许使用麦克风
callback(true); // 或者根据实际情况返回false
} else {
// 对于其他权限,拒绝或根据实际情况处理
callback(false);
}
});
}
// 添加一个专门的快捷键注册函数
function registerShortcuts(window) {
// 注册 F5 刷新快捷键
globalShortcut.register('F5', () => {
logger.info('F5 快捷键触发')
if (window && !window.isDestroyed()) {
window.reload()
}
})
const isRegistered_F12 = globalShortcut.isRegistered('F12');
logger.info(`Is CommandOrControl+X registered: ${isRegistered_F12}`);
// 桌面端快要退出的时候,注销快捷键
app.on('will-quit', () => {
globalShortcut.unregisterAll() // 注销所有快捷键
})
}
export async function createNewWindow(url, access_token, refresh_token,sandbox=false) {
const origin_url = url
logger.info('==========createNewWindow')
logger.info('==========createNewWindow sandbox',sandbox)
await checkAndApplyDeviceAccessPrivilege()
difyfullScreenWindow = new BrowserWindow({
width: 1920,
height: 1200,
show: false,
icon: icon,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true,
preload: join(__dirname, '../preload/index.js'),
sandbox: sandbox,
webSecurity: false, // 注意:在生产环境中应谨慎使用
enableRemoteModule: true, // Electron 10+ 默认禁用,需要显式启用
}
})
difyfullScreenWindow.on('ready-to-show', () => {
difyfullScreenWindow.show()
})
let code = `localStorage.setItem("IsHsAiApp","IsHsAiApp");localStorage.setItem("console_token","${access_token}");localStorage.setItem("refresh_token","12");`
const isAdmin = getStoreValue("isAdmin")
const hs_version = getStoreValue("hs_version")
let display='';
if (isAdmin==true){
display = `
(function() {
const stickyDivs = document.querySelectorAll('.sticky.top-0.left-0.right-0.basis-auto.shrink-0');
stickyDivs.forEach(div => {
// div.style.display = 'none';
});
})();
`
}else {
display = `
(function() {
const stickyDivs = document.querySelectorAll('.sticky.top-0.left-0.right-0.basis-auto.shrink-0');
stickyDivs.forEach(div => {
div.style.display = 'none';
});
})();
`
}
code=code+`document.cookie="hs_version=${hs_version}";`
difyfullScreenWindow.webContents.on('did-finish-load', () => {
difyfullScreenWindow.webContents
.executeJavaScript(code + display)
.then((result) => {
})
.catch((error) => {
console.error('Error:', error)
})
let times = 0
let timer = setInterval(() => {
if (times > 3) {
clearInterval(timer)
timer = null
times = 0
return
}
if (difyfullScreenWindow != null) {
difyfullScreenWindow.webContents
.executeJavaScript(code + display)
.then((result) => {
times = times + 1
})
.catch((error) => {
console.error('Error:', error)
})
}
}, 2000)
})
// 监听快捷键 F12 来打开/关闭 DevTools
difyfullScreenWindow.webContents.on('before-input-event', (event, input) => {
if (input.key === 'F12') {
difyfullScreenWindow.webContents.toggleDevTools()
}
})
difyfullScreenWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
difyfullScreenWindow.on('closed', () => {
logger.info('主窗口已关闭')
difyfullScreenWindow = null
})
difyfullScreenWindow.webContents.on('did-navigate', (event, url) => {
logger.info('navigate to new URL:', url)
})
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Access-Control-Allow-Origin': ['*'],
'Access-Control-Allow-Methods': ['GET, POST, PUT, DELETE, OPTIONS'],
'Access-Control-Allow-Headers': ['Content-Type, Authorization']
}
})
})
let times = 0
difyfullScreenWindow.webContents.on('did-navigate-in-page', (event, url) => {
logger.info('navigate URL change:', url)
logger.info(`===============current:${access_token}`)
if (url.includes('/signin')) {
logger.info("token 注入失效,重新注入并且加载页面")
if (times>3){
difyfullScreenWindow.close()
difyfullScreenWindow=null
times=0;
return
}
let _display = `
(function() {
const stickyDivs = document.querySelectorAll('body');
stickyDivs.forEach(div => {
div.style.display = 'none';
});
})();
`
difyfullScreenWindow.webContents
.executeJavaScript(code + _display)
.then((result) => {
logger.info('load url:', origin_url)
setTimeout(()=>{
times=times+1;
difyfullScreenWindow.loadURL(origin_url)
},1000)
})
.catch((error) => {
console.error('Error:', error)
})
}
})
let dify_site = 'http://df.1024web.cn'
const filter = { urls: [`${dify_site}/*`] }
session.defaultSession.webRequest.onBeforeRequest(filter, (details, callback) => {
if (isApiDatasetsDocumentsPath(details.url)) {
logger.info('========== onBeforeRequest url:', details.url)
logger.info('============datasetId :', extractIdFromPath(details.url))
logger.info(details)
const url = new URL(details.url)
const pathAndQuery = url.pathname + url.search
const redirectURL = `http://192.168.50.22:8001/items/`
logger.info(`[basehttp:setProxy] redirectURL = ${redirectURL}`)
callback({ redirectURL })
} else {
callback({ cancel: false })
}
})
difyfullScreenWindow.on('resize', () => {
logger.info('窗口大小发生变化,重新加载页面...');
if (difyfullScreenWindow!=null){
difyfullScreenWindow.webContents.executeJavaScript(`
if (document.body) {
// 触发窗口重排
window.dispatchEvent(new Event('resize'));
}
`).catch(err => {
console.error('执行布局调整时出错:', err);
});
}
// 触发窗口内容重新布局,而不是重新加载
});
difyfullScreenWindow.loadURL(url)
}
export function createDrageWindow() {
logger.info('开始创建悬浮窗口')
if (drageWindow) {
drageWindow.focus()
return
}
drageWindow = new BrowserWindow({
width: 120,
height: 120,
frame: false,
show: true,
skipTaskbar: true,
transparent: true,
resizable: false,
alwaysOnTop: true,
autoHideMenuBar: true,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true,
preload: join(__dirname, '../preload/index.js')
}
})
drageWindow.setAlwaysOnTop(true, 'screen-saver')
drageWindow.on('ready-to-show', () => {
drageWindow.show()
})
drageWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
drageWindow.on('close', (event) => {
if (!app.isQuiting) {
event.preventDefault()
drageWindow.hide()
return false
}
})
const h5_client_url=getStoreValue("h5_client_url")+"/electron_h5/"
logger.log("======================== drageWindow :")
logger.log(h5_client_url)
drageWindow.loadURL(h5_client_url)
const { width: screenWidth, height: screenHeight } = screen.getPrimaryDisplay().workAreaSize
drageWindow.setPosition(screenWidth - drageWindow.getSize()[0] -100, screenHeight - drageWindow.getSize()[1] - 100)
}
export async function tokenExpireTimer(){
const tokenExpireTimer = setInterval(async () => {
logger.info("tokenExpireTimer 触发")
const lastActiveTime = getStoreValue("lastActiveTime")||null
if (lastActiveTime!=null){
logger.info("tokenExpireTimer 触发 对比时间戳")
try {
const nowTime=dayjs();
const lastTime=dayjs(lastActiveTime);
const diff=nowTime.diff(lastTime, 'minute')
logger.info("tokenExpireTimer 触发 对比时间戳差距为:"+diff)
if ( diff> 30) {
deleteStore("lastActiveTime")
try {
// 清除所有窗口的浏览器缓存
const windows = BrowserWindow.getAllWindows()
for (const window of windows) {
const session = window.webContents.session
await session.clearStorageData({
storages: [
'appcache',
'cookies',
'filesystem',
'indexdb',
'localstorage',
'shadercache',
'websql',
'serviceworkers',
'cachestorage'
]
})
}
logger.info('浏览器缓存清除成功')
} catch (error) {
logger.error('清除浏览器缓存失败:', error)
}
if (mainWindow) {
mainWindow.reload()
}
logger.info('用户已退出登录')
}
} catch (e) {
}
}
}, 1000 * 10)
}
export function getMainWindow() {
if (!mainWindow || mainWindow.isDestroyed()) {
return null
}
return mainWindow
}
export function getDragWindow() {
if (!drageWindow || drageWindow.isDestroyed()) {
return null
}
return drageWindow
}
export function getDifyFullScreenWindow() {
return difyfullScreenWindow;
}
export function getDrageWindow() {
return drageWindow;
}
// 在应用退出时注销所有快捷键
export function unregisterAllShortcuts() {
globalShortcut.unregisterAll()
}
export function closeApiConfigWindow() {
if (apiConfigWindow) {
apiConfigWindow.close()
apiConfigWindow = null
}
}
export function createConfigWindow() {
if (configWindow) {
configWindow.focus()
return
}
configWindow = new BrowserWindow({
width: 600,
height: 400,
resizable: false,
minimizable: false,
maximizable: false,
parent: getMainWindow(),
modal: true,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true,
preload: join(__dirname, '../preload/index.js')
},
permissions: {
microphone: 'allow'
}
})
const h5_client_url=getStoreValue("h5_client_url")+"/electron_h5/#/config"
logger.info("======================== configWindow 11111111111:")
logger.info(h5_client_url)
// configWindow.loadURL(h5_client_url)
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
configWindow.loadURL(process.env['ELECTRON_RENDERER_URL'] + '/#/config')
} else {
configWindow.loadFile(join(__dirname, '../renderer/index.html'), { hash: '/config' })
}
//
configWindow.on('closed', () => {
configWindow = null
})
}
// 添加关闭配置窗口的方法
export function closeConfigWindow() {
if (configWindow) {
configWindow.close()
configWindow = null
}
}