From 677140bd33b41c58841ec84cf79f90ecfb77fbbe Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 11 Feb 2026 13:04:45 +0100 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20Sichtbarkeitspr=C3=A4ferenzen=20f?= =?UTF-8?q?=C3=BCr=20Mitgliederprofile=20hinzu:=20Erm=C3=B6gliche=20Benutz?= =?UTF-8?q?ern,=20ihre=20E-Mail,=20Telefonnummer=20und=20Adresse=20f=C3=BC?= =?UTF-8?q?r=20andere=20eingeloggte=20Mitglieder=20sichtbar=20zu=20machen.?= =?UTF-8?q?=20Aktualisiere=20die=20API,=20um=20diese=20Einstellungen=20zu?= =?UTF-8?q?=20respektieren=20und=20bei=20der=20Profildatenr=C3=BCckgabe=20?= =?UTF-8?q?zu=20ber=C3=BCcksichtigen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/mitgliederbereich/profil.vue | 28 ++++++++++++++++++++++++ server/api/members.get.js | 34 ++++++++++++++++++++++++++--- server/api/profile.get.js | 35 +++++++++--------------------- server/api/profile.put.js | 13 ++++++++++- 4 files changed, 81 insertions(+), 29 deletions(-) diff --git a/pages/mitgliederbereich/profil.vue b/pages/mitgliederbereich/profil.vue index d6ca755..239e3a0 100644 --- a/pages/mitgliederbereich/profil.vue +++ b/pages/mitgliederbereich/profil.vue @@ -77,6 +77,25 @@ > + +
+

Sichtbarkeit für andere Mitglieder

+
+ + + +
+
+

@@ -279,6 +298,13 @@ const formData = ref({ phone: '' }) +// Visibility preferences for other logged-in members +const visibility = ref({ + showEmail: true, + showPhone: true, + showAddress: false +}) + const passwordData = ref({ current: '', new: '', @@ -297,6 +323,7 @@ const loadProfile = async () => { email: response.user.email, phone: response.user.phone || '' } + visibility.value = response.user.visibility || visibility.value } catch { errorMessage.value = 'Fehler beim Laden des Profils.' } finally { @@ -398,6 +425,7 @@ const handleSave = async () => { name: formData.value.name, email: formData.value.email, phone: formData.value.phone, + visibility: visibility.value, currentPassword: passwordData.value.current || undefined, newPassword: passwordData.value.new || undefined } diff --git a/server/api/members.get.js b/server/api/members.get.js index 1668795..d1f43ec 100644 --- a/server/api/members.get.js +++ b/server/api/members.get.js @@ -149,11 +149,39 @@ export default defineEventHandler(async (event) => { // Sort by name mergedMembers.sort((a, b) => a.name.localeCompare(b.name)) // Die Mitgliederliste ist nur für authentifizierte Nutzer sichtbar (siehe oben). - // Entsprechend zeigen wir allen eingeloggten Nutzer*innen die vollständigen Kontaktdaten - // (inkl. Telefon und E-Mail) für alle aktiven Mitglieder. + // Respektiere individuelle Sichtbarkeitspräferenzen (user.visibility) + const currentUserToken = token + const isViewerAuthenticated = !!currentUser + + const sanitizedMembers = mergedMembers.map(member => { + // Default: show email/phone/address to other logged-in members unless member.visibility explicitly hides them + const visibility = member.visibility || {} + + const showEmail = visibility.showEmail === undefined ? true : Boolean(visibility.showEmail) + const showPhone = visibility.showPhone === undefined ? true : Boolean(visibility.showPhone) + const showAddress = visibility.showAddress === undefined ? false : Boolean(visibility.showAddress) + + return { + id: member.id, + name: member.name, + source: member.source, + editable: member.editable, + hasLogin: member.hasLogin, + loginRoles: member.loginRoles, + loginRole: member.loginRole, + lastLogin: member.lastLogin, + isMannschaftsspieler: member.isMannschaftsspieler, + notes: member.notes || '', + // Only include contact fields when viewer is authenticated and the member allows it + email: (isViewerAuthenticated && showEmail) ? member.email : undefined, + phone: (isViewerAuthenticated && showPhone) ? member.phone : undefined, + address: (isViewerAuthenticated && showAddress) ? member.address : undefined + } + }) + return { success: true, - members: mergedMembers + members: sanitizedMembers } } catch (error) { console.error('Fehler beim Abrufen der Mitgliederliste:', error) diff --git a/server/api/profile.get.js b/server/api/profile.get.js index ab8ca0f..1755fed 100644 --- a/server/api/profile.get.js +++ b/server/api/profile.get.js @@ -1,51 +1,36 @@ -import { verifyToken, getUserById, migrateUserRoles } from '../utils/auth.js' +import { verifyToken, getUserFromToken } 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.' - }) + throw createError({ statusCode: 401, message: 'Nicht authentifiziert.' }) } const decoded = verifyToken(token) - if (!decoded) { - throw createError({ - statusCode: 401, - message: 'Ungültiges Token.' - }) + throw createError({ statusCode: 401, message: 'Ungültiges Token.' }) } - const user = await getUserById(decoded.id) - - if (!user || user.active === false) { - throw createError({ - statusCode: 403, - message: 'Benutzer nicht gefunden oder inaktiv.' - }) + const user = await getUserFromToken(token) + if (!user) { + throw createError({ statusCode: 404, message: 'Benutzer nicht gefunden.' }) } - const migratedUser = migrateUserRoles({ ...user }) - const roles = Array.isArray(migratedUser.roles) ? migratedUser.roles : (migratedUser.role ? [migratedUser.role] : ['mitglied']) - - // Return user data (without password) + // Rückgabe des eigenen Profils inkl. Sichtbarkeitspräferenzen return { success: true, user: { id: user.id, - email: user.email, name: user.name, + email: user.email, phone: user.phone || '', - roles: roles, - role: roles[0] || 'mitglied' // Rückwärtskompatibilität + visibility: user.visibility || {} } } } catch (error) { - console.error('Profil-Abruf-Fehler:', error) + console.error('Fehler beim Laden des Profil:', error) throw error } }) diff --git a/server/api/profile.put.js b/server/api/profile.put.js index 27b7238..d8e7cd2 100644 --- a/server/api/profile.put.js +++ b/server/api/profile.put.js @@ -31,7 +31,7 @@ export default defineEventHandler(async (event) => { }) } - const users = await readUsers() + const users = await readUsers() const userIndex = users.findIndex(u => u.id === decoded.id) if (userIndex === -1) { @@ -59,6 +59,16 @@ export default defineEventHandler(async (event) => { user.email = email user.phone = phone || '' + // Optional visibility preferences (what to show to other logged-in members) + // Expected shape: { showEmail: boolean, showPhone: boolean, showAddress: boolean } + const visibility = body.visibility || body.visibilityPreferences || null + if (visibility && typeof visibility === 'object') { + user.visibility = user.visibility || {} + if (typeof visibility.showEmail === 'boolean') user.visibility.showEmail = visibility.showEmail + if (typeof visibility.showPhone === 'boolean') user.visibility.showPhone = visibility.showPhone + if (typeof visibility.showAddress === 'boolean') user.visibility.showAddress = visibility.showAddress + } + // Handle password change if (currentPassword && newPassword) { const isValid = await verifyPassword(currentPassword, user.password) @@ -93,6 +103,7 @@ export default defineEventHandler(async (event) => { email: user.email, name: user.name, phone: user.phone, + visibility: user.visibility || {}, roles: roles, role: roles[0] || 'mitglied' // Rückwärtskompatibilität }