Files
screenclipper/apps/frontend/app/login/actions.ts
T
ayrisdevandClaude Sonnet 5 775633bfd0 fix: oturum çerezini x-forwarded-proto'ya göre secure işaretle
NODE_ENV=production'da cookie hep secure:true oluyordu, ama panel
şu an SSL'siz sslip.io preview domain'inde (http://) çalışıyor —
tarayıcı Secure cookie'yi HTTP origin'de hiç saklamıyor. Giriş
"başarılı" gibi görünüp anında /login'e geri dönülüyordu. Artık
gerçek istek protokolüne (Traefik'in x-forwarded-proto header'ı)
bakılıyor.

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

68 lines
2.3 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, headers } 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();
// NODE_ENV alone isn't a reliable signal for this — the panel is often
// served over plain HTTP (e.g. Coolify's auto-generated sslip.io preview
// domain has no TLS). A `Secure` cookie set on an HTTP origin is silently
// dropped by the browser, which made every login look successful for an
// instant and then immediately bounce back to /login. Check the actual
// request scheme instead (Traefik/Coolify sets x-forwarded-proto).
const proto = (await headers()).get("x-forwarded-proto");
jar.set(SESSION_COOKIE_NAME, token, {
httpOnly: true,
secure: proto === "https",
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");
}