From 87aa7a78216026a4d29aa8367cc0df83c1ac6f8f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 18:26:59 +0000 Subject: [PATCH] Portal-Login: Mitarbeiter kommen ins Admin-Panel, Admin-Label bleibt WOMS-Rolle Bisher liess /api/auth/login nur Appwrite-User mit dem Label "admin" in die Admin-Ansicht; alle anderen fielen in den Kunden-Login und bekamen "Kein Kundenkonto fuer diesen Login gefunden". Mitarbeiter ohne Admin-Haken (z.B. nico@webklar.com) kamen damit nicht auf /admin.html. Jetzt gilt: Wer in der employees-Collection steht, ist Backoffice-Nutzer. Das Label "admin" ist nur noch die WOMS-Adminrolle (Mitarbeiterverwaltung) und wird als isAdmin-Flag in der Portal-Session bzw. in den Preview-Token-Labels mitgefuehrt - inklusive "Als Kunde ansehen" und zurueck. Co-Authored-By: Claude Opus 5 --- server/routes/auth.js | 63 +++++++++++++++++++++++++------- server/routes/portalAdmin.js | 2 + server/services/appwriteAdmin.js | 18 +++++++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/server/routes/auth.js b/server/routes/auth.js index 9b74ff0..3dd14ac 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -4,6 +4,8 @@ import { getCustomerByAppwriteUserId, getCustomerByEmail, getDocument, + getEmployeeByEmail, + getEmployeeByUserId, getPortalAccessByCustomerId, getUserById, listDocuments, @@ -40,6 +42,32 @@ function sanitizeCustomer(customer) { } } +/** + * Backoffice-Login: Jeder Mitarbeiter aus der employees-Collection darf ins + * Admin-Panel (/admin.html). Das Appwrite-Label "admin" ist ausschliesslich die + * WOMS-Adminrolle (Mitarbeiterverwaltung) und KEINE Voraussetzung fuer den + * Portalzugang - es wird nur als Flag in der Session mitgefuehrt. + * Gibt null zurueck, wenn der Login kein Mitarbeiter ist (dann greift der + * normale Kunden-Login). + */ +async function resolveStaffAccount(appwriteUserId, email) { + const account = await getUserById(appwriteUserId).catch(() => null) + const isAdmin = Array.isArray(account?.labels) && account.labels.includes('admin') + + let employee = await getEmployeeByUserId(appwriteUserId).catch(() => null) + if (!employee) { + employee = await getEmployeeByEmail(account?.email || email).catch(() => null) + } + + if (!employee && !isAdmin) return null + + return { + isAdmin, + name: employee?.displayName || account?.name || 'Mitarbeiter', + email: account?.email || employee?.email || email, + } +} + async function validatePortalAccess(appwriteUserId, email) { let customer = await getCustomerByAppwriteUserId(appwriteUserId) if (!customer && email) { @@ -149,28 +177,31 @@ router.post('/login', async (req, res) => { try { const user = await loginWithAppwrite(email.trim(), password) - // Ticketsystem-Admins (Appwrite-Label "admin") bekommen die Admin-Ansicht - const account = await getUserById(user.$id).catch(() => null) - if (Array.isArray(account?.labels) && account.labels.includes('admin')) { - const adminName = account.name || 'Admin' - const adminEmail = account.email || email.trim() + // Ticketsystem-Mitarbeiter (employees-Collection) bekommen die Admin-Ansicht + const staff = await resolveStaffAccount(user.$id, email.trim()) + if (staff) { setPortalSession(res, { role: 'admin', + isAdmin: staff.isAdmin, appwriteUserId: user.$id, - name: adminName, - email: adminEmail, + name: staff.name, + email: staff.email, }) - const adminPreviewToken = await createPreviewSessionToken({ + const staffPreviewToken = await createPreviewSessionToken({ userId: user.$id, customerId: '', - email: adminEmail, - name: adminName, + email: staff.email, + name: staff.name, role: 'admin', - labels: ['admin'], + labels: staff.isAdmin ? ['admin'] : [], authSource: 'kundenbereich-admin', }) - res.cookie(PREVIEW_COOKIE_NAME, adminPreviewToken, previewSessionCookieOptions()) - return res.json({ success: true, role: 'admin', admin: { name: adminName, email: adminEmail } }) + res.cookie(PREVIEW_COOKIE_NAME, staffPreviewToken, previewSessionCookieOptions()) + return res.json({ + success: true, + role: 'admin', + admin: { name: staff.name, email: staff.email, isAdmin: staff.isAdmin }, + }) } let { customer, portalAccess } = await validatePortalAccess(user.$id, email.trim()) @@ -324,6 +355,9 @@ router.get('/me', async (req, res) => { // Admin-Session (Ticketsystem-Mitarbeiter), optional im "Als Kunde ansehen"-Modus if (session.role === 'admin' && session.appwriteUserId) { + // Sessions von vor der Mitarbeiter-Freigabe kennen das Flag nicht - die + // konnten nur Admins bekommen, also als Admin werten. + const isAdmin = session.isAdmin === undefined ? true : Boolean(session.isAdmin) let customer = null if (session.customerId) { const doc = await getDocument(config.collections.customers, session.customerId).catch(() => null) @@ -335,7 +369,7 @@ router.get('/me', async (req, res) => { email: session.adminEmail || session.email || '', name: session.adminName || session.name || 'Admin', role: 'admin', - labels: ['admin'], + labels: isAdmin ? ['admin'] : [], authSource: 'kundenbereich-admin', }) return res.json({ @@ -345,6 +379,7 @@ router.get('/me', async (req, res) => { admin: { name: session.adminName || session.name || 'Admin', email: session.adminEmail || session.email || '', + isAdmin, }, customer, }) diff --git a/server/routes/portalAdmin.js b/server/routes/portalAdmin.js index 9f23c3b..df9af7f 100644 --- a/server/routes/portalAdmin.js +++ b/server/routes/portalAdmin.js @@ -408,6 +408,7 @@ router.post('/view-as/:customerId', async (req, res) => { setPortalSession(res, { role: 'admin', viewAs: true, + isAdmin: session.isAdmin, appwriteUserId: session.appwriteUserId, adminName: session.adminName || session.name || 'Admin', adminEmail: session.adminEmail || session.email || '', @@ -425,6 +426,7 @@ router.post('/exit-view-as', (req, res) => { const session = parsePortalSession(req) || req.session setPortalSession(res, { role: 'admin', + isAdmin: session.isAdmin, appwriteUserId: session.appwriteUserId, name: session.adminName || session.name || 'Admin', email: session.adminEmail || session.email || '', diff --git a/server/services/appwriteAdmin.js b/server/services/appwriteAdmin.js index 2e04bdc..4b88c61 100644 --- a/server/services/appwriteAdmin.js +++ b/server/services/appwriteAdmin.js @@ -162,6 +162,24 @@ export async function getCustomerByEmail(email) { return docs[0] || null } +export async function getEmployeeByUserId(userId) { + if (!userId) return null + const docs = await listDocuments(config.collections.employees, [ + Query.equal('userId', userId), + Query.limit(1), + ]) + return docs[0] || null +} + +export async function getEmployeeByEmail(email) { + if (!email) return null + const docs = await listDocuments(config.collections.employees, [ + Query.equal('email', email.trim().toLowerCase()), + Query.limit(1), + ]) + return docs[0] || null +} + export async function getPortalAccessByCustomerId(customerId) { const docs = await listDocuments(config.collections.customerPortalAccess, [ Query.equal('customerId', customerId),