171 lines
6.1 KiB
JavaScript
171 lines
6.1 KiB
JavaScript
import { verifyToken, getUserFromToken, hasRole } from '../utils/auth.js'
|
|
import { readMembers } from '../utils/members.js'
|
|
import { readUsers, migrateUserRoles } from '../utils/auth.js'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
const token = getCookie(event, 'auth_token')
|
|
|
|
if (!token) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Nicht authentifiziert.'
|
|
})
|
|
}
|
|
|
|
const decoded = verifyToken(token)
|
|
|
|
if (!decoded) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Ungültiges Token.'
|
|
})
|
|
}
|
|
|
|
const currentUser = await getUserFromToken(token)
|
|
|
|
// Get manual members and registered users
|
|
const manualMembers = await readMembers()
|
|
const registeredUsers = await readUsers()
|
|
|
|
// Merge members: combine manual + registered, detect duplicates
|
|
const mergedMembers = []
|
|
|
|
// Create lookup maps for O(1) matching instead of O(n) findIndex
|
|
const emailToIndexMap = new Map() // email -> index in mergedMembers
|
|
const nameToIndexMap = new Map() // name -> index in mergedMembers
|
|
|
|
// First, add all manual members and build lookup maps
|
|
for (let i = 0; i < manualMembers.length; i++) {
|
|
const member = manualMembers[i]
|
|
const normalizedEmail = member.email?.toLowerCase().trim() || ''
|
|
const fullName = `${member.firstName || ''} ${member.lastName || ''}`.trim()
|
|
const normalizedName = fullName.toLowerCase()
|
|
|
|
const memberIndex = mergedMembers.length
|
|
mergedMembers.push({
|
|
...member,
|
|
name: fullName, // Computed for display
|
|
source: 'manual',
|
|
editable: true,
|
|
hasLogin: false
|
|
})
|
|
|
|
// Build lookup maps (only for manual members)
|
|
if (normalizedEmail) {
|
|
// Only add if not already present (prefer first occurrence)
|
|
if (!emailToIndexMap.has(normalizedEmail)) {
|
|
emailToIndexMap.set(normalizedEmail, memberIndex)
|
|
}
|
|
}
|
|
if (normalizedName) {
|
|
// Only add if not already present (prefer first occurrence)
|
|
if (!nameToIndexMap.has(normalizedName)) {
|
|
nameToIndexMap.set(normalizedName, memberIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then add registered users (only active ones)
|
|
for (const user of registeredUsers) {
|
|
if (!user.active) continue
|
|
|
|
const normalizedEmail = user.email?.toLowerCase().trim() || ''
|
|
const normalizedName = user.name?.toLowerCase().trim() || ''
|
|
|
|
// Check if this user matches an existing manual member using O(1) lookup
|
|
let matchedManualIndex = -1
|
|
|
|
// Try to match by email first (O(1) lookup)
|
|
if (normalizedEmail && emailToIndexMap.has(normalizedEmail)) {
|
|
matchedManualIndex = emailToIndexMap.get(normalizedEmail)
|
|
// Verify it's still a manual member (safety check)
|
|
if (mergedMembers[matchedManualIndex]?.source !== 'manual') {
|
|
matchedManualIndex = -1
|
|
}
|
|
}
|
|
|
|
// If no email match, try name (O(1) lookup)
|
|
if (matchedManualIndex === -1 && normalizedName && nameToIndexMap.has(normalizedName)) {
|
|
matchedManualIndex = nameToIndexMap.get(normalizedName)
|
|
// Verify it's still a manual member and email doesn't conflict (safety check)
|
|
const candidate = mergedMembers[matchedManualIndex]
|
|
if (candidate?.source === 'manual') {
|
|
// Additional safety: if candidate has email, make sure it doesn't conflict
|
|
const candidateEmail = candidate.email?.toLowerCase().trim() || ''
|
|
if (!candidateEmail || candidateEmail === normalizedEmail) {
|
|
// Safe to match by name
|
|
} else {
|
|
// Email mismatch - don't match by name alone
|
|
matchedManualIndex = -1
|
|
}
|
|
} else {
|
|
matchedManualIndex = -1
|
|
}
|
|
}
|
|
|
|
if (matchedManualIndex !== -1) {
|
|
// Merge with existing manual member
|
|
const migratedUser = migrateUserRoles({ ...user })
|
|
const roles = Array.isArray(migratedUser.roles) ? migratedUser.roles : (migratedUser.role ? [migratedUser.role] : ['mitglied'])
|
|
mergedMembers[matchedManualIndex] = {
|
|
...mergedMembers[matchedManualIndex],
|
|
hasLogin: true,
|
|
loginEmail: user.email,
|
|
loginRoles: roles,
|
|
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
|
lastLogin: user.lastLogin,
|
|
isMannschaftsspieler: user.isMannschaftsspieler === true || mergedMembers[matchedManualIndex].isMannschaftsspieler === true
|
|
}
|
|
} else {
|
|
// Add as new member (from login system)
|
|
const migratedUser = migrateUserRoles({ ...user })
|
|
const roles = Array.isArray(migratedUser.roles) ? migratedUser.roles : (migratedUser.role ? [migratedUser.role] : ['mitglied'])
|
|
mergedMembers.push({
|
|
id: user.id,
|
|
name: user.name,
|
|
email: user.email,
|
|
phone: user.phone || '',
|
|
address: '',
|
|
notes: `Rolle(n): ${roles.join(', ')}`,
|
|
source: 'login',
|
|
editable: false,
|
|
hasLogin: true,
|
|
loginEmail: user.email,
|
|
loginRoles: roles,
|
|
loginRole: roles[0] || 'mitglied', // Rückwärtskompatibilität
|
|
lastLogin: user.lastLogin,
|
|
isMannschaftsspieler: user.isMannschaftsspieler === true
|
|
})
|
|
}
|
|
}
|
|
|
|
// Sort by name
|
|
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
// Serverseitiger Datenschutz: nur Vorstands-Mitglieder erhalten volle Kontaktdaten/Logindaten
|
|
const isVorstand = hasRole(currentUser, 'vorstand')
|
|
|
|
// Für nicht-vorstandliche Anfragen liefern wir eine stark reduzierte, nicht-identifizierende
|
|
// Ansicht der Mitgliederliste (nur das Nötigste für öffentliche Anzeigen)
|
|
const safeMembers = isVorstand
|
|
? mergedMembers
|
|
: mergedMembers.map(m => ({
|
|
// Minimale, unkritische Felder
|
|
id: m.id,
|
|
name: m.name,
|
|
source: m.source,
|
|
isMannschaftsspieler: !!m.isMannschaftsspieler
|
|
}))
|
|
|
|
return {
|
|
success: true,
|
|
members: safeMembers
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Abrufen der Mitgliederliste:', error)
|
|
throw error
|
|
}
|
|
})
|
|
|