'use server'; import { createAdminClient } from '@/lib/supabase/admin'; import { revalidatePath, unstable_noStore as noStore } from 'next/cache'; import { sendMail } from '@/utils/mail'; export async function getUsers() { noStore(); const admin = createAdminClient(); const { data: { users }, error } = await admin.auth.admin.listUsers(); if (error) throw error; const { data: dbUsers, error: dbError } = await admin .from('users') .select('id, role, company_id, companies(id, name)'); const { data: dbProfiles, error: profilesError } = await admin .from('profiles') .select('id, first_name, last_name'); const userMap: Record = {}; if (!dbError && dbUsers) { dbUsers.forEach((u: any) => { userMap[u.id] = { role: u.role, company_id: u.company_id || null, company_name: Array.isArray(u.companies) ? u.companies[0]?.name || null : u.companies?.name || null, }; }); } const profileMap: Record = {}; if (!profilesError && dbProfiles) { dbProfiles.forEach((p: any) => { profileMap[p.id] = { first_name: p.first_name || null, last_name: p.last_name || null, }; }); } return users.map((u) => ({ ...u, role: userMap[u.id]?.role || 'partner', company_id: userMap[u.id]?.company_id || null, company_name: userMap[u.id]?.company_name || null, first_name: profileMap[u.id]?.first_name || null, last_name: profileMap[u.id]?.last_name || null, })); } export async function createUser(data: { email: string; role?: 'admin' | 'partner' | 'verwaltung'; email_confirm?: boolean; company_id?: string; first_name?: string; last_name?: string }) { const admin = createAdminClient(); const { data: { user }, error } = await admin.auth.admin.createUser({ email: data.email, password: Math.random().toString(36).slice(-10) + Math.random().toString(36).slice(-10), email_confirm: data.email_confirm ?? true, }); if (error) throw error; if (!user) throw new Error('Benutzer konnte nicht erstellt werden.'); const role = data.role || 'partner'; const { error: dbError } = await admin .from('users') .upsert({ id: user.id, role, company_id: data.company_id || null }, { onConflict: 'id' }); if (dbError) throw dbError; const { error: profileError } = await admin .from('profiles') .upsert({ id: user.id, first_name: data.first_name || null, last_name: data.last_name || null, }, { onConflict: 'id' }); if (profileError) throw profileError; // Generate recovery link to let user set password try { const { data: linkData, error: linkError } = await admin.auth.admin.generateLink({ type: 'recovery', email: data.email, }); if (!linkError && linkData) { const tokenHash = (linkData as any)?.properties?.hashed_token; if (tokenHash) { const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'; const setupLink = `${siteUrl}/auth/verify-link?token_hash=${tokenHash}&type=recovery&next=/auth/update-password`; await sendMail({ to: data.email, subject: 'Willkommen bei CASPOS Shop – Ihr Benutzerkonto wurde erstellt', text: `Hallo,\n\nein neues Benutzerkonto wurde für Sie auf CASPOS Shop erstellt.\n\nBitte klicken Sie auf den folgenden Link, um Ihr persönliches Passwort festzulegen und Ihr Konto zu aktivieren:\n${setupLink}\n\nViele Grüße,\nIhr CASPOS Shop-Team`, html: getBeautifulEmailHtml( 'Willkommen bei CASPOS Shop', `

Hallo,

für Sie wurde erfolgreich ein neues Benutzerkonto auf unserer Plattform eingerichtet.

Bitte klicken Sie auf den untenstehenden Button, um Ihr Passwort festzulegen und Ihr Konto zu aktivieren:

`, 'Passwort festlegen', setupLink ), }); } } } catch (mailError) { console.error('Failed to send welcome setup mail:', mailError); } revalidatePath('/admin/users'); return user; } export async function deleteUser(id: string) { try { const admin = createAdminClient(); // Check if user has orders (documents/belege) const { count, error: countError } = await admin .from('orders') .select('*', { count: 'exact', head: true }) .eq('user_id', id); if (countError) { console.error('Failed to count user orders:', countError); } if (count && count > 0) { throw new Error('Auf diesen User gibt es bereits Belege. Sperren möglich.'); } // Delete from public.users first to satisfy foreign key constraints const { error: dbError } = await admin.from('users').delete().eq('id', id); if (dbError) { console.error('Database delete user error:', dbError); throw new Error(`Datenbank-Fehler beim Löschen des Benutzers: ${dbError.message}`); } const { error } = await admin.auth.admin.deleteUser(id); if (error) { console.error('Auth delete user error:', error); throw new Error(`Auth-Fehler beim Löschen des Benutzers: ${error.message}`); } revalidatePath('/admin/users'); } catch (err: any) { console.error('deleteUser Server Action failed:', err); throw new Error(err.message || 'Benutzer konnte nicht gelöscht werden.'); } } export async function updateUserRole(id: string, role: 'admin' | 'partner' | 'verwaltung' | 'gesperrt') { const admin = createAdminClient(); const { error } = await admin .from('users') .upsert({ id, role }, { onConflict: 'id' }); if (error) throw error; revalidatePath('/admin/users'); } export async function updateUserStatus(id: string, ban: boolean) { const admin = createAdminClient(); const { error } = await admin.auth.admin.updateUserById(id, { ban_duration: ban ? '876000h' : '0h', }); if (error) throw error; revalidatePath('/admin/users'); } export async function resetPassword(email: string) { const admin = createAdminClient(); const { data, error } = await admin.auth.admin.generateLink({ type: 'recovery', email, }); if (error) throw error; const linkData = data as any; // Extract token_hash from the generated link properties const tokenHash = linkData?.properties?.hashed_token; if (!tokenHash) throw new Error('Recovery token not generated'); // Build link pointing to our own /auth/confirm route const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://staging.hephex.de'; const resetLink = `${siteUrl}/auth/verify-link?token_hash=${tokenHash}&type=recovery&next=/auth/update-password`; const messageHtml = `

Hallo,

der Administrator hat das Zurücksetzen Ihres Passworts angefordert.

Bitte klicken Sie auf den untenstehenden Button, um ein neues Passwort für Ihr Konto festzulegen:

`; await sendMail({ to: email, subject: 'Passwort zurücksetzen – CASPOS Shop', text: `Hallo,\n\nder Administrator hat das Zurücksetzen Ihres Passworts angefordert.\n\nBitte klicken Sie auf den folgenden Link, um ein neues Passwort festzulegen:\n${resetLink}\n\nViele Grüße,\nIhr CASPOS Shop-Team`, html: getBeautifulEmailHtml('Passwort zurücksetzen', messageHtml, 'Passwort zurücksetzen', resetLink), }); } export async function getPartners() { const admin = createAdminClient(); const { data: { users }, error } = await admin.auth.admin.listUsers(); if (error) throw error; const { data: dbUsers, error: dbError } = await admin .from('users') .select('id, role, company_id, companies(id, name)') .in('role', ['partner', 'gesperrt']); if (dbError) throw dbError; const userMap: Record = {}; dbUsers.forEach((u: any) => { userMap[u.id] = { role: u.role, company_id: u.company_id || null, company_name: Array.isArray(u.companies) ? u.companies[0]?.name || null : u.companies?.name || null, }; }); const partnerIds = new Set(dbUsers.map((u: any) => u.id)); return users .filter((u) => partnerIds.has(u.id)) .map((u) => ({ ...u, role: userMap[u.id]?.role || 'partner', company_id: userMap[u.id]?.company_id || null, company_name: userMap[u.id]?.company_name || null, })); } export async function updateUserProfile(id: string, data: { first_name: string | null; last_name: string | null }) { const admin = createAdminClient(); const { error } = await admin .from('profiles') .upsert({ id, first_name: data.first_name, last_name: data.last_name }, { onConflict: 'id' }); if (error) throw error; revalidatePath('/admin/users'); } function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) { return `

${title}

${messageHtml} ${buttonText && buttonUrl ? `

Falls der Button nicht funktioniert, kopieren Sie bitte diesen Link in Ihren Browser:
${buttonUrl}

` : ''}

Dies ist eine automatisch generierte E-Mail.
© ${new Date().getFullYear()} CASPOS Shop. Alle Rechte vorbehalten.

`; }