This commit is contained in:
lyswhut
2022-10-29 11:36:35 +08:00
parent 66b965d9f7
commit e45cee6596
715 changed files with 57096 additions and 31771 deletions
-15
View File
@@ -1,15 +0,0 @@
const { EventEmitter } = require('events')
const USER_API_EVENT_NAME = require('./name')
class UserApi extends EventEmitter {
status(info) {
this.emit(USER_API_EVENT_NAME.status, info)
}
showUpdateAlert(info) {
this.emit(USER_API_EVENT_NAME.showUpdateAlert, info)
}
}
module.exports = UserApi
-4
View File
@@ -1,4 +0,0 @@
module.exports = {
status: 'status',
showUpdateAlert: 'showUpdateAlert',
}
-47
View File
@@ -1,47 +0,0 @@
const Event = require('./event/event')
const eventNames = require('./event/name')
const { closeWindow } = require('./main')
const { getUserApis, importApi, removeApi, setAllowShowUpdateAlert: saveAllowShowUpdateAlert } = require('./utils')
const { request, cancelRequest, getStatus, loadApi, setAllowShowUpdateAlert } = require('./rendererEvent/rendererEvent')
// const { getApiList, importApi, removeApi, setApi, getStatus, request, eventNames }
let userApiId
exports.Event = Event
exports.eventNames = eventNames
exports.getApiList = getUserApis
exports.importApi = script => {
return {
apiInfo: importApi(script),
apiList: getUserApis(),
}
}
exports.request = request
exports.cancelRequest = cancelRequest
exports.getStatus = getStatus
exports.removeApi = async ids => {
if (userApiId && ids.includes(userApiId)) {
userApiId = null
await closeWindow()
}
removeApi(ids)
return getUserApis()
}
exports.setApi = async id => {
if (userApiId) {
userApiId = null
await closeWindow()
}
const apiList = getUserApis()
if (!apiList.some(a => a.id === id)) return
userApiId = id
await loadApi(id)
}
exports.setAllowShowUpdateAlert = (id, enable) => {
saveAllowShowUpdateAlert(id, enable)
setAllowShowUpdateAlert(id, enable)
}
+49
View File
@@ -0,0 +1,49 @@
import { closeWindow } from './main'
import { getUserApis, importApi as handleImportApi, removeApi as handleRemoveApi, setAllowShowUpdateAlert as saveAllowShowUpdateAlert } from './utils'
import { loadApi, setAllowShowUpdateAlert as setRendererEventAllowShowUpdateAlert, init } from './rendererEvent/rendererEvent'
let userApiId: string | null
export const getApiList = getUserApis
export const importApi = (script: string): LX.UserApi.ImportUserApi => {
return {
apiInfo: handleImportApi(script),
apiList: getUserApis(),
}
}
export const removeApi = async(ids: string[]): Promise<LX.UserApi.UserApiInfo[]> => {
if (userApiId && ids.includes(userApiId)) {
userApiId = null
await closeWindow()
}
handleRemoveApi(ids)
return getUserApis()
}
export const setApi = async(id: string) => {
if (userApiId) {
userApiId = null
await closeWindow()
}
const apiList = getUserApis()
if (!apiList.some(a => a.id === id)) return
userApiId = id
await loadApi(id)
}
export const setAllowShowUpdateAlert = (id: string, enable: boolean) => {
saveAllowShowUpdateAlert(id, enable)
setRendererEventAllowShowUpdateAlert(id, enable)
}
export * from './rendererEvent/rendererEvent'
export default () => {
init()
global.lx.event_app.on('main_window_close', () => {
void closeWindow()
})
}
-102
View File
@@ -1,102 +0,0 @@
const { BrowserWindow } = require('electron')
const fs = require('fs')
const path = require('path')
// eslint-disable-next-line no-undef
const dir = global.isDev ? __userApi : path.join(__dirname, 'userApi')
const wait = time => new Promise(resolve => setTimeout(() => resolve(), time))
let html = ''
fs.readFile(path.join(dir, 'renderer/user-api.html'), 'utf8', (err, data) => {
if (err) throw new Error('api html read failed, info: ' + err.message)
html = data
})
const denyEvents = [
'will-navigate',
'will-redirect',
'will-attach-webview',
'will-prevent-unload',
'media-started-playing',
]
const winEvent = win => {
win.on('closed', () => {
win = global.modules.userApiWindow = null
})
}
exports.createWindow = async userApi => {
if (global.modules.userApiWindow) return
while (true) {
if (html) break
await wait(100)
}
/**
* Initial window options
*/
global.modules.userApiWindow = new BrowserWindow({
enableRemoteModule: false,
resizable: false,
minimizable: false,
maximizable: false,
fullscreenable: false,
show: false,
webPreferences: {
contextIsolation: true,
// worldSafeExecuteJavaScript: true,
nodeIntegration: false,
nodeIntegrationInWorker: false,
spellcheck: false,
autoplayPolicy: 'document-user-activation-required',
enableWebSQL: false,
disableDialogs: true,
nativeWindowOpen: false,
webgl: false,
images: false,
preload: path.join(dir, 'renderer/preload.js'),
},
})
for (const eventName of denyEvents) {
global.modules.userApiWindow.webContents.on(eventName, event => {
event.preventDefault()
})
}
global.modules.userApiWindow.webContents.session.setPermissionRequestHandler((webContents, permission, resolve) => {
if (webContents === global.modules.mainWindow.webContents) return resolve(true)
resolve(false)
})
global.modules.userApiWindow.webContents.setWindowOpenHandler(() => {
return { action: 'deny' }
})
winEvent(global.modules.userApiWindow)
// console.log(html.replace('</body>', `<script>${userApi.script}</script></body>`))
const randomNum = Math.random().toString().substring(2, 10)
global.modules.userApiWindow.loadURL(
'data:text/html;charset=UTF-8,' + encodeURIComponent(html
.replace('<meta http-equiv="Content-Security-Policy" content="default-src \'none\'">',
`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${randomNum}';">`)
.replace('</body>', `<script nonce="${randomNum}">${userApi.script}</script></body>`)))
// global.modules.userApiWindow.loadFile(path.join(dir, 'renderer/user-api.html'))
// global.modules.userApiWindow.webContents.openDevTools()
}
exports.closeWindow = async() => {
if (!global.modules.userApiWindow) return
await Promise.all([
global.modules.userApiWindow.webContents.session.clearAuthCache(),
global.modules.userApiWindow.webContents.session.clearStorageData(),
global.modules.userApiWindow.webContents.session.clearCache(),
])
global.modules.userApiWindow.destroy()
global.modules.userApiWindow = null
}
+116
View File
@@ -0,0 +1,116 @@
import { mainSend } from '@common/mainIpc'
import { BrowserWindow } from 'electron'
import fs from 'fs'
import { join } from 'path'
import { openDevTools as handleOpenDevTools } from '@main/utils'
import { encodePath, isWin } from '@common/utils'
let browserWindow: Electron.BrowserWindow | null = null
let html: string | null = null
let dir: string | null = null
const denyEvents = [
'will-navigate',
'will-redirect',
'will-attach-webview',
'will-prevent-unload',
'media-started-playing',
] as const
const winEvent = () => {
if (!browserWindow) return
browserWindow.on('closed', () => {
browserWindow = null
})
}
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
await closeWindow()
if (!dir) dir = global.isDev ? webpackUserApiPath : join(encodePath(isWin ? __dirname.replaceAll('\\', '/') : __dirname), 'userApi')
if (!html) {
html = await fs.promises.readFile(join(dir, 'renderer/user-api.html'), 'utf8')
}
const preloadUrl = global.isDev
? `${join(encodePath(isWin ? __dirname.replaceAll('\\', '/') : __dirname), '../dist/user-api-preload.js')}`
: `${join(encodePath(isWin ? __dirname.replaceAll('\\', '/') : __dirname), 'user-api-preload.js')}`
// console.log(preloadUrl)
/**
* Initial window options
*/
browserWindow = new BrowserWindow({
// enableRemoteModule: false,
resizable: false,
minimizable: false,
maximizable: false,
fullscreenable: false,
show: false,
webPreferences: {
contextIsolation: true,
// worldSafeExecuteJavaScript: true,
nodeIntegration: false,
nodeIntegrationInWorker: false,
spellcheck: false,
autoplayPolicy: 'document-user-activation-required',
enableWebSQL: false,
disableDialogs: true,
// nativeWindowOpen: false,
webgl: false,
images: false,
preload: preloadUrl,
},
})
for (const eventName of denyEvents) {
// @ts-expect-error
browserWindow.webContents.on(eventName, (event: Electron.Event) => {
event.preventDefault()
})
}
browserWindow.webContents.session.setPermissionRequestHandler((webContents, permission, resolve) => {
if (webContents === browserWindow?.webContents) return resolve(true)
resolve(false)
})
browserWindow.webContents.setWindowOpenHandler(() => {
return { action: 'deny' }
})
winEvent()
// console.log(html.replace('</body>', `<script>${userApi.script}</script></body>`))
const randomNum = Math.random().toString().substring(2, 10)
await browserWindow.loadURL(
'data:text/html;charset=UTF-8,' + encodeURIComponent(html
.replace('<meta http-equiv="Content-Security-Policy" content="default-src \'none\'">',
`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${randomNum}';">`)
.replace('</body>', `<script nonce="${randomNum}">${userApi.script}</script></body>`)))
// global.modules.userApiWindow.loadFile(join(dir, 'renderer/user-api.html'))
// global.modules.userApiWindow.webContents.openDevTools()
}
export const closeWindow = async() => {
if (!browserWindow) return
await Promise.all([
browserWindow.webContents.session.clearAuthCache(),
browserWindow.webContents.session.clearStorageData(),
browserWindow.webContents.session.clearCache(),
])
browserWindow.destroy()
browserWindow = null
}
export const sendEvent = <T = any>(name: string, params?: T) => {
if (!browserWindow) return
mainSend(browserWindow, name, params)
}
export const openDevTools = () => {
if (!browserWindow) return
handleOpenDevTools(browserWindow.webContents)
}
@@ -1,98 +0,0 @@
const { mainOn, mainSend } = require('@common/ipc')
const USER_API_RENDERER_EVENT_NAME = require('../rendererEvent/name')
const { createWindow } = require('../main')
const { getUserApis } = require('../utils')
const { openDevTools } = require('@main/utils')
let userApi
let status = { status: true }
const requestQueue = new Map()
const timeouts = {}
const handleInit = (event, { status, message, data: apiInfo }) => {
// console.log('inited')
if (!status) {
console.log('init failed:', message)
global.lx_event.userApi.status(status = { status: false, apiInfo: userApi, message })
return
}
global.lx_event.userApi.status(status = { status: true, apiInfo: { ...userApi, sources: apiInfo.sources } })
}
const handleResponse = (event, { status, data: { requestKey, result }, message }) => {
const request = requestQueue.get(requestKey)
if (!request) return
requestQueue.delete(requestKey)
clearTimeout(timeouts[requestKey])
delete timeouts[requestKey]
if (status) {
request[0](result)
} else {
request[1](new Error(message))
}
}
const handleOpenDevTools = () => {
if (global.modules.userApiWindow) {
openDevTools(global.modules.userApiWindow.webContents)
}
}
const handleShowUpdateAlert = (event, { data }) => {
if (!userApi.allowShowUpdateAlert) return
global.lx_event.userApi.showUpdateAlert({
name: userApi.name,
description: userApi.description,
log: data.log,
updateUrl: data.updateUrl,
})
}
mainOn(USER_API_RENDERER_EVENT_NAME.init, handleInit)
mainOn(USER_API_RENDERER_EVENT_NAME.response, handleResponse)
mainOn(USER_API_RENDERER_EVENT_NAME.openDevTools, handleOpenDevTools)
mainOn(USER_API_RENDERER_EVENT_NAME.showUpdateAlert, handleShowUpdateAlert)
exports.loadApi = async apiId => {
if (!apiId) return global.lx_event.userApi.status(status = { status: false, message: 'api id is null' })
userApi = getUserApis().find(api => api.id == apiId)
console.log('load api', userApi.name)
await createWindow(userApi)
// if (!userApi) return global.lx_event.userApi.status(status = { status: false, message: 'api script is not found' })
// if (!global.modules.userApiWindow) {
// global.lx_event.userApi.status(status = { status: false, message: 'user api runtime is not defined' })
// throw new Error('user api window is not defined')
// }
// // const path = require('path')
// // // eslint-disable-next-line no-undef
// // userApi.script = require('fs').readFileSync(path.join(global.isDev ? __userApi : __dirname, 'renderer/test-api.js')).toString()
// console.log('load api', userApi.name)
// mainSend(global.modules.userApiWindow, USER_API_RENDERER_EVENT_NAME.init, { userApi })
}
exports.cancelRequest = requestKey => {
if (!requestQueue.has(requestKey)) return
const request = requestQueue.get(requestKey)
request[1](new Error('Cancel request'))
requestQueue.delete(requestKey)
clearTimeout(timeouts[requestKey])
delete timeouts[requestKey]
}
exports.request = ({ requestKey, data }) => new Promise((resolve, reject) => {
if (!userApi) return reject(new Error('user api is not load'))
// const requestKey = `request__${Math.random().toString().substring(2)}`
timeouts[requestKey] = setTimeout(() => {
exports.cancelRequest(requestKey)
}, 20000)
requestQueue.set(requestKey, [resolve, reject, data])
mainSend(global.modules.userApiWindow, USER_API_RENDERER_EVENT_NAME.request, { requestKey, data })
})
exports.getStatus = () => status
exports.setAllowShowUpdateAlert = (id, enable) => {
if (!userApi || userApi.id != id) return
userApi.allowShowUpdateAlert = enable
}
@@ -0,0 +1,148 @@
import { mainOn } from '@common/mainIpc'
import USER_API_RENDERER_EVENT_NAME from './name'
import { createWindow, openDevTools, sendEvent } from '../main'
import { getUserApis } from '../utils'
import { sendShowUpdateAlert, sendStatusChange } from '@main/modules/winMain'
let userApi: LX.UserApi.UserApiInfo
let apiStatus: LX.UserApi.UserApiStatus = { status: true }
const requestQueue = new Map()
const timeouts: Map<string, NodeJS.Timeout> = new Map()
interface InitParams {
params: {
status: boolean
message: string
data: LX.UserApi.UserApiInfo
}
}
interface ResponseParams {
params: {
status: boolean
message: string
data: {
requestKey: string
result: any
}
}
}
interface UpdateInfoParams {
params: {
data: {
log: string
updateUrl: string
}
}
}
export const init = () => {
const handleInit = ({ params: { status, message, data: apiInfo } }: InitParams) => {
// console.log('inited')
// if (!status) {
// console.log('init failed:', message)
// global.lx_event.userApi.status(status = { status: true, apiInfo: { ...userApi, sources: apiInfo.sources } })
// return
// }
apiStatus = status
? { status: true, apiInfo: { ...userApi, sources: apiInfo.sources } }
: { status: false, apiInfo: userApi, message }
sendStatusChange(apiStatus)
}
const handleResponse = ({ params: { status, data: { requestKey, result }, message } }: ResponseParams) => {
const request = requestQueue.get(requestKey)
if (!request) return
requestQueue.delete(requestKey)
clearRequestTimeout(requestKey)
if (status) {
request[0](result)
} else {
request[1](new Error(message))
}
}
const handleOpenDevTools = () => {
openDevTools()
}
const handleShowUpdateAlert = ({ params: { data } }: UpdateInfoParams) => {
if (!userApi.allowShowUpdateAlert) return
sendShowUpdateAlert({
name: userApi.name,
description: userApi.description,
log: data.log,
updateUrl: data.updateUrl,
})
}
mainOn(USER_API_RENDERER_EVENT_NAME.init, handleInit)
mainOn(USER_API_RENDERER_EVENT_NAME.response, handleResponse)
mainOn(USER_API_RENDERER_EVENT_NAME.openDevTools, handleOpenDevTools)
mainOn(USER_API_RENDERER_EVENT_NAME.showUpdateAlert, handleShowUpdateAlert)
}
export const clearRequestTimeout = (requestKey: string) => {
const timeout = timeouts.get(requestKey)
if (timeout) {
clearTimeout(timeout)
timeouts.delete(requestKey)
}
}
export const loadApi = async(apiId: string) => {
if (!apiId) {
apiStatus = { status: false, message: 'api id is null' }
sendStatusChange(apiStatus)
return
}
const targetApi = getUserApis().find(api => api.id == apiId)
if (!targetApi) throw new Error('api not found')
userApi = targetApi
console.log('load api', userApi.name)
await createWindow(userApi)
// if (!userApi) return global.lx_event.userApi.status(status = { status: false, message: 'api script is not found' })
// if (!global.modules.userApiWindow) {
// global.lx_event.userApi.status(status = { status: false, message: 'user api runtime is not defined' })
// throw new Error('user api window is not defined')
// }
// // const path = require('path')
// // // eslint-disable-next-line no-undef
// // userApi.script = require('fs').readFileSync(join(global.isDev ? __userApi : __dirname, 'renderer/test-api.js')).toString()
// console.log('load api', userApi.name)
// mainSend(global.modules.userApiWindow, USER_API_RENDERER_EVENT_NAME.init, { userApi })
}
export const cancelRequest = (requestKey: string) => {
if (!requestQueue.has(requestKey)) return
const request = requestQueue.get(requestKey)
request[1](new Error('Cancel request'))
requestQueue.delete(requestKey)
clearRequestTimeout(requestKey)
}
export const request = async({ requestKey, data }: LX.UserApi.UserApiRequestParams): Promise<any> => await new Promise((resolve, reject) => {
if (!userApi) return reject(new Error('user api is not load'))
// const requestKey = `request__${Math.random().toString().substring(2)}`
const timeout = timeouts.get(requestKey)
if (timeout) {
clearTimeout(timeout)
timeouts.delete(requestKey)
cancelRequest(requestKey)
}
timeouts.set(requestKey, setTimeout(() => {
cancelRequest(requestKey)
}, 20000))
requestQueue.set(requestKey, [resolve, reject, data])
sendRequest({ requestKey, data })
})
export const getStatus = (): LX.UserApi.UserApiStatus => apiStatus
export const setAllowShowUpdateAlert = (id: string, enable: boolean) => {
if (!userApi || userApi.id != id) return
userApi.allowShowUpdateAlert = enable
}
export const sendRequest = (reqData: { requestKey: string, data: any }) => {
sendEvent(USER_API_RENDERER_EVENT_NAME.request, reqData)
}
@@ -1,12 +1,13 @@
const { userApis: defaultUserApis } = require('../config')
const getStore = require('@common/store')
import { userApis as defaultUserApis } from './config'
import { STORE_NAMES } from '@common/constants'
import getStore from '@main/utils/store'
let userApis
let userApis: LX.UserApi.UserApiInfo[] | null
exports.getUserApis = () => {
const electronStore_userApi = getStore('userApi')
export const getUserApis = (): LX.UserApi.UserApiInfo[] => {
const electronStore_userApi = getStore(STORE_NAMES.USER_API)
if (userApis) return userApis
userApis = electronStore_userApi.get('userApis')
userApis = electronStore_userApi.get('userApis') as LX.UserApi.UserApiInfo[]
if (!userApis) {
userApis = defaultUserApis
electronStore_userApi.set('userApis', userApis)
@@ -17,7 +18,7 @@ exports.getUserApis = () => {
return userApis
}
exports.importApi = script => {
export const importApi = (script: string): LX.UserApi.UserApiInfo => {
let scriptInfo = script.split(/\r?\n/)
let name = scriptInfo[1] || ''
let description = scriptInfo[2] || ''
@@ -32,24 +33,26 @@ exports.importApi = script => {
script,
allowShowUpdateAlert: true,
}
if (!userApis) userApis = []
userApis.push(apiInfo)
getStore('userApi').set('userApis', userApis)
getStore(STORE_NAMES.USER_API).set('userApis', userApis)
return apiInfo
}
exports.removeApi = ids => {
export const removeApi = (ids: string[]) => {
if (!userApis) return
for (let index = userApis.length - 1; index > -1; index--) {
if (ids.includes(userApis[index].id)) {
userApis.splice(index, 1)
ids.splice(index, 1)
}
}
getStore('userApi').set('userApis', userApis)
getStore(STORE_NAMES.USER_API).set('userApis', userApis)
}
exports.setAllowShowUpdateAlert = (id, enable) => {
const targetApi = userApis.find(api => api.id == id)
export const setAllowShowUpdateAlert = (id: string, enable: boolean) => {
const targetApi = userApis?.find(api => api.id == id)
if (!targetApi) return
targetApi.allowShowUpdateAlert = enable
getStore('userApi').set('userApis', userApis)
getStore(STORE_NAMES.USER_API).set('userApis', userApis)
}