Rebuild as InnungsApp project: replace stadtwerke analysis with full documentation
- PRD: vollständige Produktspezifikation (5 Module, Scope, Akzeptanzkriterien) - ARCHITECTURE: Tech Stack, Ordnerstruktur, Multi-Tenancy, Push, Kosten - DATABASE_SCHEMA: Vollständiges SQL-Schema mit RLS Policies und Views - USER_STORIES: 40+ Stories nach Rolle (Admin, Mitglied, Azubi, Obermeister) - PERSONAS: 5 detaillierte Nutzerprofile mit Alltag, Zitaten und Erwartungen - BUSINESS_MODEL: Preistabellen, Unit Economics, Revenue-Projektionen, Distribution - ROADMAP: 6 Phasen, Sprint-Planung, Meilensteine und KPIs - COMPETITIVE_ANALYSIS: Wettbewerbsmatrix, USPs, Preispositionierung - API_DESIGN: Supabase Query Patterns, Edge Functions, Realtime Subscriptions - ONBOARDING_FLOWS: 7 User Flows von Setup bis Fehlerfall - GTM_STRATEGY: 3-Phasen-Vertrieb, Outreach-Sequenz, Einwandbehandlung - AZUBI_MODULE: Video-Feed, 1-Click-Apply, Chat, Berichtsheft, Quiz - DSGVO_KONZEPT: Rechtsgrundlagen, TOMs, AVV, Minderjährige, Incident Response - FEATURES_BACKLOG: 72 Features nach MoSCoW + Technische Schulden Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
90
innungsapp/apps/admin/lib/email.ts
Normal file
90
innungsapp/apps/admin/lib/email.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT) || 587,
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
auth:
|
||||
process.env.SMTP_USER
|
||||
? { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }
|
||||
: undefined,
|
||||
})
|
||||
|
||||
export async function sendMagicLinkEmail({
|
||||
to,
|
||||
magicUrl,
|
||||
}: {
|
||||
to: string
|
||||
magicUrl: string
|
||||
}) {
|
||||
await transporter.sendMail({
|
||||
from: process.env.EMAIL_FROM ?? 'noreply@innungsapp.de',
|
||||
to,
|
||||
subject: 'Ihr Login-Link für InnungsApp',
|
||||
html: `
|
||||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||
<div style="background: #E63946; padding: 24px; border-radius: 8px 8px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 24px;">InnungsApp</h1>
|
||||
</div>
|
||||
<div style="background: #fff; padding: 32px; border: 1px solid #e5e7eb; border-top: none; border-radius: 0 0 8px 8px;">
|
||||
<h2 style="color: #111827; margin-top: 0;">Ihr persönlicher Login-Link</h2>
|
||||
<p style="color: #4b5563;">Klicken Sie auf den folgenden Button, um sich einzuloggen. Der Link ist 24 Stunden gültig.</p>
|
||||
<a href="${magicUrl}"
|
||||
style="display: inline-block; background: #E63946; color: white; padding: 12px 24px;
|
||||
border-radius: 6px; text-decoration: none; font-weight: bold; margin: 16px 0;">
|
||||
Jetzt einloggen
|
||||
</a>
|
||||
<p style="color: #9ca3af; font-size: 14px;">
|
||||
Wenn Sie diesen Link nicht angefordert haben, können Sie diese E-Mail ignorieren.
|
||||
</p>
|
||||
<hr style="border-color: #e5e7eb; margin: 24px 0;" />
|
||||
<p style="color: #9ca3af; font-size: 12px; margin: 0;">
|
||||
InnungsApp · Die digitale Plattform für Innungen
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
export async function sendInviteEmail({
|
||||
to,
|
||||
memberName,
|
||||
orgName,
|
||||
apiUrl,
|
||||
}: {
|
||||
to: string
|
||||
memberName: string
|
||||
orgName: string
|
||||
apiUrl: string
|
||||
}) {
|
||||
// Generate magic link for the invite
|
||||
const signInUrl = `${apiUrl}/login?email=${encodeURIComponent(to)}&invited=true`
|
||||
|
||||
await transporter.sendMail({
|
||||
from: process.env.EMAIL_FROM ?? 'noreply@innungsapp.de',
|
||||
to,
|
||||
subject: `Einladung zur InnungsApp — ${orgName}`,
|
||||
html: `
|
||||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||||
<div style="background: #E63946; padding: 24px; border-radius: 8px 8px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 24px;">InnungsApp</h1>
|
||||
</div>
|
||||
<div style="background: #fff; padding: 32px; border: 1px solid #e5e7eb; border-top: none; border-radius: 0 0 8px 8px;">
|
||||
<h2 style="color: #111827; margin-top: 0;">Hallo ${memberName},</h2>
|
||||
<p style="color: #4b5563;">
|
||||
Sie wurden von der <strong>${orgName}</strong> zur InnungsApp eingeladen.
|
||||
InnungsApp ist die digitale Plattform Ihrer Innung für News, Termine und das Mitgliederverzeichnis.
|
||||
</p>
|
||||
<p style="color: #4b5563;">Klicken Sie auf den Button, um Ihren Account zu aktivieren:</p>
|
||||
<a href="${signInUrl}"
|
||||
style="display: inline-block; background: #E63946; color: white; padding: 12px 24px;
|
||||
border-radius: 6px; text-decoration: none; font-weight: bold; margin: 16px 0;">
|
||||
Jetzt Zugang aktivieren
|
||||
</a>
|
||||
<p style="color: #9ca3af; font-size: 14px;">Kein Passwort nötig — Sie erhalten einen sicheren Login-Link per E-Mail.</p>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user