This commit is contained in:
2025-10-10 10:08:05 +08:00
parent ed0527bf27
commit 204dd4781b
6 changed files with 235 additions and 816 deletions

View File

@@ -109,10 +109,7 @@ export class ApplicationSandboxEngine {
private resourceService: ResourceService
private eventService: EventCommunicationService
constructor(
resourceService: ResourceService,
eventService: EventCommunicationService
) {
constructor(resourceService: ResourceService, eventService: EventCommunicationService) {
this.resourceService = resourceService
this.eventService = eventService
this.startPerformanceMonitoring()
@@ -127,7 +124,7 @@ export class ApplicationSandboxEngine {
config: Partial<SandboxConfig> = {}
): Promise<SandboxInstance> {
const sandboxId = uuidv4()
const defaultConfig: SandboxConfig = {
type: SandboxType.IFRAME,
securityLevel: SecurityLevel.HIGH,
@@ -166,21 +163,20 @@ export class ApplicationSandboxEngine {
try {
// 根据类型创建沙箱容器
await this.createSandboxContainer(sandbox)
// 设置安全策略
this.applySecurity(sandbox)
// 设置性能监控
this.setupPerformanceMonitoring(sandbox)
// 更新状态
this.updateSandboxState(sandbox, SandboxState.READY)
this.sandboxes.set(sandboxId, sandbox)
console.log(`沙箱 ${sandboxId} 创建成功`)
return sandbox
} catch (error) {
this.updateSandboxState(sandbox, SandboxState.ERROR)
sandbox.errors.push(error instanceof Error ? error.message : String(error))
@@ -200,23 +196,25 @@ export class ApplicationSandboxEngine {
try {
console.log(`开始加载应用: ${sandbox.appId}, 沙箱ID: ${sandboxId}`)
this.updateSandboxState(sandbox, SandboxState.RUNNING)
if (sandbox.config.type === SandboxType.IFRAME && sandbox.iframe) {
console.log(`使用iframe加载应用: ${applicationUrl}`)
// 检查iframe是否已挂载到DOM
if (!document.contains(sandbox.iframe)) {
console.warn('检测到iframe未挂载到DOM尝试重新挂载')
await this.mountIframeToWindow(sandbox)
}
// 设置iframe源
sandbox.iframe.src = applicationUrl
// 等待加载完成
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
console.error(`应用加载超时: ${sandbox.appId}, URL: ${applicationUrl}, 超时时间: ${sandbox.config.networkTimeout}ms`)
console.error(
`应用加载超时: ${sandbox.appId}, URL: ${applicationUrl}, 超时时间: ${sandbox.config.networkTimeout}ms`
)
reject(new Error('应用加载超时'))
}, sandbox.config.networkTimeout)
@@ -241,7 +239,7 @@ export class ApplicationSandboxEngine {
})
return true
}
return false
} catch (error) {
console.error(`加载应用失败: ${sandbox.appId}`, error)
@@ -251,64 +249,6 @@ export class ApplicationSandboxEngine {
}
}
/**
* 暂停沙箱
*/
suspendSandbox(sandboxId: string): boolean {
const sandbox = this.sandboxes.get(sandboxId)
if (!sandbox || sandbox.state !== SandboxState.RUNNING) {
return false
}
try {
this.updateSandboxState(sandbox, SandboxState.SUSPENDED)
if (sandbox.iframe) {
// 暂停iframe中的脚本执行
const iframeWindow = sandbox.iframe.contentWindow
if (iframeWindow) {
// 发送暂停事件到应用
iframeWindow.postMessage({ type: 'system:suspend' }, '*')
}
}
console.log(`沙箱 ${sandboxId} 已暂停`)
return true
} catch (error) {
console.error('暂停沙箱失败:', error)
return false
}
}
/**
* 恢复沙箱
*/
resumeSandbox(sandboxId: string): boolean {
const sandbox = this.sandboxes.get(sandboxId)
if (!sandbox || sandbox.state !== SandboxState.SUSPENDED) {
return false
}
try {
this.updateSandboxState(sandbox, SandboxState.RUNNING)
sandbox.lastActiveAt = new Date()
if (sandbox.iframe) {
const iframeWindow = sandbox.iframe.contentWindow
if (iframeWindow) {
// 发送恢复事件到应用
iframeWindow.postMessage({ type: 'system:resume' }, '*')
}
}
console.log(`沙箱 ${sandboxId} 已恢复`)
return true
} catch (error) {
console.error('恢复沙箱失败:', error)
return false
}
}
/**
* 销毁沙箱
*/
@@ -320,18 +260,18 @@ export class ApplicationSandboxEngine {
try {
this.updateSandboxState(sandbox, SandboxState.DESTROYED)
// 移除消息事件监听器
if (sandbox.messageHandler) {
window.removeEventListener('message', sandbox.messageHandler);
sandbox.messageHandler = undefined;
window.removeEventListener('message', sandbox.messageHandler)
sandbox.messageHandler = undefined
}
// 清理DOM元素
if (sandbox.iframe) {
sandbox.iframe.remove()
}
if (sandbox.container) {
sandbox.container.remove()
}
@@ -343,10 +283,10 @@ export class ApplicationSandboxEngine {
// 清理性能数据
this.performanceData.delete(sandboxId)
// 从集合中移除
this.sandboxes.delete(sandboxId)
console.log(`沙箱 ${sandboxId} 已销毁`)
return true
} catch (error) {
@@ -366,7 +306,7 @@ export class ApplicationSandboxEngine {
* 获取应用的所有沙箱
*/
getAppSandboxes(appId: string): SandboxInstance[] {
return Array.from(this.sandboxes.values()).filter(sandbox => sandbox.appId === appId)
return Array.from(this.sandboxes.values()).filter((sandbox) => sandbox.appId === appId)
}
/**
@@ -396,7 +336,7 @@ export class ApplicationSandboxEngine {
sandbox.worker.postMessage(message)
return true
}
return false
} catch (error) {
console.error('发送消息到沙箱失败:', error)
@@ -421,7 +361,7 @@ export class ApplicationSandboxEngine {
clearInterval(this.monitoringInterval)
this.monitoringInterval = null
}
this.cleanup()
console.log('沙箱引擎已销毁')
}
@@ -453,10 +393,10 @@ export class ApplicationSandboxEngine {
private async createIframeSandbox(sandbox: SandboxInstance): Promise<void> {
const iframe = document.createElement('iframe')
iframe.id = `sandbox-${sandbox.id}`
// 设置sandbox属性
const sandboxAttributes: string[] = []
if (sandbox.config.allowScripts) sandboxAttributes.push('allow-scripts')
if (sandbox.config.allowSameOrigin) sandboxAttributes.push('allow-same-origin')
if (sandbox.config.allowForms) sandboxAttributes.push('allow-forms')
@@ -465,9 +405,9 @@ export class ApplicationSandboxEngine {
if (sandbox.config.allowPointerLock) sandboxAttributes.push('allow-pointer-lock')
if (sandbox.config.allowPresentation) sandboxAttributes.push('allow-presentation')
if (sandbox.config.allowTopNavigation) sandboxAttributes.push('allow-top-navigation')
iframe.sandbox = sandboxAttributes.join(' ')
// 设置样式
iframe.style.cssText = `
width: 100%;
@@ -483,11 +423,11 @@ export class ApplicationSandboxEngine {
sandbox.iframe = iframe
sandbox.container = iframe
// 将iframe挂载到对应的窗口内容区域
await this.mountIframeToWindow(sandbox)
}
/**
* 将iframe挂载到窗口内容区域
*/
@@ -512,18 +452,18 @@ export class ApplicationSandboxEngine {
}
throw new Error(`找不到窗口元素: window-${sandbox.windowId}`)
}
const contentArea = windowElement.querySelector('.window-content')
if (!contentArea) {
throw new Error(`找不到窗口内容区域: window-${sandbox.windowId}`)
}
// 替换窗口中的默认iframe
const existingIframe = contentArea.querySelector('iframe')
if (existingIframe) {
contentArea.removeChild(existingIframe)
}
contentArea.appendChild(sandbox.iframe!)
}
@@ -567,9 +507,9 @@ export class ApplicationSandboxEngine {
const blob = new Blob([workerScript], { type: 'application/javascript' })
const workerUrl = URL.createObjectURL(blob)
const worker = new Worker(workerUrl)
worker.onmessage = (e) => {
const { type, error } = e.data
if (type === 'error') {
@@ -593,9 +533,9 @@ export class ApplicationSandboxEngine {
private async createShadowDOMSandbox(sandbox: SandboxInstance): Promise<void> {
const container = document.createElement('div')
container.id = `sandbox-${sandbox.id}`
const shadowRoot = container.attachShadow({ mode: 'closed' })
// 添加样式隔离
const style = document.createElement('style')
style.textContent = `
@@ -626,16 +566,16 @@ export class ApplicationSandboxEngine {
}
// 这些头部主要用于服务器端设置,在客户端我们通过其他方式实现
// 监听iframe的消息事件
const messageHandler = (event: MessageEvent) => {
if (event.source === sandbox.iframe!.contentWindow) {
this.handleSandboxMessage(sandbox, event.data)
}
};
window.addEventListener('message', messageHandler);
}
window.addEventListener('message', messageHandler)
// 存储事件监听器引用,以便在销毁时移除
sandbox.messageHandler = messageHandler;
sandbox.messageHandler = messageHandler
// 简化安全限制主要依赖iframe的sandbox属性
sandbox.iframe.addEventListener('load', () => {
@@ -675,15 +615,15 @@ export class ApplicationSandboxEngine {
case 'app:ready':
sandbox.lastActiveAt = new Date()
break
case 'app:error':
sandbox.errors.push(message.error || '未知错误')
break
case 'app:resource-request':
this.handleResourceRequest(sandbox, message.data)
break
case 'app:performance':
this.recordPerformance(sandbox, message.data)
break
@@ -695,10 +635,10 @@ export class ApplicationSandboxEngine {
*/
private async handleResourceRequest(sandbox: SandboxInstance, request: any): Promise<void> {
const { type, data } = request
try {
let result = null
switch (type) {
case 'storage':
if (data.action === 'get') {
@@ -707,12 +647,16 @@ export class ApplicationSandboxEngine {
result = await this.resourceService.setStorage(sandbox.appId, data.key, data.value)
}
break
case 'network':
result = await this.resourceService.makeNetworkRequest(sandbox.appId, data.url, data.options)
result = await this.resourceService.makeNetworkRequest(
sandbox.appId,
data.url,
data.options
)
break
}
// 发送结果回沙箱
this.sendMessage(sandbox.id, {
type: 'system:resource-response',
@@ -1171,10 +1115,10 @@ export class ApplicationSandboxEngine {
// 通知系统应用已准备就绪
window.parent.postMessage({ type: 'app:ready' }, '*');
})();
`;
iframeDoc.head.appendChild(script)
}
`
iframeDoc.head.appendChild(script)
}
/**
* 设置性能监控
@@ -1260,9 +1204,12 @@ export class ApplicationSandboxEngine {
private collectPerformanceMetrics(sandbox: SandboxInstance): void {
if (sandbox.iframe && sandbox.iframe.contentWindow) {
// 请求性能数据
sandbox.iframe.contentWindow.postMessage({
type: 'system:performance-request'
}, '*')
sandbox.iframe.contentWindow.postMessage(
{
type: 'system:performance-request'
},
'*'
)
}
}
@@ -1272,7 +1219,7 @@ export class ApplicationSandboxEngine {
private updateSandboxState(sandbox: SandboxInstance, newState: SandboxState): void {
const oldState = sandbox.state
sandbox.state = newState
// 触发状态变化事件
this.eventService.sendMessage('system', 'sandbox-state-change', {
sandboxId: sandbox.id,
@@ -1280,4 +1227,4 @@ export class ApplicationSandboxEngine {
oldState
})
}
}
}