Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 47s
This commit removes the X-Frame-Options header in favor of using Content Security Policy (CSP) with frame-ancestors for better flexibility and modern security practices. It also adds a fallback for frame-ancestors in case CSP is not enabled. Additionally, the JavaScript middleware is updated to reflect these changes, ensuring consistent security header management across the application.
154 lines
5.4 KiB
JavaScript
154 lines
5.4 KiB
JavaScript
import { verifyToken } 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.'
|
|
})
|
|
}
|
|
|
|
// 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))
|
|
|
|
return {
|
|
success: true,
|
|
members: mergedMembers
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Abrufen der Mitgliederliste:', error)
|
|
throw error
|
|
}
|
|
})
|
|
|