Files
screenclipper/apps/frontend/app/login/actions.ts
T
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

61 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import bcrypt from "bcryptjs";
import { prisma } from "@streamclipper/db";
import { createSessionToken, SESSION_COOKIE_NAME } from "../../lib/auth";
const SESSION_MAX_AGE_SEC = 60 * 60 * 24 * 30;
async function setSessionCookie(userId: string, username: string) {
const token = await createSessionToken({ sub: userId, username });
const jar = await cookies();
jar.set(SESSION_COOKIE_NAME, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: SESSION_MAX_AGE_SEC,
});
}
export async function bootstrapAdmin(formData: FormData) {
const username = String(formData.get("username") ?? "").trim();
const password = String(formData.get("password") ?? "");
if (!username || password.length < 8) {
throw new Error("Kullanıcı adı gerekli, şifre en az 8 karakter olmalı.");
}
const existing = await prisma.user.count();
if (existing > 0) {
redirect("/login");
}
const passwordHash = await bcrypt.hash(password, 12);
const user = await prisma.user.create({ data: { username, passwordHash } });
await setSessionCookie(user.id, user.username);
redirect("/");
}
export async function login(formData: FormData) {
const username = String(formData.get("username") ?? "").trim();
const password = String(formData.get("password") ?? "");
const user = await prisma.user.findUnique({ where: { username } });
if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
redirect("/login?error=1");
}
await setSessionCookie(user.id, user.username);
redirect("/");
}
export async function logout() {
const jar = await cookies();
jar.delete(SESSION_COOKIE_NAME);
redirect("/login");
}