2026-04-11 15:59:14 +08:00
|
|
|
import { defineConfig } from 'vite'
|
|
|
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
|
|
import type { ProxyOptions } from 'vite'
|
|
|
|
|
import { resolve } from 'path'
|
2026-04-11 18:54:46 +08:00
|
|
|
import type { IncomingMessage, ServerResponse } from 'http'
|
|
|
|
|
import { mkdir, writeFile } from 'fs/promises'
|
|
|
|
|
import { tmpdir } from 'os'
|
|
|
|
|
import { randomBytes } from 'crypto'
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
function createProxyConfig(): ProxyOptions {
|
|
|
|
|
return {
|
|
|
|
|
target: 'http://127.0.0.1:8642',
|
|
|
|
|
changeOrigin: true,
|
|
|
|
|
configure: (proxy) => {
|
|
|
|
|
proxy.on('proxyReq', (proxyReq) => {
|
|
|
|
|
proxyReq.removeHeader('origin')
|
|
|
|
|
proxyReq.removeHeader('referer')
|
|
|
|
|
})
|
|
|
|
|
// Disable response buffering for SSE streaming
|
|
|
|
|
proxy.on('proxyRes', (proxyRes) => {
|
|
|
|
|
proxyRes.headers['cache-control'] = 'no-cache'
|
|
|
|
|
proxyRes.headers['x-accel-buffering'] = 'no'
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 18:54:46 +08:00
|
|
|
const UPLOAD_DIR = resolve(tmpdir(), 'hermes-uploads')
|
|
|
|
|
|
|
|
|
|
async function handleUpload(req: IncomingMessage, res: ServerResponse) {
|
|
|
|
|
if (req.method !== 'POST') {
|
|
|
|
|
res.writeHead(405, { 'Content-Type': 'application/json' })
|
|
|
|
|
res.end(JSON.stringify({ error: 'Method not allowed' }))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const contentType = req.headers['content-type'] || ''
|
|
|
|
|
if (!contentType.startsWith('multipart/form-data')) {
|
|
|
|
|
res.writeHead(400, { 'Content-Type': 'application/json' })
|
|
|
|
|
res.end(JSON.stringify({ error: 'Expected multipart/form-data' }))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await mkdir(UPLOAD_DIR, { recursive: true })
|
|
|
|
|
const chunks: Buffer[] = []
|
|
|
|
|
for await (const chunk of req) chunks.push(chunk)
|
|
|
|
|
const body = Buffer.concat(chunks).toString()
|
|
|
|
|
|
|
|
|
|
const boundary = '--' + contentType.split('boundary=')[1]
|
|
|
|
|
const parts = body.split(boundary).slice(1, -1)
|
|
|
|
|
|
|
|
|
|
const results: { name: string; path: string }[] = []
|
|
|
|
|
for (const part of parts) {
|
|
|
|
|
const headerEnd = part.indexOf('\r\n\r\n')
|
|
|
|
|
if (headerEnd === -1) continue
|
|
|
|
|
const header = part.substring(0, headerEnd)
|
|
|
|
|
const data = part.substring(headerEnd + 4, part.length - 2)
|
|
|
|
|
|
|
|
|
|
const filenameMatch = header.match(/filename="([^"]+)"/)
|
|
|
|
|
if (!filenameMatch) continue
|
|
|
|
|
|
|
|
|
|
const filename = filenameMatch[1]
|
|
|
|
|
const ext = filename.includes('.') ? '.' + filename.split('.').pop() : ''
|
|
|
|
|
const savedName = randomBytes(8).toString('hex') + ext
|
|
|
|
|
const savedPath = resolve(UPLOAD_DIR, savedName)
|
|
|
|
|
|
|
|
|
|
await writeFile(savedPath, Buffer.from(data, 'binary'))
|
|
|
|
|
results.push({ name: filename, path: savedPath })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
|
|
|
res.end(JSON.stringify({ files: results }))
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
|
|
|
res.end(JSON.stringify({ error: err.message }))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 15:59:14 +08:00
|
|
|
export default defineConfig({
|
2026-04-11 18:54:46 +08:00
|
|
|
plugins: [
|
|
|
|
|
vue(),
|
|
|
|
|
{
|
|
|
|
|
name: 'upload-middleware',
|
|
|
|
|
configureServer(server) {
|
|
|
|
|
server.middlewares.use((req, res, next) => {
|
|
|
|
|
if (req.url?.startsWith('/__upload')) {
|
|
|
|
|
handleUpload(req as any, res as any)
|
|
|
|
|
} else {
|
|
|
|
|
next()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
2026-04-11 15:59:14 +08:00
|
|
|
resolve: {
|
|
|
|
|
alias: {
|
|
|
|
|
'@': resolve(__dirname, 'src'),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
server: {
|
|
|
|
|
proxy: {
|
|
|
|
|
'/api': createProxyConfig(),
|
|
|
|
|
'/v1': createProxyConfig(),
|
|
|
|
|
'/health': createProxyConfig(),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|