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:
67
src/app/api/admin/password/route.ts
Normal file
67
src/app/api/admin/password/route.ts
Normal 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user