Files
ayrisdevandClaude Sonnet 5 fb8485d638 feat: panele auth sistemi ekle (kullanıcı/şifre + oturum + middleware)
Panel şu ana kadar herkese açıktı — URL'yi bilen herkes kanal
ekleyip/silebilir, cookie güncelleyebilir, kayıt indirebilirdi.

- users tablosu (bcrypt şifre hash'i)
- /login: hiç kullanıcı yoksa "ilk admin hesabı oluştur" formu, varsa
  normal giriş formu
- jose ile imzalanmış, httpOnly çerezde tutulan 30 günlük oturum
- middleware.ts: /login hariç tüm rotaları korur (video stream route'u
  dahil — aynı origin istekleri çerezi otomatik taşır)
- layout: oturum yoksa nav hiç gösterilmiyor, varsa kullanıcı adı +
  çıkış butonu ekleniyor

SESSION_SECRET production'da zorunlu (docker-compose derleme zamanında
kontrol ediyor); Coolify'a rastgele bir değer eklendi.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 11:26:42 +03:00

41 lines
1.2 KiB
TypeScript

import { cookies } from "next/headers";
import { SignJWT, jwtVerify } from "jose";
export const SESSION_COOKIE_NAME = "session";
const secret = new TextEncoder().encode(
process.env.SESSION_SECRET ?? "dev-insecure-secret-change-me-in-production",
);
export interface SessionPayload {
sub: string;
username: string;
}
export async function createSessionToken(payload: SessionPayload): Promise<string> {
return new SignJWT({ username: payload.username })
.setProtectedHeader({ alg: "HS256" })
.setSubject(payload.sub)
.setIssuedAt()
.setExpirationTime("30d")
.sign(secret);
}
export async function verifySessionToken(token: string): Promise<SessionPayload | null> {
try {
const { payload } = await jwtVerify(token, secret);
if (typeof payload.sub !== "string" || typeof payload.username !== "string") return null;
return { sub: payload.sub, username: payload.username };
} catch {
return null;
}
}
/** Server Component helper — reads and verifies the session cookie. */
export async function getSession(): Promise<SessionPayload | null> {
const jar = await cookies();
const token = jar.get(SESSION_COOKIE_NAME)?.value;
if (!token) return null;
return verifySessionToken(token);
}