82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { z } from "zod";
|
|
import { getSessionUser } from "@/lib/session";
|
|
import {
|
|
getHumanUserDetail,
|
|
updateHumanUserProfile,
|
|
} from "@/lib/zitadel";
|
|
|
|
/**
|
|
* GET /api/settings/profile — read the caller's ZITADEL profile.
|
|
* Returns first/last/display name and email. Used by the settings
|
|
* page server component to populate the form.
|
|
*
|
|
* PUT /api/settings/profile — update first + last name. Email is
|
|
* NOT mutable here — changing email needs verification flow that
|
|
* ZITADEL's own self-service UI already provides; we don't
|
|
* duplicate that.
|
|
*
|
|
* Authorization: any authenticated user can edit their own profile.
|
|
* The PAT (ZITADEL_SA_PAT) is used to call the ZITADEL v2 user
|
|
* service, but only against the caller's own userId. There is no
|
|
* userId field on the request — it's always derived from the
|
|
* session, so the route can't be abused to edit other users.
|
|
*/
|
|
|
|
const updateSchema = z.object({
|
|
firstName: z.string().trim().min(1).max(100),
|
|
lastName: z.string().trim().min(1).max(100),
|
|
});
|
|
|
|
export async function GET() {
|
|
const user = await getSessionUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
try {
|
|
const profile = await getHumanUserDetail(user.id);
|
|
return NextResponse.json({ profile });
|
|
} catch (e: any) {
|
|
// Surface ZITADEL-side failures (e.g. user not found, PAT expired)
|
|
// as 502 — the portal couldn't reach its identity provider, which
|
|
// is operationally different from a 4xx on the caller's input.
|
|
console.error("getHumanUserDetail failed:", e);
|
|
return NextResponse.json(
|
|
{ error: "Could not load profile from identity provider" },
|
|
{ status: 502 }
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function PUT(request: Request) {
|
|
const user = await getSessionUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
const body = await request.json().catch(() => ({}));
|
|
const parsed = updateSchema.safeParse(body);
|
|
if (!parsed.success) {
|
|
return NextResponse.json(
|
|
{ error: "Invalid request", details: parsed.error.flatten() },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
try {
|
|
const result = await updateHumanUserProfile({
|
|
userId: user.id,
|
|
givenName: parsed.data.firstName,
|
|
familyName: parsed.data.lastName,
|
|
});
|
|
return NextResponse.json({
|
|
displayName: result.displayName,
|
|
changeDate: result.changeDate,
|
|
});
|
|
} catch (e: any) {
|
|
console.error("updateHumanUserProfile failed:", e);
|
|
return NextResponse.json(
|
|
{ error: "Could not update profile in identity provider" },
|
|
{ status: 502 }
|
|
);
|
|
}
|
|
}
|