feat(tutorials): add video training academy management with YouTube parser and live preview

This commit is contained in:
mstfyldz
2026-08-22 13:49:00 +03:00
parent 2d149f1178
commit 7f690cc6d3
29 changed files with 2476 additions and 1034 deletions
+13 -8
View File
@@ -10,19 +10,24 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
// Boilerplate mock logic
// TODO: In production, lookup user in Prisma and verify password using bcrypt
// const user = await db.user.findUnique({ where: { email: credentials.email } })
if (credentials?.email === "admin@ayris.tech" && credentials?.password === "admin") {
// Gerçek AyrisLegal kullanıcı sistemine (Supabase Auth) bağlı DEĞİL —
// admin paneli girişi bilerek ayrı, basit bir env-tabanlı kontrol.
// ADMIN_EMAIL / ADMIN_PASSWORD .env dosyasında tanımlı, sadece
// server-side (bu fonksiyon) okunuyor, hiçbir zaman client'a gitmiyor.
const adminEmail = process.env.ADMIN_EMAIL
const adminPassword = process.env.ADMIN_PASSWORD
if (!adminEmail || !adminPassword) return null
if (credentials?.email === adminEmail && credentials?.password === adminPassword) {
return {
id: "1",
name: "Admin User",
email: "admin@ayris.tech",
name: "Admin",
email: adminEmail,
role: "ADMIN"
}
}
return null
}
})
-9
View File
@@ -1,9 +0,0 @@
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const db = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db
+79
View File
@@ -0,0 +1,79 @@
import 'server-only';
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL;
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !serviceRoleKey) {
throw new Error('SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY tanımlı değil (.env dosyasına bakın).');
}
// service_role ile — RLS bypass edilir, admin panelinin ihtiyacı budur (licenses,
// crash_reports, access_logs gibi tablolarda bilerek hiç RLS policy yok, sadece
// service_role erişebiliyor). SADECE server component / route handler içinde
// import edilmeli — 'server-only' paketi bunu build-time'da garanti ediyor.
export const supabaseAdmin = createClient(supabaseUrl, serviceRoleKey, {
auth: { autoRefreshToken: false, persistSession: false },
});
export interface AuthUser {
id: string;
email?: string;
created_at: string;
last_sign_in_at?: string | null;
}
// profiles tablosunda e-posta yok (auth.users'a referans veriyor) — e-postayı
// Supabase Auth Admin API'den çekmemiz gerekiyor.
export async function listAuthUsers(): Promise<Map<string, AuthUser>> {
const res = await fetch(`${supabaseUrl}/auth/v1/admin/users?per_page=1000`, {
headers: { apikey: serviceRoleKey!, Authorization: `Bearer ${serviceRoleKey}` },
cache: 'no-store',
});
if (!res.ok) return new Map();
const data = await res.json();
const users: AuthUser[] = data.users || [];
return new Map(users.map((u) => [u.id, u]));
}
// Kullanıcı silme: Supabase Auth Admin API üzerinden. profiles satırını silmek
// admin'in sorumluluğunda ayrıca çağrılmalı — auth.users -> profiles cascade'i
// bu self-hosted kurulumda garanti değil.
export async function deleteAuthUser(id: string): Promise<{ error?: string }> {
const res = await fetch(`${supabaseUrl}/auth/v1/admin/users/${id}`, {
method: 'DELETE',
headers: { apikey: serviceRoleKey!, Authorization: `Bearer ${serviceRoleKey}` },
});
if (!res.ok) {
const text = await res.text().catch(() => '');
return { error: `Auth kullanıcı silinemedi (${res.status}): ${text}` };
}
return {};
}
export interface CustomerOption {
id: string;
name: string | null;
email: string;
}
// Lisans düzenleme/oluşturma formlarındaki "Müşteri" seçimi Kullanıcılar sayfasıyla
// AYNI kaynaktan (profiles + Supabase Auth e-postası) besleniyor — serbest metin
// yerine, gerçekten var olan bir kullanıcı seçtirmek için.
export async function listCustomerOptions(): Promise<CustomerOption[]> {
const [{ data: profiles }, authUsers] = await Promise.all([
supabaseAdmin.from('profiles').select('id, full_name'),
listAuthUsers(),
]);
const options: CustomerOption[] = (profiles || [])
.map((p: { id: string; full_name: string | null }) => ({
id: p.id,
name: p.full_name,
email: authUsers.get(p.id)?.email || '',
}))
.filter((c) => c.email);
options.sort((a, b) => (a.name || a.email).localeCompare(b.name || b.email, 'tr'));
return options;
}