202 lines
5.6 KiB
TypeScript
202 lines
5.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { storeFile } from '@/lib/fileStorage'
|
|
import { push } from '@/lib/push'
|
|
import { withCors } from '@/lib/middleware'
|
|
|
|
interface WindowInfo {
|
|
title: string
|
|
path: string
|
|
memory: number
|
|
}
|
|
|
|
const timeoutMap = new Map<string, NodeJS.Timeout>()
|
|
|
|
async function handleScreenshotUpload(req: NextRequest) {
|
|
try {
|
|
const pathSegments = req.nextUrl.pathname.split('/')
|
|
const hostnameIndex = pathSegments.indexOf('hosts') + 1
|
|
const hostname = pathSegments[hostnameIndex]
|
|
|
|
if (!hostname) {
|
|
return NextResponse.json({ error: '缺少主机名' }, { status: 400 })
|
|
}
|
|
|
|
const formData = await req.formData()
|
|
const files: File[] = []
|
|
const windowsInfo: WindowInfo[] = JSON.parse(formData.get('windows_info') as string || '[]')
|
|
|
|
// Extract files from formData
|
|
for (const [key, value] of formData.entries()) {
|
|
if (key.startsWith('screenshot_') && value instanceof File) {
|
|
files.push(value)
|
|
}
|
|
}
|
|
|
|
if (files.length === 0) {
|
|
return NextResponse.json({ error: '没有收到文件' }, { status: 400 })
|
|
}
|
|
|
|
// Process screenshots
|
|
const screenshots = await Promise.all(files.map(async (file, index) => {
|
|
const ext = file.name ? file.name.split('.').pop() : 'webp'
|
|
const filename = `${hostname}-${Date.now()}-${Math.round(Math.random() * 1E9)}.${ext}`
|
|
|
|
const buffer = Buffer.from(await file.arrayBuffer())
|
|
const storedFile = await storeFile(buffer, filename, file.type, 'screenshot', hostname)
|
|
|
|
return {
|
|
fileId: storedFile.id,
|
|
objectName: storedFile.objectName,
|
|
filename: storedFile.filename,
|
|
contentType: storedFile.contentType,
|
|
fileSize: storedFile.size,
|
|
monitorName: formData.get(`monitor_name_${index}`) as string || `Monitor ${index + 1}`
|
|
}
|
|
}))
|
|
|
|
// Ensure host exists first
|
|
await prisma.host.upsert({
|
|
where: { hostname },
|
|
update: {},
|
|
create: { hostname }
|
|
})
|
|
|
|
// Create new record
|
|
const newRecord = await prisma.record.create({
|
|
data: {
|
|
hostname,
|
|
timestamp: new Date(),
|
|
windows: {
|
|
create: windowsInfo
|
|
},
|
|
screenshots: {
|
|
create: screenshots
|
|
}
|
|
}
|
|
})
|
|
|
|
// Handle host status and notifications
|
|
const host = await prisma.host.findUnique({
|
|
where: { hostname },
|
|
select: { lastUpdate: true }
|
|
})
|
|
|
|
const lastUpdate = host?.lastUpdate || new Date(0)
|
|
|
|
if (lastUpdate.getTime() === new Date(0).getTime()) {
|
|
push(`新设备 ${hostname} 上线`)
|
|
} else if (lastUpdate.getTime() < new Date().getTime() - 1000 * 60) {
|
|
push(`设备 ${hostname} 上线`)
|
|
}
|
|
|
|
// Clear existing timeout and set new one
|
|
if (timeoutMap.has(hostname)) {
|
|
clearTimeout(timeoutMap.get(hostname)!)
|
|
}
|
|
timeoutMap.set(hostname, setTimeout(() => {
|
|
push(`设备 ${hostname} 离线`)
|
|
timeoutMap.delete(hostname)
|
|
}, 1000 * 60))
|
|
|
|
// Update host last update time
|
|
await prisma.host.upsert({
|
|
where: { hostname },
|
|
update: { lastUpdate: new Date() },
|
|
create: { hostname, lastUpdate: new Date() }
|
|
})
|
|
|
|
return NextResponse.json({
|
|
message: '上传成功',
|
|
hostname,
|
|
filesCount: files.length,
|
|
windowsCount: windowsInfo.length
|
|
})
|
|
|
|
} catch (error) {
|
|
console.error('上传失败:', error)
|
|
return NextResponse.json({ error: '上传失败' }, { status: 500 })
|
|
}
|
|
}
|
|
|
|
async function handleGetScreenshots(req: NextRequest) {
|
|
try {
|
|
const pathSegments = req.nextUrl.pathname.split('/')
|
|
const hostnameIndex = pathSegments.indexOf('hosts') + 1
|
|
const hostname = pathSegments[hostnameIndex]
|
|
|
|
if (!hostname) {
|
|
return NextResponse.json({ error: '缺少主机名' }, { status: 400 })
|
|
}
|
|
|
|
const { searchParams } = req.nextUrl
|
|
const startTimeParam = searchParams.get('startTime')
|
|
const endTimeParam = searchParams.get('endTime')
|
|
|
|
let startTime: Date | undefined
|
|
let endTime: Date | undefined
|
|
|
|
if (startTimeParam) {
|
|
const timestamp = isNaN(Number(startTimeParam)) ?
|
|
new Date(startTimeParam) :
|
|
new Date(Number(startTimeParam) * 1000)
|
|
startTime = timestamp
|
|
}
|
|
|
|
if (endTimeParam) {
|
|
const timestamp = isNaN(Number(endTimeParam)) ?
|
|
new Date(endTimeParam) :
|
|
new Date(Number(endTimeParam) * 1000)
|
|
endTime = timestamp
|
|
}
|
|
|
|
// Build query conditions
|
|
const whereClause: any = { hostname }
|
|
|
|
if (startTime || endTime) {
|
|
whereClause.timestamp = {}
|
|
if (startTime) whereClause.timestamp.gte = startTime
|
|
if (endTime) whereClause.timestamp.lte = endTime
|
|
}
|
|
|
|
// Get records
|
|
const records = await prisma.record.findMany({
|
|
where: whereClause,
|
|
include: {
|
|
windows: true,
|
|
screenshots: true
|
|
},
|
|
orderBy: {
|
|
timestamp: 'desc'
|
|
}
|
|
})
|
|
|
|
// Get host info
|
|
const host = await prisma.host.findUnique({
|
|
where: { hostname },
|
|
select: {
|
|
hostname: true,
|
|
lastUpdate: true
|
|
}
|
|
})
|
|
|
|
if (!host) {
|
|
return NextResponse.json({ error: '未找到主机记录' }, { status: 404 })
|
|
}
|
|
|
|
return NextResponse.json({
|
|
hostname,
|
|
lastUpdate: host.lastUpdate,
|
|
records,
|
|
total: records.length
|
|
})
|
|
|
|
} catch (error) {
|
|
console.error('获取记录失败:', error)
|
|
return NextResponse.json({ error: '获取记录失败' }, { status: 500 })
|
|
}
|
|
}
|
|
|
|
export const POST = withCors(handleScreenshotUpload)
|
|
export const GET = withCors(handleGetScreenshots)
|