mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
Add WebAPI handler for extensions
- Rename http.ts to web.ts handler - Implement handleWebMethodAsync with haextension.web.fetch support - Add base64 body encoding/decoding - Add timeout support with AbortController - Convert response headers and body to proper format - Update message handler to route haextension.web.* methods - Add TODO for permission checks This enables extensions to make web requests through the host app, bypassing iframe CORS restrictions.
This commit is contained in:
@ -7,7 +7,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
handleDatabaseMethodAsync,
|
handleDatabaseMethodAsync,
|
||||||
handleFilesystemMethodAsync,
|
handleFilesystemMethodAsync,
|
||||||
handleHttpMethodAsync,
|
handleWebMethodAsync,
|
||||||
handlePermissionsMethodAsync,
|
handlePermissionsMethodAsync,
|
||||||
handleContextMethodAsync,
|
handleContextMethodAsync,
|
||||||
handleStorageMethodAsync,
|
handleStorageMethodAsync,
|
||||||
@ -165,8 +165,8 @@ const registerGlobalMessageHandler = () => {
|
|||||||
result = await handleDatabaseMethodAsync(request, instance.extension)
|
result = await handleDatabaseMethodAsync(request, instance.extension)
|
||||||
} else if (request.method.startsWith('haextension.fs.')) {
|
} else if (request.method.startsWith('haextension.fs.')) {
|
||||||
result = await handleFilesystemMethodAsync(request, instance.extension)
|
result = await handleFilesystemMethodAsync(request, instance.extension)
|
||||||
} else if (request.method.startsWith('haextension.http.')) {
|
} else if (request.method.startsWith('haextension.web.')) {
|
||||||
result = await handleHttpMethodAsync(request, instance.extension)
|
result = await handleWebMethodAsync(request, instance.extension)
|
||||||
} else if (request.method.startsWith('haextension.permissions.')) {
|
} else if (request.method.startsWith('haextension.permissions.')) {
|
||||||
result = await handlePermissionsMethodAsync(request, instance.extension)
|
result = await handlePermissionsMethodAsync(request, instance.extension)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import type { IHaexHubExtension } from '~/types/haexhub'
|
|
||||||
import type { ExtensionRequest } from './types'
|
|
||||||
|
|
||||||
export async function handleHttpMethodAsync(
|
|
||||||
request: ExtensionRequest,
|
|
||||||
extension: IHaexHubExtension,
|
|
||||||
) {
|
|
||||||
if (!extension || !request) {
|
|
||||||
throw new Error('Extension not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implementiere HTTP Commands im Backend
|
|
||||||
throw new Error('HTTP methods not yet implemented')
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
// Export all handler functions
|
// Export all handler functions
|
||||||
export { handleDatabaseMethodAsync } from './database'
|
export { handleDatabaseMethodAsync } from './database'
|
||||||
export { handleFilesystemMethodAsync } from './filesystem'
|
export { handleFilesystemMethodAsync } from './filesystem'
|
||||||
export { handleHttpMethodAsync } from './http'
|
export { handleWebMethodAsync } from './web'
|
||||||
export { handlePermissionsMethodAsync } from './permissions'
|
export { handlePermissionsMethodAsync } from './permissions'
|
||||||
export { handleContextMethodAsync, setContextGetters } from './context'
|
export { handleContextMethodAsync, setContextGetters } from './context'
|
||||||
export { handleStorageMethodAsync } from './storage'
|
export { handleStorageMethodAsync } from './storage'
|
||||||
|
|||||||
92
src/composables/handlers/web.ts
Normal file
92
src/composables/handlers/web.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import type { IHaexHubExtension } from '~/types/haexhub'
|
||||||
|
import type { ExtensionRequest } from './types'
|
||||||
|
|
||||||
|
export async function handleWebMethodAsync(
|
||||||
|
request: ExtensionRequest,
|
||||||
|
extension: IHaexHubExtension,
|
||||||
|
) {
|
||||||
|
if (!extension || !request) {
|
||||||
|
throw new Error('Extension not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add permission check for web requests
|
||||||
|
// This should verify that the extension has permission to make web requests
|
||||||
|
// before proceeding with the fetch operation
|
||||||
|
|
||||||
|
const { method, params } = request
|
||||||
|
|
||||||
|
if (method === 'haextension.web.fetch') {
|
||||||
|
return await handleWebFetchAsync(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown web method: ${method}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleWebFetchAsync(params: Record<string, unknown>) {
|
||||||
|
const url = params.url as string
|
||||||
|
const method = (params.method as string) || 'GET'
|
||||||
|
const headers = (params.headers as Record<string, string>) || {}
|
||||||
|
const body = params.body as string | undefined
|
||||||
|
const timeout = (params.timeout as number) || 30000
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('URL is required')
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const controller = new AbortController()
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||||
|
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers,
|
||||||
|
signal: controller.signal,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert base64 body back to binary if present
|
||||||
|
if (body) {
|
||||||
|
const binaryString = atob(body)
|
||||||
|
const bytes = new Uint8Array(binaryString.length)
|
||||||
|
for (let i = 0; i < binaryString.length; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i)
|
||||||
|
}
|
||||||
|
fetchOptions.body = bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, fetchOptions)
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
|
||||||
|
// Read response as ArrayBuffer
|
||||||
|
const responseBody = await response.arrayBuffer()
|
||||||
|
|
||||||
|
// Convert ArrayBuffer to base64
|
||||||
|
const bytes = new Uint8Array(responseBody)
|
||||||
|
let binary = ''
|
||||||
|
for (let i = 0; i < bytes.byteLength; i++) {
|
||||||
|
binary += String.fromCharCode(bytes[i])
|
||||||
|
}
|
||||||
|
const base64Body = btoa(binary)
|
||||||
|
|
||||||
|
// Convert headers to plain object
|
||||||
|
const responseHeaders: Record<string, string> = {}
|
||||||
|
response.headers.forEach((value, key) => {
|
||||||
|
responseHeaders[key] = value
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers: responseHeaders,
|
||||||
|
body: base64Body,
|
||||||
|
url: response.url,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
if (error.name === 'AbortError') {
|
||||||
|
throw new Error(`Request timeout after ${timeout}ms`)
|
||||||
|
}
|
||||||
|
throw new Error(`Fetch failed: ${error.message}`)
|
||||||
|
}
|
||||||
|
throw new Error('Fetch failed with unknown error')
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user