feat(auth): complete sqlite auth and client invitations

This commit is contained in:
poyrazavsever
2026-07-16 16:21:31 +03:00
parent cce7265fb1
commit 92bc99ba12
30 changed files with 14355 additions and 170 deletions
+29 -95
View File
@@ -1,103 +1,37 @@
import { createInternalAuthUser } from "@/lib/auth/internal-users";
import { createClient } from "@/lib/supabase/server";
import { NextResponse } from "next/server";
import {
createPortalInvitation,
PortalInvitationError,
} from "@/server/auth/invitations";
import { getSessionContextFromHeaders } from "@/server/auth/session";
/**
* Legacy adapter for the current client detail screen.
* The old endpoint created a Supabase Auth user with a freelancer-chosen password.
* It now issues a one-time Better Auth invitation and never accepts a password.
*/
export async function POST(request: Request) {
const actor = await getSessionContextFromHeaders(new Headers(request.headers));
if (!actor) {
return NextResponse.json({ error: "Müşteri daveti için giriş yapmalısınız." }, { status: 401 });
}
try {
const { email, password, client_id } = await request.json();
const { email, client_id: clientId } = await request.json();
const invitation = await createPortalInvitation(actor, { email, clientId });
if (!email || !password || !client_id) {
return NextResponse.json(
{ error: "E-posta, şifre ve müşteri ID gereklidir." },
{ status: 400 },
);
}
const supabase = await createClient();
const {
data: { user },
error: userError,
} = await supabase.auth.getUser();
if (userError || !user) {
return NextResponse.json(
{ error: "Müşteri hesabı oluşturmak için giriş yapmalısınız." },
{ status: 401 },
);
}
const { data: client, error: clientError } = await supabase
.from("clients")
.select("id, client_auth_id")
.eq("id", client_id)
.eq("user_id", user.id)
.single();
if (clientError || !client) {
return NextResponse.json(
{ error: "Müşteri kaydı bulunamadı." },
{ status: 404 },
);
}
if (client.client_auth_id) {
return NextResponse.json(
{ error: "Bu müşteri için portal hesabı zaten oluşturulmuş." },
{ status: 409 },
);
}
const {
admin,
user: createdUser,
userId,
} = await createInternalAuthUser({
email,
password,
role: "client",
reason: "client_portal",
});
const { error: profileError } = await admin
.from("profiles")
.update({ role: "client" })
.eq("id", userId);
if (profileError) {
return NextResponse.json(
{
error: `Kullanıcı oluşturuldu fakat profil rolü güncellenemedi: ${profileError.message}`,
},
{ status: 500 },
);
}
const { error: updateClientError } = await admin
.from("clients")
.update({ client_auth_id: userId })
.eq("id", client_id)
.eq("user_id", user.id);
if (updateClientError) {
return NextResponse.json(
{
error: `Kullanıcı oluşturuldu fakat müşteri kaydıyla ilişkilendirilemedi: ${updateClientError.message}`,
},
{ status: 500 },
);
}
return NextResponse.json({ success: true, user: createdUser });
return NextResponse.json({ success: true, invitation }, { status: 201 });
} catch (error) {
console.error("Create client user error:", error);
return NextResponse.json(
{
error:
error instanceof Error
? error.message
: "Sunucu tarafında beklenmeyen bir hata oluştu.",
},
{ status: 500 },
);
if (error instanceof SyntaxError) {
return NextResponse.json({ error: "Geçersiz JSON gövdesi." }, { status: 400 });
}
if (error instanceof PortalInvitationError) {
const status = error.code === "FORBIDDEN" ? 403 : error.code === "INVALID_INPUT" ? 400 : 409;
return NextResponse.json({ error: error.message, code: error.code }, { status });
}
console.error("Legacy client invitation adapter failed", error);
return NextResponse.json({ error: "Müşteri daveti oluşturulamadı." }, { status: 500 });
}
}