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 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-11 18:26:59 +00:00
parent ac264dc9ba
commit 87aa7a7821
3 changed files with 69 additions and 14 deletions

View File

@@ -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,
})

View File

@@ -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 || '',