feat(auth): Better Auth, kullanıcı adı ile giriş, DAL ve proxy

- MongoDB/Mongoose bağlantıları, kayıt kapalı, admin + username plugin, rate limit
- DAL: getCurrentUser, requireSession, requireRole, getActionUser
- Kullanıcı adı normalizasyonu: İ/I/ı → i (tr-TR 'ADMIN' → 'admın' hatası)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 02:02:00 +03:00
parent 381214c2cb
commit 5191a5cce1
14 changed files with 319 additions and 4 deletions
+68
View File
@@ -0,0 +1,68 @@
import "server-only";
import { headers } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { cache } from "react";
import type { SessionUser } from "@/features/auth/types";
import { getAuth } from "@/lib/auth";
import { visibleEmail } from "@/shared/account-email";
import type { SystemRole } from "@/shared/schemas/common";
/**
* Data Access Layer — her korumalı sayfa, layout, Server Action ve Route Handler oturumu
* BURADAN okur. proxy.ts yalnızca iyimser (cookie var mı?) kontrol yapar; güvenlik bu katmandadır.
*/
type RawUser = {
id: string;
name: string;
email: string;
username?: string | null;
role?: string | null;
banned?: boolean | null;
image?: string | null;
};
export function toSessionUser(user: RawUser): SessionUser {
return {
id: user.id,
name: user.name,
username: user.username ?? "",
email: visibleEmail(user.email),
role: user.role === "admin" ? "admin" : "user",
image: user.image ?? undefined,
};
}
/** İstek başına bir kez çalışır (React cache). Oturum yoksa veya kullanıcı askıdaysa null. */
export const getCurrentUser = cache(async (): Promise<SessionUser | null> => {
// headers() önce çağrılır: Cache Components prerender'ı burada durur, auth hiç oluşturulmaz.
const requestHeaders = await headers();
const session = await getAuth().api.getSession({ headers: requestHeaders });
if (!session || session.user.banned) return null;
return toSessionUser(session.user);
});
/** Sayfa/layout: oturum yoksa /login'e yönlendirir. */
export async function requireSession(): Promise<SessionUser> {
const user = await getCurrentUser();
if (!user) redirect("/login");
return user;
}
/** Sayfa/layout: rol yoksa 404 (sayfanın varlığı bile gösterilmez). */
export async function requireRole(role: SystemRole): Promise<SessionUser> {
const user = await requireSession();
if (user.role !== role) notFound();
return user;
}
/**
* Server Action / Route Handler: yönlendirme yapmaz, yetkisizse null döner
* (çağıran `fail(...)` veya 401/403 döndürür).
*/
export async function getActionUser(role?: SystemRole): Promise<SessionUser | null> {
const user = await getCurrentUser();
if (!user) return null;
if (role && user.role !== role) return null;
return user;
}