|
|
import crypto from 'crypto'
|
|
|
import http from './express.js'
|
|
|
import { Config, puppeteer, logger } from '#karin'
|
|
|
import VueCache from './VueFileCache.js'
|
|
|
|
|
|
const port = Config.Config.http.port
|
|
|
|
|
|
export default class Server {
|
|
|
constructor (socket, request) {
|
|
|
this.id = 0
|
|
|
this.socket = socket
|
|
|
this.request = request
|
|
|
this.host = this.getHost()
|
|
|
this.vueCache = VueCache
|
|
|
}
|
|
|
|
|
|
init () {
|
|
|
this.socket.on('message', data => this.message(data))
|
|
|
this.socket.on('close', () => {
|
|
|
logger.error(`[Server][连接关闭]:${this.request.url}`)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
getHost () {
|
|
|
const isSecure = this.request.connection.encrypted
|
|
|
const protocol = isSecure ? 'wss' : 'ws'
|
|
|
const host = this.request.headers.host
|
|
|
const path = this.request.url
|
|
|
return `${protocol}://${host}${path}`
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async message (data) {
|
|
|
data = JSON.parse(data)
|
|
|
switch (data.action) {
|
|
|
case 'heartbeat':
|
|
|
return logger.debug(`[Server][心跳]:${this.request.url}`)
|
|
|
case 'render': {
|
|
|
|
|
|
data.data.file = decodeURIComponent(data.data.file)
|
|
|
logger.debug(`[正向WS][渲染] URL: ${this.host} html:${data.data.file}`)
|
|
|
let res
|
|
|
if (data.data.vue && Config.Config.server.http) {
|
|
|
const cacheId = this.vueCache.addCache(data.data.file, data.data.name, data.data.props)
|
|
|
data.data.file = `http://localhost:${Config.Config.http.port}/vue/${data.data.vueTemplate || 'default'}/?id=${cacheId}`
|
|
|
res = await puppeteer.screenshot(data.data)
|
|
|
this.vueCache.deleteCache(cacheId)
|
|
|
} else {
|
|
|
res = await puppeteer.screenshot(data.data)
|
|
|
}
|
|
|
res.echo = data.echo
|
|
|
res.action = 'renderRes'
|
|
|
this.socket.send(JSON.stringify(res))
|
|
|
break
|
|
|
}
|
|
|
case 'renderHtml': {
|
|
|
|
|
|
const file = decodeURIComponent(data.data.file)
|
|
|
logger.debug(`[正向WS][渲染] URL: ${this.host} html:${file}`)
|
|
|
|
|
|
const hash = `render-${this.id}-${crypto.randomUUID()}`
|
|
|
const host = `http://localhost:${port}/api/render?hash=${hash}`
|
|
|
http.file.set(hash, { ws: this, file })
|
|
|
|
|
|
data.data.file = host
|
|
|
|
|
|
data.data.hash = hash
|
|
|
const res = await puppeteer.screenshot(data.data)
|
|
|
res.echo = data.echo
|
|
|
res.action = 'renderRes'
|
|
|
this.socket.send(JSON.stringify(res))
|
|
|
break
|
|
|
}
|
|
|
case 'static': {
|
|
|
|
|
|
this.socket.emit(data.echo, data)
|
|
|
break
|
|
|
}
|
|
|
default:
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async SendApi (action, params, time = 120) {
|
|
|
const echo = crypto.randomUUID()
|
|
|
const request = JSON.stringify({ echo, action, params })
|
|
|
return new Promise((resolve, reject) => {
|
|
|
this.socket.send(request)
|
|
|
this.socket.once(echo, (data) => {
|
|
|
if (data.status === 'ok') {
|
|
|
resolve(data.data)
|
|
|
} else {
|
|
|
logger.error(`[Api请求错误] ${JSON.stringify(data, null, 2)}`)
|
|
|
reject(data)
|
|
|
}
|
|
|
})
|
|
|
|
|
|
setTimeout(() => {
|
|
|
reject(new Error('API请求超时'))
|
|
|
}, time * 1000)
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|