Update .gitignore to exclude admin JSON files; add bcrypt dependency and types for password handling; implement password change functionality in AdminPage component.

This commit is contained in:
rattatwinko
2025-06-17 13:12:07 +02:00
parent df97fe59bb
commit 96c9c28f83
5 changed files with 267 additions and 13 deletions

View File

@@ -0,0 +1,67 @@
import { NextResponse } from 'next/server';
import fs from 'fs';
import path from 'path';
import bcrypt from 'bcrypt';
const adminPath = path.join(process.cwd(), 'posts', 'admin.json');
const tempPath = path.join(process.cwd(), 'posts', 'admin.json.tmp');
const BCRYPT_SALT_ROUNDS = 12; // Stronger than minimum recommended
// Generate a bcrypt hash for 'admin' at runtime if needed
async function getDefaultHash() {
return await bcrypt.hash('admin', BCRYPT_SALT_ROUNDS);
}
async function getAdminData() {
try {
if (fs.existsSync(adminPath)) {
const data = JSON.parse(fs.readFileSync(adminPath, 'utf8'));
if (typeof data.hash === 'string') return data;
}
} catch (e) {
// Log error and continue to fallback
}
// Fallback: default admin/admin
return { hash: await getDefaultHash() };
}
function setAdminDataAtomic(hash: string) {
// Write to a temp file first, then rename
fs.writeFileSync(tempPath, JSON.stringify({ hash }, null, 2), 'utf8');
fs.renameSync(tempPath, adminPath);
}
export async function GET() {
// Check if a password is set (if admin.json exists)
const exists = fs.existsSync(adminPath);
return NextResponse.json({ passwordSet: exists });
}
export async function POST(request: Request) {
const body = await request.json();
const { password, mode } = body;
if (!password || typeof password !== 'string') {
return NextResponse.json({ error: 'Kein Passwort angegeben.' }, { status: 400 });
}
if (Buffer.byteLength(password, 'utf8') > 72) {
return NextResponse.json({ error: 'Passwort zu lang (max. 72 Zeichen).' }, { status: 400 });
}
const { hash } = await getAdminData();
if (mode === 'login') {
const match = await bcrypt.compare(password, hash);
if (match) {
return NextResponse.json({ success: true });
} else {
return NextResponse.json({ error: 'Falsches Passwort.' }, { status: 401 });
}
} else {
// Set/change password atomically
const newHash = await bcrypt.hash(password, BCRYPT_SALT_ROUNDS);
try {
setAdminDataAtomic(newHash);
return NextResponse.json({ success: true });
} catch (e) {
return NextResponse.json({ error: 'Fehler beim Speichern des Passworts.' }, { status: 500 });
}
}
}