- Enhanced the proxy function to log user activity for both authenticated and unauthenticated requests, capturing details such as IP address, user agent, and referer. - Introduced a new utility for logging activities, allowing for structured tracking of user actions across various routes. - Updated photo upload and guess submission routes to log relevant user activity, improving visibility into user interactions. - Added a script to watch user activity logs in real-time for easier monitoring.
59 lines
1.8 KiB
TypeScript
59 lines
1.8 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
|
import { readFile } from "fs/promises"
|
|
import { join } from "path"
|
|
import { existsSync } from "fs"
|
|
|
|
export async function GET(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ filename: string }> }
|
|
) {
|
|
try {
|
|
const { filename } = await params
|
|
|
|
// Sanitize filename - only allow alphanumeric, dots, hyphens
|
|
if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {
|
|
return NextResponse.json({ error: "Invalid filename" }, { status: 400 })
|
|
}
|
|
|
|
// Get the uploads directory
|
|
const uploadsDir = join(process.cwd(), "public", "uploads")
|
|
const filepath = join(uploadsDir, filename)
|
|
|
|
// Security: ensure file is within uploads directory (prevent path traversal)
|
|
if (!filepath.startsWith(uploadsDir)) {
|
|
return NextResponse.json({ error: "Invalid path" }, { status: 400 })
|
|
}
|
|
|
|
// Check if file exists
|
|
if (!existsSync(filepath)) {
|
|
console.error(`[UPLOAD] File not found: ${filepath} (cwd: ${process.cwd()})`)
|
|
return NextResponse.json({ error: "File not found" }, { status: 404 })
|
|
}
|
|
|
|
// Read and serve the file
|
|
const fileBuffer = await readFile(filepath)
|
|
|
|
// Determine content type from extension
|
|
const ext = filename.split(".").pop()?.toLowerCase()
|
|
const contentType =
|
|
ext === "jpg" || ext === "jpeg" ? "image/jpeg" :
|
|
ext === "png" ? "image/png" :
|
|
ext === "gif" ? "image/gif" :
|
|
ext === "webp" ? "image/webp" :
|
|
"application/octet-stream"
|
|
|
|
return new NextResponse(fileBuffer, {
|
|
headers: {
|
|
"Content-Type": contentType,
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
},
|
|
})
|
|
} catch (error) {
|
|
console.error("[UPLOAD] Error serving file:", error)
|
|
return NextResponse.json(
|
|
{ error: "Internal server error" },
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|