61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { auth, getAuthSession } from "@/lib/auth";
|
|
import { checkRateLimit, getClientIp } from "@/lib/security/rateLimit";
|
|
import { sanitizeFilePath } from "@/lib/security/sanitize";
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
|
|
export async function GET(req: NextRequest) {
|
|
const session = await getAuthSession(req.headers);
|
|
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
|
|
const ip = getClientIp(req);
|
|
const { allowed } = checkRateLimit(ip);
|
|
if (!allowed) return NextResponse.json({ error: "Too many requests" }, { status: 429 });
|
|
|
|
const mcBase = path.resolve(process.env.MC_SERVER_PATH ?? "/opt/minecraft/server");
|
|
const relativePath = new URL(req.url).searchParams.get("path") ?? "/";
|
|
|
|
let resolvedPath: string;
|
|
try {
|
|
resolvedPath = sanitizeFilePath(relativePath, mcBase);
|
|
} catch {
|
|
return NextResponse.json({ error: "Invalid path" }, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const entries = fs.readdirSync(resolvedPath, { withFileTypes: true });
|
|
const files = entries.map((entry) => {
|
|
const fullPath = path.join(resolvedPath, entry.name);
|
|
let size = 0;
|
|
let modifiedAt = 0;
|
|
try {
|
|
const stat = fs.statSync(fullPath);
|
|
size = stat.size;
|
|
modifiedAt = stat.mtimeMs;
|
|
} catch {}
|
|
|
|
return {
|
|
name: entry.name,
|
|
path: path.relative(mcBase, fullPath),
|
|
isDirectory: entry.isDirectory(),
|
|
size,
|
|
modifiedAt,
|
|
};
|
|
});
|
|
|
|
// Sort: directories first, then files, alphabetically
|
|
files.sort((a, b) => {
|
|
if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
|
|
return NextResponse.json({
|
|
path: path.relative(mcBase, resolvedPath) || "/",
|
|
entries: files,
|
|
});
|
|
} catch (err) {
|
|
return NextResponse.json({ error: "Cannot read directory" }, { status: 500 });
|
|
}
|
|
}
|