193 lines
5.4 KiB
JavaScript
193 lines
5.4 KiB
JavaScript
import { verifyToken, getUserById, hasAnyRole } from '../../utils/auth.js'
|
|
import { readMembers, writeMembers, normalizeDate } from '../../utils/members.js'
|
|
import { randomUUID } from 'crypto'
|
|
|
|
// Helper function to check for duplicates in a list (with optional exclude)
|
|
function findDuplicateMemberInList(members, firstName, lastName, geburtsdatum, excludeId = null) {
|
|
const normalizedFirstName = (firstName || '').trim().toLowerCase()
|
|
const normalizedLastName = (lastName || '').trim().toLowerCase()
|
|
const normalizedDate = normalizeDate(geburtsdatum)
|
|
|
|
return members.find(m => {
|
|
if (excludeId && m.id === excludeId) return false
|
|
const mFirstName = (m.firstName || '').trim().toLowerCase()
|
|
const mLastName = (m.lastName || '').trim().toLowerCase()
|
|
const mDate = normalizeDate(m.geburtsdatum)
|
|
|
|
return mFirstName === normalizedFirstName &&
|
|
mLastName === normalizedLastName &&
|
|
mDate === normalizedDate &&
|
|
mDate !== ''
|
|
})
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
// Support both Cookie and Authorization Header
|
|
let token = getCookie(event, 'auth_token')
|
|
|
|
if (!token) {
|
|
const authHeader = getHeader(event, 'authorization')
|
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
token = authHeader.substring(7)
|
|
}
|
|
}
|
|
|
|
if (!token) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen.'
|
|
})
|
|
}
|
|
|
|
const decoded = verifyToken(token)
|
|
|
|
if (!decoded) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Ungültiges Token.'
|
|
})
|
|
}
|
|
|
|
const user = await getUserById(decoded.id)
|
|
|
|
if (!user) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: 'Benutzer nicht gefunden.'
|
|
})
|
|
}
|
|
|
|
// Only admin and vorstand can add members in bulk
|
|
if (!hasAnyRole(user, 'admin', 'vorstand')) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
message: 'Keine Berechtigung zum Bulk-Import von Mitgliedern. Erforderlich: admin oder vorstand Rolle.'
|
|
})
|
|
}
|
|
|
|
const body = await readBody(event)
|
|
const { members: membersToImport } = body
|
|
|
|
if (!Array.isArray(membersToImport) || membersToImport.length === 0) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: 'Bitte senden Sie ein Array von Mitgliedern im Feld "members".'
|
|
})
|
|
}
|
|
|
|
// Validate all members before processing
|
|
const validationErrors = []
|
|
membersToImport.forEach((member, index) => {
|
|
if (!member.firstName || !member.lastName) {
|
|
validationErrors.push(`Zeile ${index + 1}: Vorname und Nachname sind erforderlich.`)
|
|
}
|
|
if (!member.geburtsdatum) {
|
|
validationErrors.push(`Zeile ${index + 1}: Geburtsdatum ist erforderlich.`)
|
|
}
|
|
})
|
|
|
|
if (validationErrors.length > 0) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: `Validierungsfehler:\n${validationErrors.join('\n')}`
|
|
})
|
|
}
|
|
|
|
// Read existing members
|
|
const existingMembers = await readMembers()
|
|
|
|
const results = {
|
|
success: [],
|
|
duplicates: [],
|
|
errors: []
|
|
}
|
|
|
|
// Process each member
|
|
for (let i = 0; i < membersToImport.length; i++) {
|
|
const memberData = membersToImport[i]
|
|
|
|
try {
|
|
// Check for duplicates in existing members
|
|
const duplicateInExisting = findDuplicateMemberInList(
|
|
existingMembers,
|
|
memberData.firstName,
|
|
memberData.lastName,
|
|
memberData.geburtsdatum
|
|
)
|
|
|
|
if (duplicateInExisting) {
|
|
results.duplicates.push({
|
|
index: i + 1,
|
|
member: memberData,
|
|
reason: `Existiert bereits (ID: ${duplicateInExisting.id})`
|
|
})
|
|
continue
|
|
}
|
|
|
|
// Check for duplicates within the import batch
|
|
const duplicateInBatch = findDuplicateMemberInList(
|
|
membersToImport.slice(0, i),
|
|
memberData.firstName,
|
|
memberData.lastName,
|
|
memberData.geburtsdatum
|
|
)
|
|
|
|
if (duplicateInBatch) {
|
|
results.duplicates.push({
|
|
index: i + 1,
|
|
member: memberData,
|
|
reason: 'Duplikat innerhalb des Imports'
|
|
})
|
|
continue
|
|
}
|
|
|
|
// Add new member
|
|
const newMember = {
|
|
...memberData,
|
|
id: memberData.id || randomUUID()
|
|
}
|
|
|
|
existingMembers.push(newMember)
|
|
results.success.push({
|
|
index: i + 1,
|
|
member: newMember
|
|
})
|
|
|
|
} catch (error) {
|
|
results.errors.push({
|
|
index: i + 1,
|
|
member: memberData,
|
|
error: error.message || 'Unbekannter Fehler'
|
|
})
|
|
}
|
|
}
|
|
|
|
// Save all successfully imported members
|
|
if (results.success.length > 0) {
|
|
await writeMembers(existingMembers)
|
|
}
|
|
|
|
return {
|
|
success: results.success.length > 0,
|
|
summary: {
|
|
total: membersToImport.length,
|
|
imported: results.success.length,
|
|
duplicates: results.duplicates.length,
|
|
errors: results.errors.length
|
|
},
|
|
results: results
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Bulk-Import von Mitgliedern:', error)
|
|
if (error.statusCode) {
|
|
throw error
|
|
}
|
|
throw createError({
|
|
statusCode: error.statusCode || 500,
|
|
message: error.message || 'Fehler beim Bulk-Import von Mitgliedern.'
|
|
})
|
|
}
|
|
})
|
|
|