feat(branding): add global dynamic theme system and fix 2fa mail

This commit is contained in:
DanielS
2026-08-14 15:57:29 +02:00
parent 009b167cbd
commit 9ecdc645e8
22 changed files with 1764 additions and 313 deletions

View File

@@ -2,6 +2,7 @@
import { createAdminClient } from '@/lib/supabase/admin'
import { revalidatePath } from 'next/cache'
import nodemailer from 'nodemailer'
export type AdminSetupData = {
email: string
@@ -11,6 +12,19 @@ export type AdminSetupData = {
lastName: string
}
export type BrandingSetupData = {
street: string
zip: string
city: string
billingStreet: string
billingZip: string
billingCity: string
sameBillingAddress: boolean
colorScheme: string
primaryColor: string
accentColor: string
}
export type SmtpSetupData = {
host: string
port: number
@@ -32,26 +46,21 @@ export async function isSetupNeeded(): Promise<boolean> {
perPage: 1
})
if (authError) {
console.error('Error checking auth users list:', authError)
if (!authError && authData && authData.users && authData.users.length > 0) {
// Mindestens ein Auth-Benutzer vorhanden -> Setup ist beendet!
return false
}
if (authData.users.length > 0) {
return false
}
// 2. Check if any user exists in public.users table
// 2. Zusatzprüfung public.users Tabelle
const { count, error: dbError } = await admin
.from('users')
.select('*', { count: 'exact', head: true })
.select('id', { count: 'exact' })
if (dbError) {
console.error('Error checking users table status:', dbError)
if (!dbError && typeof count === 'number' && count > 0) {
return false
}
return count === 0
return true
} catch (e) {
console.error('Exception checking setup status:', e)
return false
@@ -60,14 +69,14 @@ export async function isSetupNeeded(): Promise<boolean> {
/**
* Completes the initial setup by creating the first admin user,
* updating their role and profile, and storing SMTP settings.
* updating their role and profile, and storing branding & SMTP settings.
*/
export async function completeSetup(
adminData: AdminSetupData,
brandingData: BrandingSetupData,
smtpData: SmtpSetupData
): Promise<{ success: boolean; error?: string }> {
try {
// 1. Double check if setup is actually needed to prevent double runs
const needed = await isSetupNeeded()
if (!needed) {
return { success: false, error: 'Setup bereits abgeschlossen.' }
@@ -75,7 +84,7 @@ export async function completeSetup(
const admin = createAdminClient()
// 2. Create user in Supabase Auth
// 1. Create user in Supabase Auth
const { data: authData, error: authError } = await admin.auth.admin.createUser({
email: adminData.email,
password: adminData.password,
@@ -88,17 +97,20 @@ export async function completeSetup(
const userId = authData.user.id
// 3. Update user role to admin in public.users
// 2. Set user role to admin in public.users using Service Role Client
const { error: roleError } = await admin
.from('users')
.update({ role: 'admin' })
.eq('id', userId)
.upsert({
id: userId,
role: 'admin',
}, { onConflict: 'id' })
if (roleError) {
console.error('Error setting user role to admin:', roleError)
return { success: false, error: `Fehler beim Zuweisen der Admin-Rolle: ${roleError.message}` }
}
// 4. Update company and name details in public.profiles
// 3. Update company and name details in public.profiles
const { error: profileError } = await admin
.from('profiles')
.update({
@@ -113,22 +125,46 @@ export async function completeSetup(
console.error('Error updating admin profile:', profileError)
}
// 5. Store SMTP configuration in public.settings
const { error: smtpError } = await admin
// 4. Store Branding settings in public.settings (id = 'branding')
const { error: brandingError } = await admin
.from('settings')
.upsert({
id: 'smtp',
host: smtpData.host,
port: smtpData.port,
secure: smtpData.secure,
user: smtpData.user,
pass: smtpData.pass,
id: 'branding',
company_name: adminData.companyName,
street: brandingData.street,
zip: brandingData.zip,
city: brandingData.city,
billing_street: brandingData.sameBillingAddress ? brandingData.street : brandingData.billingStreet,
billing_zip: brandingData.sameBillingAddress ? brandingData.zip : brandingData.billingZip,
billing_city: brandingData.sameBillingAddress ? brandingData.city : brandingData.billingCity,
same_billing_address: brandingData.sameBillingAddress,
color_scheme: brandingData.colorScheme,
primary_color: brandingData.primaryColor,
accent_color: brandingData.accentColor,
updated_at: new Date().toISOString(),
})
if (smtpError) {
console.error('Error saving SMTP settings:', smtpError)
return { success: false, error: `Fehler beim Speichern der SMTP-Einstellungen: ${smtpError.message}` }
if (brandingError) {
console.error('Error saving branding settings:', brandingError)
}
// 5. Store SMTP configuration in public.settings (id = 'smtp')
if (smtpData.host && smtpData.host.trim().length > 0) {
const { error: smtpError } = await admin
.from('settings')
.upsert({
id: 'smtp',
host: smtpData.host,
port: smtpData.port,
secure: smtpData.secure,
user: smtpData.user,
pass: smtpData.pass,
updated_at: new Date().toISOString(),
})
if (smtpError) {
console.error('Error saving SMTP settings:', smtpError)
}
}
revalidatePath('/')
@@ -138,3 +174,46 @@ export async function completeSetup(
return { success: false, error: e.message || 'Unerwarteter Fehler beim Setup.' }
}
}
export async function testSmtpConfig(
smtpData: SmtpSetupData,
recipient: string
): Promise<{ success: boolean; message: string }> {
try {
if (!smtpData.host || !smtpData.user) {
return { success: false, message: 'Bitte Host und Benutzername ausfüllen.' }
}
const transporter = nodemailer.createTransport({
host: smtpData.host,
port: Number(smtpData.port) || 587,
secure: smtpData.secure,
auth: {
user: smtpData.user,
pass: smtpData.pass || '',
},
tls: {
rejectUnauthorized: false,
},
})
const info = await transporter.sendMail({
from: smtpData.user,
to: recipient || smtpData.user,
subject: 'CASPOS Setup Test-E-Mail',
text: 'Diese Test-E-Mail wurde erfolgreich während des Webshop-Setups versendet.',
html: '<b>Diese Test-E-Mail wurde erfolgreich während des Webshop-Setups versendet.</b>',
})
return {
success: true,
message: `Test-E-Mail erfolgreich gesendet an ${recipient || smtpData.user}! (ID: ${info.messageId})`,
}
} catch (err: any) {
console.error('SMTP test error:', err)
return {
success: false,
message: `SMTP-Fehler: ${err.message || err}`,
}
}
}