Unterschriftenservice, Extern-Modus, Adressmodell, Alt-Mail, Team-Mails
skrift-app (Konfigurator): - Datei-Produkte (Unterschriftenservice): Mehrfach-Upload (PDF/Word/Excel), komplett preisfrei (keine Preisleiste/Aufpreise/Kostenübersicht/Zahlung) - Extern-/Etsy-Modus (?extern=1&versand&kuvert&beschriftung&menge): vom Betreiber vorkonfiguriert, Kunde füllt nur Empfänger/Text/Rechnungsdaten/Übersicht, keine Preise/Zahlung; Menge greift immer (auch >25 / ohne Empfänger); eigener Danke-Text - Adressmodell: zwei feste Adressen (Rechnung/Lieferung) ohne Namen statt Dropdown; Auto-Vorbefüllung; Lieferadresse nur bei Sammelversand; abweichende Lieferadresse per Default aus; Pflichtfelder erzwungen - Platzhalter-Tabelle immer im Text-Schritt (auch Sammelversand ohne Kuvert) + Excel; Tabellen-Eingabe nur bis 25, „Ab 26"-Hinweis entfernt - Stammdaten: zwei Adressblöcke + Erfolgsmeldung Directus/Backend: - Signatur-Endpunkte /signature-preview + /signature-render (Proxy zum signature-service, Marker + Konto-Offsets), Modul-Reiter „Unterschriften" (aus Auftrag oder manuell), Tabelle über volle Breite - Generierung läuft bei jeder Bestellung; Kuverts immer (Datei-Produkt: nur Kuvert, DIN Lang als Default); source_files am Auftrag - Produktions-Board: „Dateien"-Button (hochgeladene Originale, keine SVGs) - Alt-Mail-Login: Zweit-Mails melden im selben Konto an (skrift-auth) - Team-Mails schlank + konfigurierbar (email_settings.team_email) für Bestellung/Muster/Anfrage; keine Bestellbestätigung bei Datei-Produkten, separate Vorlagen unterschriften_eingang/extern_eingang - Rechnungsadresse wird ins Konto übernommen, wenn dort noch keine steht - Kunden-Policy: directus_files create/read (Upload); Bootstrap: users billing_address/shipping_address/alt_emails, orders.source_files/source=extern, signatures marker/offsets, unterschriftenservice input_mode=datei/pricing_mode=formel Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,14 +43,26 @@ const handler = (router, { services, getSchema, env, logger }) => {
|
||||
|
||||
const ctx = async () => ({ schema: await getSchema(), accountability: null });
|
||||
|
||||
// Findet das Konto per primärer E-Mail ODER per zusätzlicher Login-Mail
|
||||
// (alt_emails). So melden sich Zweit-Mails im SELBEN Konto an.
|
||||
async function findUserByEmail(email, schema) {
|
||||
const users = new UsersService({ schema, accountability: null });
|
||||
const found = await users.readByQuery({
|
||||
filter: { email: { _eq: email } },
|
||||
fields: ['id', 'email', 'status'],
|
||||
limit: 1,
|
||||
});
|
||||
return found?.[0] || null;
|
||||
const felder = ['id', 'email', 'status', 'alt_emails'];
|
||||
const primaer = await users.readByQuery({ filter: { email: { _eq: email } }, fields: felder, limit: 1 });
|
||||
if (primaer?.[0]) return primaer[0];
|
||||
// Zusätzliche Login-Mails: JSON-Feld ist DB-abhängig schwer filterbar, daher
|
||||
// die (wenigen) Konten mit alt_emails laden und exakt in JS prüfen.
|
||||
try {
|
||||
const cand = await users.readByQuery({ filter: { alt_emails: { _nnull: true } }, fields: felder, limit: -1 });
|
||||
const treffer = (cand || []).find((u) => {
|
||||
const liste = Array.isArray(u.alt_emails)
|
||||
? u.alt_emails
|
||||
: (typeof u.alt_emails === 'string' ? (() => { try { return JSON.parse(u.alt_emails); } catch { return []; } })() : []);
|
||||
return Array.isArray(liste) && liste.map((x) => norm(x)).includes(email);
|
||||
});
|
||||
if (treffer) return treffer;
|
||||
} catch { /* alt_emails evtl. noch nicht vorhanden → ignorieren */ }
|
||||
return null;
|
||||
}
|
||||
|
||||
async function sendCodeMail(email, code) {
|
||||
@@ -173,6 +185,11 @@ const handler = (router, { services, getSchema, env, logger }) => {
|
||||
if (!user || user.status !== 'active')
|
||||
return res.status(401).json({ error: 'Kein aktives Konto zu dieser Adresse.' });
|
||||
|
||||
// WICHTIG: Directus kennt nur die PRIMÄRE E-Mail als Login. Wurde der Code
|
||||
// an eine Zweit-Mail geschickt, melden wir dennoch mit der primären an –
|
||||
// dadurch landet die Zweit-Mail im selben Konto (voller Zugriff).
|
||||
const loginEmail = norm(user.email);
|
||||
|
||||
// Offizieller Login-Pfad mit rotierendem Zufallspasswort (s. Kopfkommentar).
|
||||
const users = new UsersService({ schema, accountability: null });
|
||||
const tempPassword = tempPasswort();
|
||||
@@ -183,9 +200,9 @@ const handler = (router, { services, getSchema, env, logger }) => {
|
||||
// curl funktioniert – daher zuerst diese Variante, dann Fallbacks.
|
||||
const auth = new AuthenticationService({ schema, accountability: null });
|
||||
const versuche = [
|
||||
() => auth.login('default', { email, password: tempPassword }),
|
||||
() => auth.login({ email, password: tempPassword }),
|
||||
() => auth.login({ email, password: tempPassword }, {}),
|
||||
() => auth.login('default', { email: loginEmail, password: tempPassword }),
|
||||
() => auth.login({ email: loginEmail, password: tempPassword }),
|
||||
() => auth.login({ email: loginEmail, password: tempPassword }, {}),
|
||||
];
|
||||
let result, letzterFehler;
|
||||
for (const versuch of versuche) {
|
||||
|
||||
Reference in New Issue
Block a user