Initial commit: FPT Financial Planning Tool
Deploy App / deploy (push) Successful in 3m3s

This commit is contained in:
2026-07-08 19:19:39 +02:00
commit 4f990c9686
60 changed files with 12436 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
import { NextRequest, NextResponse } from "next/server";
import { createSessionToken, SESSION_COOKIE_NAME } from "@/lib/auth";
import { getAppCredential, verifyAppPassword } from "@/lib/credentials";
export async function POST(request: NextRequest) {
const { password } = await request.json();
const credential = await getAppCredential();
if (!credential) {
return NextResponse.json(
{ error: "Es ist noch kein Passwort gesetzt. Bitte zuerst ein Passwort festlegen." },
{ status: 409 }
);
}
if (typeof password !== "string" || password.length === 0 || !(await verifyAppPassword(password))) {
return NextResponse.json({ error: "Falsches Passwort." }, { status: 401 });
}
const token = await createSessionToken();
const response = NextResponse.json({ ok: true });
response.cookies.set(SESSION_COOKIE_NAME, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 30,
});
return response;
}
+8
View File
@@ -0,0 +1,8 @@
import { NextResponse } from "next/server";
import { SESSION_COOKIE_NAME } from "@/lib/auth";
export async function POST() {
const response = NextResponse.json({ ok: true });
response.cookies.delete(SESSION_COOKIE_NAME);
return response;
}
+37
View File
@@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { createSessionToken, SESSION_COOKIE_NAME } from "@/lib/auth";
import { getAppCredential, setAppPassword } from "@/lib/credentials";
const setupSchema = z.object({
password: z.string().min(4, "Das Passwort muss mindestens 4 Zeichen lang sein."),
});
// Legt das Login-Passwort einmalig fest. Nur solange noch keine AppCredential-Zeile
// existiert (d. h. beim allerersten Login) erreichbar -- danach ausschliesslich
// ueber /api/auth/login.
export async function POST(request: NextRequest) {
const existing = await getAppCredential();
if (existing) {
return NextResponse.json({ error: "Es ist bereits ein Passwort gesetzt." }, { status: 409 });
}
const body = await request.json();
const parsed = setupSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
}
await setAppPassword(parsed.data.password);
const token = await createSessionToken();
const response = NextResponse.json({ ok: true });
response.cookies.set(SESSION_COOKIE_NAME, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 30,
});
return response;
}
+7
View File
@@ -0,0 +1,7 @@
import { NextResponse } from "next/server";
import { getAppCredential } from "@/lib/credentials";
export async function GET() {
const credential = await getAppCredential();
return NextResponse.json({ passwordSet: credential != null });
}