Initial commit: Personal Dashboard
Next.js 16 dashboard with configurable widgets (favorites, notes, calendar, clock, calculator, search, domain-check), multi-tab support, user auth, dark mode, and Docker deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { requireCurrentUser, UnauthorizedError } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import {
|
||||
normalizeDisplayName,
|
||||
normalizeEmail,
|
||||
normalizePassword,
|
||||
normalizeUserRole
|
||||
} from "@/lib/validation";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const currentUser = await requireCurrentUser();
|
||||
|
||||
if (currentUser.role !== "ADMIN") {
|
||||
return NextResponse.json({ error: "Keine Berechtigung." }, { status: 403 });
|
||||
}
|
||||
|
||||
const users = await prisma.user.findMany({
|
||||
orderBy: {
|
||||
createdAt: "asc"
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
displayName: true,
|
||||
role: true,
|
||||
createdAt: true
|
||||
}
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
users
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof UnauthorizedError) {
|
||||
return NextResponse.json({ error: "Nicht angemeldet." }, { status: 401 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Nutzer konnten nicht geladen werden." }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const currentUser = await requireCurrentUser();
|
||||
|
||||
if (currentUser.role !== "ADMIN") {
|
||||
return NextResponse.json({ error: "Keine Berechtigung." }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = (await request.json().catch(() => null)) as {
|
||||
email?: unknown;
|
||||
password?: unknown;
|
||||
displayName?: unknown;
|
||||
role?: unknown;
|
||||
} | null;
|
||||
|
||||
const email = normalizeEmail(body?.email);
|
||||
const password = normalizePassword(body?.password);
|
||||
const displayName = normalizeDisplayName(body?.displayName);
|
||||
const role = normalizeUserRole(body?.role) ?? "USER";
|
||||
|
||||
if (!email || !password) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "E-Mail oder Passwort ungültig. Das Passwort muss 10 bis 128 Zeichen lang sein."
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const existingUser = await prisma.user.findUnique({
|
||||
where: {
|
||||
email
|
||||
},
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
});
|
||||
|
||||
if (existingUser) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Ein Nutzer mit dieser E-Mail existiert bereits."
|
||||
},
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
const passwordHash = await bcrypt.hash(password, 12);
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
passwordHash,
|
||||
displayName,
|
||||
role,
|
||||
settings: {
|
||||
create: {
|
||||
darkMode: false,
|
||||
calendarMaxEvents: 8,
|
||||
calendarLookaheadDays: 60,
|
||||
dashboardTitle: "Personal Dashboard",
|
||||
logoUrl: "/logo.svg",
|
||||
primaryColor: "#2563eb",
|
||||
secondaryColor: "#dbeafe"
|
||||
}
|
||||
},
|
||||
widgets: {
|
||||
create: [
|
||||
{
|
||||
type: "favorites",
|
||||
title: "Links/Favoriten",
|
||||
position: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 3,
|
||||
h: 6
|
||||
},
|
||||
{
|
||||
type: "note",
|
||||
title: "Notiz",
|
||||
position: 1,
|
||||
x: 3,
|
||||
y: 0,
|
||||
w: 3,
|
||||
h: 4
|
||||
},
|
||||
{
|
||||
type: "search",
|
||||
title: "Suche",
|
||||
position: 3,
|
||||
x: 9,
|
||||
y: 0,
|
||||
w: 3,
|
||||
h: 2
|
||||
},
|
||||
{
|
||||
type: "calendar",
|
||||
title: "Kalender",
|
||||
position: 4,
|
||||
x: 9,
|
||||
y: 2,
|
||||
w: 3,
|
||||
h: 7
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
displayName: true,
|
||||
role: true,
|
||||
createdAt: true
|
||||
}
|
||||
});
|
||||
|
||||
const createdWidgets = await prisma.widget.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
type: {
|
||||
in: ["note"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
createdWidgets.map((widget) =>
|
||||
prisma.noteBoardItem.create({
|
||||
data: {
|
||||
id: widget.id,
|
||||
userId: user.id,
|
||||
type: widget.type,
|
||||
title: widget.title,
|
||||
content: ""
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
user
|
||||
},
|
||||
{ status: 201 }
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof UnauthorizedError) {
|
||||
return NextResponse.json({ error: "Nicht angemeldet." }, { status: 401 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ error: "Nutzer konnte nicht erstellt werden." }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user