Initial commit
This commit is contained in:
612
src/main/window.js
Normal file
612
src/main/window.js
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user