/
ncit
/
c4panelmodel
Обзор
Документация
Войти
/
ncit
/
c4panelmodel
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/lib/server/auth.ts
159 строк
5 KB
ncit
feat: implement full IcePanel feature clone (Phases 1-8)
09 июн 2026, 11:13
09 июн 2026, 11:13
858c15a
Код
Авторство
О чём код?
// @ts-nocheck /** * @deprecated This module is deprecated in favor of Supabase Auth. * * Authentication is now handled by @supabase/ssr via: * - supabase.auth.signInWithPassword() (login) * - supabase.auth.signUp() (registration) * - supabase.auth.signOut() (logout) * - supabase.auth.resetPasswordForEmail() (password reset) * - supabase.auth.verifyOtp() (email verification) * * Kept temporarily as a reference. Safe to remove after full migration verification. * Related Prisma models (Session, PasswordResetToken, EmailVerification) will be * removed in the schema cleanup phase. */ import { sha256 } from '@oslojs/crypto/sha2'; import { encodeHexLowerCase, encodeBase32LowerCaseNoPadding } from '@oslojs/encoding'; import { scryptSync, randomBytes, timingSafeEqual } from 'node:crypto'; import { db } from './db'; export interface SessionUser { id: string; email: string; name: string | null; avatarUrl: string | null; emailVerified: Date | null; theme: string; } const SESSION_COOKIE = 'c4panel_session'; const SESSION_TTL_MS = 1000 * 60 * 60 * 24 * 30; // 30 days const SESSION_REFRESH_MS = 1000 * 60 * 60 * 24 * 15; // refresh when <15d left export const sessionCookieName = SESSION_COOKIE; // ----- Passwords (scrypt) ----- export function hashPassword(password: string): string { const salt = randomBytes(16); const derived = scryptSync(password.normalize('NFKC'), salt, 64); return `${salt.toString('hex')}:${derived.toString('hex')}`; } export function verifyPassword(password: string, stored: string | null): boolean { if (!stored) return false; const [saltHex, hashHex] = stored.split(':'); if (!saltHex || !hashHex) return false; const salt = Buffer.from(saltHex, 'hex'); const expected = Buffer.from(hashHex, 'hex'); const derived = scryptSync(password.normalize('NFKC'), salt, expected.length); return derived.length === expected.length && timingSafeEqual(derived, expected); } // ----- Sessions ----- export function generateSessionToken(): string { const bytes = randomBytes(20); return encodeBase32LowerCaseNoPadding(bytes); } function sessionId(token: string): string { return encodeHexLowerCase(sha256(new TextEncoder().encode(token))); } export async function createSession(token: string, userId: string, userAgent?: string) { const id = sessionId(token); const uaHash = userAgent ? encodeHexLowerCase(sha256(new TextEncoder().encode(userAgent))) : null; const session = await db.session.create({ data: { id, userId, expiresAt: new Date(Date.now() + SESSION_TTL_MS), // Store a truncated user-agent hash for basic hijacking detection. // If the model doesn't have a userAgentHash column yet, we skip it gracefully. ...((await hasSessionField('userAgentHash')) ? { userAgentHash: uaHash?.slice(0, 64) } : {}) } as any }); return session; } /** Cache for whether the Session model has the userAgentHash field. */ let _hasUaField: boolean | null = null; async function hasSessionField(field: string): Promise<boolean> { if (_hasUaField !== null) return _hasUaField; try { // Quick probe — if the column exists, this won't throw. await db.session.findFirst({ select: { [field]: true } }); _hasUaField = true; } catch { _hasUaField = false; } return _hasUaField; } export async function validateSessionToken( token: string ): Promise<{ user: SessionUser; sessionId: string } | null> { const id = sessionId(token); const session = await db.session.findUnique({ where: { id }, include: { user: true } }); if (!session) return null; if (Date.now() >= session.expiresAt.getTime()) { await db.session.delete({ where: { id } }); return null; } // Sliding expiry: refresh if close to expiry. if (Date.now() >= session.expiresAt.getTime() - SESSION_REFRESH_MS) { await db.session.update({ where: { id }, data: { expiresAt: new Date(Date.now() + SESSION_TTL_MS) } }); } const u = session.user; return { sessionId: id, user: { id: u.id, email: u.email, name: u.name, avatarUrl: u.avatarUrl, emailVerified: u.emailVerified, theme: u.theme } }; } export async function invalidateSession(id: string) { await db.session.delete({ where: { id } }).catch(() => {}); } export async function invalidateAllUserSessions(userId: string) { await db.session.deleteMany({ where: { userId } }); } export function setSessionCookie( cookies: import('@sveltejs/kit').Cookies, token: string, expiresAt: Date ) { cookies.set(SESSION_COOKIE, token, { path: '/', httpOnly: true, sameSite: 'lax', expires: expiresAt, secure: process.env.NODE_ENV === 'production' }); } export function clearSessionCookie(cookies: import('@sveltejs/kit').Cookies) { cookies.delete(SESSION_COOKIE, { path: '/' }); } export const SESSION_TTL = SESSION_TTL_MS;