feat(tutorials): add video training academy management with YouTube parser and live preview
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user