fix(users): refactor user admin actions to return status objects instead of throwing to prevent production error obfuscation
All checks were successful
Staging Build / build (push) Successful in 3m29s

This commit is contained in:
DanielS
2026-07-03 13:51:00 +02:00
parent 12d06e46ad
commit 38651037bc
3 changed files with 154 additions and 94 deletions

View File

@@ -50,64 +50,77 @@ export async function getUsers() {
}
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',
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 (!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',
`
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-top: 0;">Hallo,</p>
<p style="color: #475569; font-size: 16px; line-height: 1.6;">für Sie wurde erfolgreich ein neues Benutzerkonto auf unserer Plattform eingerichtet.</p>
<p style="color: #475569; font-size: 16px; line-height: 1.6;">Bitte klicken Sie auf den untenstehenden Button, um Ihr Passwort festzulegen und Ihr Konto zu aktivieren:</p>
`,
'Passwort festlegen',
setupLink
),
});
}
if (error) {
return { success: false, error: error.message };
}
if (!user) {
return { success: false, error: 'Benutzer konnte nicht erstellt werden.' };
}
} catch (mailError) {
console.error('Failed to send welcome setup mail:', mailError);
}
revalidatePath('/admin/users');
return user;
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) {
return { success: false, error: dbError.message };
}
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) {
return { success: false, error: profileError.message };
}
// 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',
`
<p style="color: #475569; font-size: 16px; line-height: 1.6; margin-top: 0;">Hallo,</p>
<p style="color: #475569; font-size: 16px; line-height: 1.6;">für Sie wurde erfolgreich ein neues Benutzerkonto auf unserer Plattform eingerichtet.</p>
<p style="color: #475569; font-size: 16px; line-height: 1.6;">Bitte klicken Sie auf den untenstehenden Button, um Ihr Passwort festzulegen und Ihr Konto zu aktivieren:</p>
`,
'Passwort festlegen',
setupLink
),
});
}
}
} catch (mailError) {
console.error('Failed to send welcome setup mail:', mailError);
}
revalidatePath('/admin/users');
return { success: true, user };
} catch (err: any) {
console.error('Error creating user in action:', err);
return { success: false, error: err.message || 'Ein unerwarteter Fehler ist aufgetreten.' };
}
}
export async function deleteUser(id: string) {
@@ -125,35 +138,44 @@ export async function deleteUser(id: string) {
}
if (count && count > 0) {
throw new Error('Auf diesen User gibt es bereits Belege. Sperren möglich.');
return { success: false, 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}`);
return { success: false, 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}`);
return { success: false, error: `Auth-Fehler beim Löschen des Benutzers: ${error.message}` };
}
revalidatePath('/admin/users');
return { success: true };
} catch (err: any) {
console.error('deleteUser Server Action failed:', err);
throw new Error(err.message || 'Benutzer konnte nicht gelöscht werden.');
return { success: false, 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');
try {
const admin = createAdminClient();
const { error } = await admin
.from('users')
.upsert({ id, role }, { onConflict: 'id' });
if (error) {
return { success: false, error: error.message };
}
revalidatePath('/admin/users');
return { success: true };
} catch (err: any) {
console.error('updateUserRole Server Action failed:', err);
return { success: false, error: err.message || 'Rolle konnte nicht aktualisiert werden.' };
}
}
export async function updateUserStatus(id: string, ban: boolean) {
@@ -224,12 +246,20 @@ export async function getPartners() {
}
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');
try {
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) {
return { success: false, error: error.message };
}
revalidatePath('/admin/users');
return { success: true };
} catch (err: any) {
console.error('updateUserProfile Server Action failed:', err);
return { success: false, error: err.message || 'Profil konnte nicht aktualisiert werden.' };
}
}
function getBeautifulEmailHtml(title: string, messageHtml: string, buttonText?: string, buttonUrl?: string) {