Files
harheimertc/server/api/cms/password-reset-diagnostics.get.js
Torsten Schulz (local) e033d716dd
All checks were successful
Code Analysis and Production Deploy / analyze (push) Successful in 5m23s
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m18s
feat: Add CMS and Member Area screens with ViewModels
- Implemented CmsViewModel to manage CMS data loading and state.
- Created MemberAreaDetailScreens for displaying member information and news.
- Added MembersViewModel and MemberNewsViewModel for managing member data and news.
- Developed MemberAreaScreen to provide navigation and display member-related options.
- Introduced ProfileScreen and ProfileViewModel for user profile management.
- Implemented state management for loading, error handling, and form updates across screens.
2026-05-28 08:01:35 +02:00

95 lines
2.8 KiB
JavaScript

import { getUserFromToken, hasRole, readUsers } from '../../utils/auth.js'
import {
fingerprintResetEmail,
normalizeResetEmail,
PASSWORD_RESET_LOG_RETENTION_HOURS,
readPasswordResetLogs
} from '../../utils/password-reset-log.js'
function summarizeAttempts(entries) {
const attemptsById = new Map()
for (const entry of [...entries].reverse()) {
const attempt = attemptsById.get(entry.requestId) || {
requestId: entry.requestId,
startedAt: entry.ts,
emailMasked: entry.emailMasked,
ip: entry.ip,
userId: entry.userId || null,
steps: [],
failed: false
}
attempt.startedAt = attempt.startedAt || entry.ts
attempt.userId = attempt.userId || entry.userId || null
attempt.steps.push({
ts: entry.ts,
step: entry.step,
status: entry.status,
reason: entry.reason || null,
errorCode: entry.errorCode || entry.error || null,
errorMessage: entry.errorMessage || null
})
if (
entry.status === 'failed' ||
entry.status === 'not_found' ||
entry.status === 'no_account' ||
entry.reason === 'smtp_credentials_missing'
) {
attempt.failed = true
}
attemptsById.set(entry.requestId, attempt)
}
return [...attemptsById.values()]
.sort((a, b) => String(b.startedAt).localeCompare(String(a.startedAt)))
}
export default defineEventHandler(async (event) => {
const token = getCookie(event, 'auth_token') || getHeader(event, 'authorization')?.replace(/^Bearer\s+/i, '')
const currentUser = token ? await getUserFromToken(token) : null
if (!currentUser || !hasRole(currentUser, 'admin')) {
throw createError({ statusCode: 403, message: 'Zugriff verweigert' })
}
const query = getQuery(event)
const email = normalizeResetEmail(query.email)
const failedOnly = query.failedOnly !== 'false'
const users = await readUsers()
const logs = await readPasswordResetLogs()
const filteredLogs = email
? logs.filter(entry => entry.emailFingerprint === fingerprintResetEmail(email))
: logs
const attempts = summarizeAttempts(filteredLogs)
.filter(attempt => !failedOnly || attempt.failed)
let matchingUsers = []
if (email) {
const term = email.toLowerCase()
matchingUsers = users
.filter(user => {
const userEmail = normalizeResetEmail(user.email)
const name = String(user.name || '').toLowerCase()
return userEmail.includes(term) || name.includes(term)
})
.slice(0, 20)
.map(user => ({
id: user.id,
name: user.name,
email: user.email,
active: user.active !== false,
lastLogin: user.lastLogin || null
}))
}
return {
retentionHours: PASSWORD_RESET_LOG_RETENTION_HOURS,
searchedEmail: email,
matchingUsers,
attempts
}
})