[split-required] [guardrail-change] Enforce 500 LOC budget across all services

Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-27 00:09:30 +02:00
parent 5ef039a6bc
commit 92c86ec6ba
162 changed files with 23853 additions and 23034 deletions

View File

@@ -3,7 +3,6 @@ package services
import (
"bytes"
"fmt"
"html/template"
"net/smtp"
"strings"
)
@@ -249,306 +248,3 @@ Ihr BreakPilot Team`, getDisplayName(name), appLink)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// renderTemplate renders an email HTML template
func (s *EmailService) renderTemplate(templateName string, data map[string]interface{}) string {
templates := map[string]string{
"verification": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>Willkommen bei BreakPilot!</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<p>Vielen Dank für Ihre Registrierung! Bitte bestätigen Sie Ihre E-Mail-Adresse, um Ihr Konto zu aktivieren.</p>
<p style="text-align: center;">
<a href="{{.VerifyLink}}" class="button">E-Mail bestätigen</a>
</p>
<p>Dieser Link ist 24 Stunden gültig.</p>
<p>Falls Sie sich nicht bei BreakPilot registriert haben, können Sie diese E-Mail ignorieren.</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"password_reset": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.warning { background: #fef3c7; border-left: 4px solid #f59e0b; padding: 12px; margin: 20px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>Passwort zurücksetzen</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<p>Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt.</p>
<p style="text-align: center;">
<a href="{{.ResetLink}}" class="button">Passwort zurücksetzen</a>
</p>
<div class="warning">
<strong>Hinweis:</strong> Dieser Link ist nur 1 Stunde gültig.
</div>
<p>Falls Sie keine Passwort-Zurücksetzung angefordert haben, können Sie diese E-Mail ignorieren.</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"new_version": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.info-box { background: #e0e7ff; border-left: 4px solid #6366f1; padding: 12px; margin: 20px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>Neue Version: {{.DocumentName}}</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<p>Wir haben unsere <strong>{{.DocumentName}}</strong> aktualisiert.</p>
<div class="info-box">
<strong>Wichtig:</strong> Bitte bestätigen Sie die neuen Bedingungen innerhalb der nächsten <strong>{{.DeadlineDays}} Tage</strong>.
</div>
<p style="text-align: center;">
<a href="{{.ConsentLink}}" class="button">Dokument ansehen & bestätigen</a>
</p>
<p>Falls Sie nicht innerhalb dieser Frist bestätigen, wird Ihr Account vorübergehend gesperrt.</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"reminder": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #f59e0b, #d97706); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #f59e0b; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.warning { background: #fef3c7; border-left: 4px solid #f59e0b; padding: 12px; margin: 20px 0; }
.doc-list { background: white; padding: 15px; border-radius: 8px; margin: 15px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>{{.Urgency}}: Ausstehende Bestätigungen</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<p>Dies ist eine freundliche Erinnerung, dass Sie noch ausstehende rechtliche Dokumente bestätigen müssen.</p>
<div class="doc-list">
<strong>Ausstehende Dokumente:</strong>
<ul>
{{range .Documents}}<li>{{.}}</li>{{end}}
</ul>
</div>
<div class="warning">
<strong>Sie haben noch {{.DaysLeft}} Tage Zeit.</strong> Nach Ablauf dieser Frist wird Ihr Account vorübergehend gesperrt.
</div>
<p style="text-align: center;">
<a href="{{.ConsentLink}}" class="button">Jetzt bestätigen</a>
</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"suspended": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #ef4444, #dc2626); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.alert { background: #fee2e2; border-left: 4px solid #ef4444; padding: 12px; margin: 20px 0; }
.doc-list { background: white; padding: 15px; border-radius: 8px; margin: 15px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>Account vorübergehend gesperrt</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<div class="alert">
<strong>Ihr Account wurde vorübergehend gesperrt</strong>, da Sie die folgenden rechtlichen Dokumente nicht innerhalb der Frist bestätigt haben.
</div>
<div class="doc-list">
<strong>Nicht bestätigte Dokumente:</strong>
<ul>
{{range .Documents}}<li>{{.}}</li>{{end}}
</ul>
</div>
<p>Um Ihren Account zu entsperren, bestätigen Sie bitte alle ausstehenden Dokumente:</p>
<p style="text-align: center;">
<a href="{{.ConsentLink}}" class="button">Dokumente bestätigen & Account entsperren</a>
</p>
<p>Sobald Sie alle Dokumente bestätigt haben, wird Ihr Account automatisch entsperrt.</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"reactivated": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #22c55e, #16a34a); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #22c55e; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.success { background: #dcfce7; border-left: 4px solid #22c55e; padding: 12px; margin: 20px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>Account wieder aktiviert!</h1>
</div>
<div class="content">
<p>Hallo {{.Name}},</p>
<div class="success">
<strong>Vielen Dank!</strong> Ihr Account wurde erfolgreich wieder aktiviert.
</div>
<p>Sie können BreakPilot ab sofort wieder wie gewohnt nutzen.</p>
<p style="text-align: center;">
<a href="{{.AppLink}}" class="button">Zu BreakPilot</a>
</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
"generic_notification": `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; padding: 30px; border-radius: 10px 10px 0 0; text-align: center; }
.content { background: #f8fafc; padding: 30px; border-radius: 0 0 10px 10px; }
.button { display: inline-block; background: #6366f1; color: white; padding: 14px 28px; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 20px 0; }
.footer { text-align: center; color: #64748b; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="header">
<h1>{{.Title}}</h1>
</div>
<div class="content">
<p>{{.Body}}</p>
<p style="text-align: center;">
<a href="{{.BaseURL}}/app" class="button">Zu BreakPilot</a>
</p>
</div>
<div class="footer">
<p>© 2024 BreakPilot. Alle Rechte vorbehalten.</p>
</div>
</body>
</html>`,
}
tmplStr, ok := templates[templateName]
if !ok {
return ""
}
tmpl, err := template.New(templateName).Parse(tmplStr)
if err != nil {
return ""
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return ""
}
return buf.String()
}
// SendConsentReminderEmail sends a simplified consent reminder email
func (s *EmailService) SendConsentReminderEmail(to, title, body string) error {
subject := title
htmlBody := s.renderTemplate("generic_notification", map[string]interface{}{
"Title": title,
"Body": body,
"BaseURL": s.config.BaseURL,
})
return s.SendEmail(to, subject, htmlBody, body)
}
// SendGenericNotificationEmail sends a generic notification email
func (s *EmailService) SendGenericNotificationEmail(to, title, body string) error {
subject := title
htmlBody := s.renderTemplate("generic_notification", map[string]interface{}{
"Title": title,
"Body": body,
"BaseURL": s.config.BaseURL,
})
return s.SendEmail(to, subject, htmlBody, body)
}
// getDisplayName returns display name or fallback
func getDisplayName(name string) string {
if name != "" {
return name
}
return "Nutzer"
}