From b024bfe884df93d741189a764d0c78487a8ade93 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 21 Oct 2025 14:35:21 +0200 Subject: [PATCH] Add smart member list with manual+login merge and duplicate detection --- .output/nitro.json | 15 + .output/public/_nuxt/builds/latest.json | 2 +- .../d55b57df-1177-4966-90bc-a07b1837debb.json | 1 - .../f1db3745-a031-477c-933f-6868f5829384.json | 1 + .output/server/chunks/_/auth.mjs | 108 + .output/server/chunks/_/auth.mjs.map | 1 + .output/server/chunks/_/error-500.mjs | 10 + .output/server/chunks/_/error-500.mjs.map | 1 + .../chunks/build/Hero-styles.DnxJI8Rq.mjs | 8 + .../chunks/build/Hero-styles.DnxJI8Rq.mjs.map | 1 + .../_plugin-vue_export-helper-1tPrXgE0.mjs | 10 + ..._plugin-vue_export-helper-1tPrXgE0.mjs.map | 1 + .../server/chunks/build/_slug_-CcxmD4wa.mjs | 120 + .../chunks/build/_slug_-CcxmD4wa.mjs.map | 1 + .../chunks/build/anfaenger-Cj28UOue.mjs | 85 + .../chunks/build/anfaenger-Cj28UOue.mjs.map | 1 + .../server/chunks/build/anlagen-tpcA4Hv2.mjs | 110 + .../chunks/build/anlagen-tpcA4Hv2.mjs.map | 1 + .output/server/chunks/build/auth-DIPdM0XJ.mjs | 376 ++ .../server/chunks/build/auth-DIPdM0XJ.mjs.map | 1 + .../server/chunks/build/benutzer-CEqkQsLi.mjs | 145 + .../chunks/build/benutzer-CEqkQsLi.mjs.map | 1 + .../server/chunks/build/client.manifest.mjs | 779 +++ .../chunks/build/client.manifest.mjs.map | 1 + .../chunks/build/composables-CK-Mp9jS.mjs | 23 + .../chunks/build/composables-CK-Mp9jS.mjs.map | 1 + .../server/chunks/build/damen-DuM0biIS.mjs | 60 + .../chunks/build/damen-DuM0biIS.mjs.map | 1 + .../chunks/build/error-404-DndCdOwS.mjs | 95 + .../chunks/build/error-404-DndCdOwS.mjs.map | 1 + .../build/error-404-styles.BsF5Lbhq.mjs | 8 + .../build/error-404-styles.BsF5Lbhq.mjs.map | 1 + .../chunks/build/error-500-BP06ycvf.mjs | 78 + .../chunks/build/error-500-BP06ycvf.mjs.map | 1 + .../build/error-500-styles.Dccc6iq5.mjs | 8 + .../build/error-500-styles.Dccc6iq5.mjs.map | 1 + .../server/chunks/build/galerie-CoMmWwEg.mjs | 82 + .../chunks/build/galerie-CoMmWwEg.mjs.map | 1 + .../chunks/build/geschichte-DSCikZtf.mjs | 43 + .../chunks/build/geschichte-DSCikZtf.mjs.map | 1 + .../server/chunks/build/herren-Cfh4Z8oD.mjs | 43 + .../chunks/build/herren-Cfh4Z8oD.mjs.map | 1 + .../chunks/build/impressum-BJL_VXpe.mjs | 73 + .../chunks/build/impressum-BJL_VXpe.mjs.map | 1 + .../server/chunks/build/index-CDfPDFNK.mjs | 96 + .../chunks/build/index-CDfPDFNK.mjs.map | 1 + .../server/chunks/build/index-D7JtbHaf.mjs | 153 + .../chunks/build/index-D7JtbHaf.mjs.map | 1 + .../server/chunks/build/index-DUWe6TYg.mjs | 215 + .../chunks/build/index-DUWe6TYg.mjs.map | 1 + .../server/chunks/build/index-Dnh6tx7H.mjs | 95 + .../chunks/build/index-Dnh6tx7H.mjs.map | 1 + .../server/chunks/build/index-mblkcwgM.mjs | 188 + .../chunks/build/index-mblkcwgM.mjs.map | 1 + .../server/chunks/build/jugend-CDQZwKSx.mjs | 60 + .../chunks/build/jugend-CDQZwKSx.mjs.map | 1 + .../server/chunks/build/kontakt-DXDMYVtx.mjs | 139 + .../chunks/build/kontakt-DXDMYVtx.mjs.map | 1 + .../server/chunks/build/login-BdWGyHdA.mjs | 104 + .../chunks/build/login-BdWGyHdA.mjs.map | 1 + .../chunks/build/mitgliedschaft-ItL-1heT.mjs | 202 + .../build/mitgliedschaft-ItL-1heT.mjs.map | 1 + .../build/passwort-vergessen-D8jfiCTL.mjs | 94 + .../build/passwort-vergessen-D8jfiCTL.mjs.map | 1 + .../server/chunks/build/profil-UUfhNGZ3.mjs | 99 + .../chunks/build/profil-UUfhNGZ3.mjs.map | 1 + .../chunks/build/registrieren-DS8LMhBE.mjs | 105 + .../build/registrieren-DS8LMhBE.mjs.map | 1 + .../server/chunks/build/satzung-FqIDL6FT.mjs | 48 + .../chunks/build/satzung-FqIDL6FT.mjs.map | 1 + .output/server/chunks/build/server.mjs | 2639 ++++++++ .output/server/chunks/build/server.mjs.map | 1 + .../chunks/build/spielplaene-DVdlC_Hk.mjs | 98 + .../chunks/build/spielplaene-DVdlC_Hk.mjs.map | 1 + .../chunks/build/spielsysteme-CdvH6qFp.mjs | 141 + .../build/spielsysteme-CdvH6qFp.mjs.map | 1 + .output/server/chunks/build/styles.mjs | 12 + .output/server/chunks/build/styles.mjs.map | 1 + .../server/chunks/build/termine-ZiVCG4zJ.mjs | 85 + .../chunks/build/termine-ZiVCG4zJ.mjs.map | 1 + .../server/chunks/build/trainer-CcVTsygv.mjs | 43 + .../chunks/build/trainer-CcVTsygv.mjs.map | 1 + .../chunks/build/tt-regeln-DyKJvBml.mjs | 88 + .../chunks/build/tt-regeln-DyKJvBml.mjs.map | 1 + .../chunks/build/ueber-uns-pGQi6hRz.mjs | 111 + .../chunks/build/ueber-uns-pGQi6hRz.mjs.map | 1 + .../build/vereinsmeisterschaften-B9yng3Zq.mjs | 160 + .../vereinsmeisterschaften-B9yng3Zq.mjs.map | 1 + .../server/chunks/build/vorstand-CFLwDNhn.mjs | 43 + .../chunks/build/vorstand-CFLwDNhn.mjs.map | 1 + .output/server/chunks/nitro/nitro.mjs | 5948 +++++++++++++++++ .output/server/chunks/nitro/nitro.mjs.map | 1 + .../chunks/routes/api/auth/login.post.mjs | 76 + .../chunks/routes/api/auth/login.post.mjs.map | 1 + .../chunks/routes/api/auth/logout.post.mjs | 37 + .../routes/api/auth/logout.post.mjs.map | 1 + .../chunks/routes/api/auth/register.post.mjs | 108 + .../routes/api/auth/register.post.mjs.map | 1 + .../routes/api/auth/reset-password.post.mjs | 82 + .../api/auth/reset-password.post.mjs.map | 1 + .../chunks/routes/api/auth/status.get.mjs | 56 + .../chunks/routes/api/auth/status.get.mjs.map | 1 + .../routes/api/cms/users/approve.post.mjs | 79 + .../routes/api/cms/users/approve.post.mjs.map | 1 + .../routes/api/cms/users/deactivate.post.mjs | 56 + .../api/cms/users/deactivate.post.mjs.map | 1 + .../chunks/routes/api/cms/users/list.get.mjs | 47 + .../routes/api/cms/users/list.get.mjs.map | 1 + .../routes/api/cms/users/reject.post.mjs | 42 + .../routes/api/cms/users/reject.post.mjs.map | 1 + .../routes/api/cms/users/update-role.post.mjs | 56 + .../api/cms/users/update-role.post.mjs.map | 1 + .../server/chunks/routes/api/contact.post.mjs | 105 + .../chunks/routes/api/contact.post.mjs.map | 1 + .../server/chunks/routes/api/galerie.get.mjs | 42 + .../chunks/routes/api/galerie.get.mjs.map | 1 + .../server/chunks/routes/api/profile.get.mjs | 56 + .../chunks/routes/api/profile.get.mjs.map | 1 + .../server/chunks/routes/api/profile.put.mjs | 96 + .../chunks/routes/api/profile.put.mjs.map | 1 + .../chunks/routes/api/spielplaene.get.mjs | 41 + .../chunks/routes/api/spielplaene.get.mjs.map | 1 + .output/server/chunks/routes/renderer.mjs | 465 ++ .output/server/chunks/routes/renderer.mjs.map | 1 + .../chunks/virtual/_virtual_spa-template.mjs | 4 + .../virtual/_virtual_spa-template.mjs.map | 1 + .output/server/index.mjs | 10 + .output/server/index.mjs.map | 1 + pages/mitgliederbereich/mitglieder.vue | 331 + server/api/members.delete.js | 55 + server/api/members.get.js | 113 + server/api/members.post.js | 62 + server/data/members.json | 22 +- server/utils/members.js | 97 + 134 files changed, 15439 insertions(+), 10 deletions(-) create mode 100644 .output/nitro.json delete mode 100644 .output/public/_nuxt/builds/meta/d55b57df-1177-4966-90bc-a07b1837debb.json create mode 100644 .output/public/_nuxt/builds/meta/f1db3745-a031-477c-933f-6868f5829384.json create mode 100644 .output/server/chunks/_/auth.mjs create mode 100644 .output/server/chunks/_/auth.mjs.map create mode 100644 .output/server/chunks/_/error-500.mjs create mode 100644 .output/server/chunks/_/error-500.mjs.map create mode 100644 .output/server/chunks/build/Hero-styles.DnxJI8Rq.mjs create mode 100644 .output/server/chunks/build/Hero-styles.DnxJI8Rq.mjs.map create mode 100644 .output/server/chunks/build/_plugin-vue_export-helper-1tPrXgE0.mjs create mode 100644 .output/server/chunks/build/_plugin-vue_export-helper-1tPrXgE0.mjs.map create mode 100644 .output/server/chunks/build/_slug_-CcxmD4wa.mjs create mode 100644 .output/server/chunks/build/_slug_-CcxmD4wa.mjs.map create mode 100644 .output/server/chunks/build/anfaenger-Cj28UOue.mjs create mode 100644 .output/server/chunks/build/anfaenger-Cj28UOue.mjs.map create mode 100644 .output/server/chunks/build/anlagen-tpcA4Hv2.mjs create mode 100644 .output/server/chunks/build/anlagen-tpcA4Hv2.mjs.map create mode 100644 .output/server/chunks/build/auth-DIPdM0XJ.mjs create mode 100644 .output/server/chunks/build/auth-DIPdM0XJ.mjs.map create mode 100644 .output/server/chunks/build/benutzer-CEqkQsLi.mjs create mode 100644 .output/server/chunks/build/benutzer-CEqkQsLi.mjs.map create mode 100644 .output/server/chunks/build/client.manifest.mjs create mode 100644 .output/server/chunks/build/client.manifest.mjs.map create mode 100644 .output/server/chunks/build/composables-CK-Mp9jS.mjs create mode 100644 .output/server/chunks/build/composables-CK-Mp9jS.mjs.map create mode 100644 .output/server/chunks/build/damen-DuM0biIS.mjs create mode 100644 .output/server/chunks/build/damen-DuM0biIS.mjs.map create mode 100644 .output/server/chunks/build/error-404-DndCdOwS.mjs create mode 100644 .output/server/chunks/build/error-404-DndCdOwS.mjs.map create mode 100644 .output/server/chunks/build/error-404-styles.BsF5Lbhq.mjs create mode 100644 .output/server/chunks/build/error-404-styles.BsF5Lbhq.mjs.map create mode 100644 .output/server/chunks/build/error-500-BP06ycvf.mjs create mode 100644 .output/server/chunks/build/error-500-BP06ycvf.mjs.map create mode 100644 .output/server/chunks/build/error-500-styles.Dccc6iq5.mjs create mode 100644 .output/server/chunks/build/error-500-styles.Dccc6iq5.mjs.map create mode 100644 .output/server/chunks/build/galerie-CoMmWwEg.mjs create mode 100644 .output/server/chunks/build/galerie-CoMmWwEg.mjs.map create mode 100644 .output/server/chunks/build/geschichte-DSCikZtf.mjs create mode 100644 .output/server/chunks/build/geschichte-DSCikZtf.mjs.map create mode 100644 .output/server/chunks/build/herren-Cfh4Z8oD.mjs create mode 100644 .output/server/chunks/build/herren-Cfh4Z8oD.mjs.map create mode 100644 .output/server/chunks/build/impressum-BJL_VXpe.mjs create mode 100644 .output/server/chunks/build/impressum-BJL_VXpe.mjs.map create mode 100644 .output/server/chunks/build/index-CDfPDFNK.mjs create mode 100644 .output/server/chunks/build/index-CDfPDFNK.mjs.map create mode 100644 .output/server/chunks/build/index-D7JtbHaf.mjs create mode 100644 .output/server/chunks/build/index-D7JtbHaf.mjs.map create mode 100644 .output/server/chunks/build/index-DUWe6TYg.mjs create mode 100644 .output/server/chunks/build/index-DUWe6TYg.mjs.map create mode 100644 .output/server/chunks/build/index-Dnh6tx7H.mjs create mode 100644 .output/server/chunks/build/index-Dnh6tx7H.mjs.map create mode 100644 .output/server/chunks/build/index-mblkcwgM.mjs create mode 100644 .output/server/chunks/build/index-mblkcwgM.mjs.map create mode 100644 .output/server/chunks/build/jugend-CDQZwKSx.mjs create mode 100644 .output/server/chunks/build/jugend-CDQZwKSx.mjs.map create mode 100644 .output/server/chunks/build/kontakt-DXDMYVtx.mjs create mode 100644 .output/server/chunks/build/kontakt-DXDMYVtx.mjs.map create mode 100644 .output/server/chunks/build/login-BdWGyHdA.mjs create mode 100644 .output/server/chunks/build/login-BdWGyHdA.mjs.map create mode 100644 .output/server/chunks/build/mitgliedschaft-ItL-1heT.mjs create mode 100644 .output/server/chunks/build/mitgliedschaft-ItL-1heT.mjs.map create mode 100644 .output/server/chunks/build/passwort-vergessen-D8jfiCTL.mjs create mode 100644 .output/server/chunks/build/passwort-vergessen-D8jfiCTL.mjs.map create mode 100644 .output/server/chunks/build/profil-UUfhNGZ3.mjs create mode 100644 .output/server/chunks/build/profil-UUfhNGZ3.mjs.map create mode 100644 .output/server/chunks/build/registrieren-DS8LMhBE.mjs create mode 100644 .output/server/chunks/build/registrieren-DS8LMhBE.mjs.map create mode 100644 .output/server/chunks/build/satzung-FqIDL6FT.mjs create mode 100644 .output/server/chunks/build/satzung-FqIDL6FT.mjs.map create mode 100644 .output/server/chunks/build/server.mjs create mode 100644 .output/server/chunks/build/server.mjs.map create mode 100644 .output/server/chunks/build/spielplaene-DVdlC_Hk.mjs create mode 100644 .output/server/chunks/build/spielplaene-DVdlC_Hk.mjs.map create mode 100644 .output/server/chunks/build/spielsysteme-CdvH6qFp.mjs create mode 100644 .output/server/chunks/build/spielsysteme-CdvH6qFp.mjs.map create mode 100644 .output/server/chunks/build/styles.mjs create mode 100644 .output/server/chunks/build/styles.mjs.map create mode 100644 .output/server/chunks/build/termine-ZiVCG4zJ.mjs create mode 100644 .output/server/chunks/build/termine-ZiVCG4zJ.mjs.map create mode 100644 .output/server/chunks/build/trainer-CcVTsygv.mjs create mode 100644 .output/server/chunks/build/trainer-CcVTsygv.mjs.map create mode 100644 .output/server/chunks/build/tt-regeln-DyKJvBml.mjs create mode 100644 .output/server/chunks/build/tt-regeln-DyKJvBml.mjs.map create mode 100644 .output/server/chunks/build/ueber-uns-pGQi6hRz.mjs create mode 100644 .output/server/chunks/build/ueber-uns-pGQi6hRz.mjs.map create mode 100644 .output/server/chunks/build/vereinsmeisterschaften-B9yng3Zq.mjs create mode 100644 .output/server/chunks/build/vereinsmeisterschaften-B9yng3Zq.mjs.map create mode 100644 .output/server/chunks/build/vorstand-CFLwDNhn.mjs create mode 100644 .output/server/chunks/build/vorstand-CFLwDNhn.mjs.map create mode 100644 .output/server/chunks/nitro/nitro.mjs create mode 100644 .output/server/chunks/nitro/nitro.mjs.map create mode 100644 .output/server/chunks/routes/api/auth/login.post.mjs create mode 100644 .output/server/chunks/routes/api/auth/login.post.mjs.map create mode 100644 .output/server/chunks/routes/api/auth/logout.post.mjs create mode 100644 .output/server/chunks/routes/api/auth/logout.post.mjs.map create mode 100644 .output/server/chunks/routes/api/auth/register.post.mjs create mode 100644 .output/server/chunks/routes/api/auth/register.post.mjs.map create mode 100644 .output/server/chunks/routes/api/auth/reset-password.post.mjs create mode 100644 .output/server/chunks/routes/api/auth/reset-password.post.mjs.map create mode 100644 .output/server/chunks/routes/api/auth/status.get.mjs create mode 100644 .output/server/chunks/routes/api/auth/status.get.mjs.map create mode 100644 .output/server/chunks/routes/api/cms/users/approve.post.mjs create mode 100644 .output/server/chunks/routes/api/cms/users/approve.post.mjs.map create mode 100644 .output/server/chunks/routes/api/cms/users/deactivate.post.mjs create mode 100644 .output/server/chunks/routes/api/cms/users/deactivate.post.mjs.map create mode 100644 .output/server/chunks/routes/api/cms/users/list.get.mjs create mode 100644 .output/server/chunks/routes/api/cms/users/list.get.mjs.map create mode 100644 .output/server/chunks/routes/api/cms/users/reject.post.mjs create mode 100644 .output/server/chunks/routes/api/cms/users/reject.post.mjs.map create mode 100644 .output/server/chunks/routes/api/cms/users/update-role.post.mjs create mode 100644 .output/server/chunks/routes/api/cms/users/update-role.post.mjs.map create mode 100644 .output/server/chunks/routes/api/contact.post.mjs create mode 100644 .output/server/chunks/routes/api/contact.post.mjs.map create mode 100644 .output/server/chunks/routes/api/galerie.get.mjs create mode 100644 .output/server/chunks/routes/api/galerie.get.mjs.map create mode 100644 .output/server/chunks/routes/api/profile.get.mjs create mode 100644 .output/server/chunks/routes/api/profile.get.mjs.map create mode 100644 .output/server/chunks/routes/api/profile.put.mjs create mode 100644 .output/server/chunks/routes/api/profile.put.mjs.map create mode 100644 .output/server/chunks/routes/api/spielplaene.get.mjs create mode 100644 .output/server/chunks/routes/api/spielplaene.get.mjs.map create mode 100644 .output/server/chunks/routes/renderer.mjs create mode 100644 .output/server/chunks/routes/renderer.mjs.map create mode 100644 .output/server/chunks/virtual/_virtual_spa-template.mjs create mode 100644 .output/server/chunks/virtual/_virtual_spa-template.mjs.map create mode 100644 .output/server/index.mjs create mode 100644 .output/server/index.mjs.map create mode 100644 pages/mitgliederbereich/mitglieder.vue create mode 100644 server/api/members.delete.js create mode 100644 server/api/members.get.js create mode 100644 server/api/members.post.js create mode 100644 server/utils/members.js diff --git a/.output/nitro.json b/.output/nitro.json new file mode 100644 index 0000000..a6b7a81 --- /dev/null +++ b/.output/nitro.json @@ -0,0 +1,15 @@ +{ + "date": "2025-10-21T12:32:03.239Z", + "preset": "node-server", + "framework": { + "name": "nuxt", + "version": "4.1.3" + }, + "versions": { + "nitro": "2.12.7" + }, + "commands": { + "preview": "node server/index.mjs" + }, + "config": {} +} \ No newline at end of file diff --git a/.output/public/_nuxt/builds/latest.json b/.output/public/_nuxt/builds/latest.json index a6eddb2..73c2910 100644 --- a/.output/public/_nuxt/builds/latest.json +++ b/.output/public/_nuxt/builds/latest.json @@ -1 +1 @@ -{"id":"d55b57df-1177-4966-90bc-a07b1837debb","timestamp":1761049822869} \ No newline at end of file +{"id":"f1db3745-a031-477c-933f-6868f5829384","timestamp":1761049916604} \ No newline at end of file diff --git a/.output/public/_nuxt/builds/meta/d55b57df-1177-4966-90bc-a07b1837debb.json b/.output/public/_nuxt/builds/meta/d55b57df-1177-4966-90bc-a07b1837debb.json deleted file mode 100644 index 19388c7..0000000 --- a/.output/public/_nuxt/builds/meta/d55b57df-1177-4966-90bc-a07b1837debb.json +++ /dev/null @@ -1 +0,0 @@ -{"id":"d55b57df-1177-4966-90bc-a07b1837debb","timestamp":1761049822869,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]} \ No newline at end of file diff --git a/.output/public/_nuxt/builds/meta/f1db3745-a031-477c-933f-6868f5829384.json b/.output/public/_nuxt/builds/meta/f1db3745-a031-477c-933f-6868f5829384.json new file mode 100644 index 0000000..4d383de --- /dev/null +++ b/.output/public/_nuxt/builds/meta/f1db3745-a031-477c-933f-6868f5829384.json @@ -0,0 +1 @@ +{"id":"f1db3745-a031-477c-933f-6868f5829384","timestamp":1761049916604,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]} \ No newline at end of file diff --git a/.output/server/chunks/_/auth.mjs b/.output/server/chunks/_/auth.mjs new file mode 100644 index 0000000..ae13652 --- /dev/null +++ b/.output/server/chunks/_/auth.mjs @@ -0,0 +1,108 @@ +import bcrypt from 'bcryptjs'; +import jwt from 'jsonwebtoken'; +import { promises } from 'fs'; +import path from 'path'; + +const JWT_SECRET = process.env.JWT_SECRET || "harheimertc-secret-key-change-in-production"; +const getDataPath = (filename) => { + const cwd = process.cwd(); + if (cwd.endsWith(".output")) { + return path.join(cwd, "../server/data", filename); + } + return path.join(cwd, "server/data", filename); +}; +const USERS_FILE = getDataPath("users.json"); +const SESSIONS_FILE = getDataPath("sessions.json"); +async function readUsers() { + try { + const data = await promises.readFile(USERS_FILE, "utf-8"); + return JSON.parse(data); + } catch (error) { + console.error("Fehler beim Lesen der Benutzerdaten:", error); + return []; + } +} +async function writeUsers(users) { + try { + await promises.writeFile(USERS_FILE, JSON.stringify(users, null, 2), "utf-8"); + return true; + } catch (error) { + console.error("Fehler beim Schreiben der Benutzerdaten:", error); + return false; + } +} +async function readSessions() { + try { + const data = await promises.readFile(SESSIONS_FILE, "utf-8"); + return JSON.parse(data); + } catch (error) { + console.error("Fehler beim Lesen der Sessions:", error); + return []; + } +} +async function writeSessions(sessions) { + try { + await promises.writeFile(SESSIONS_FILE, JSON.stringify(sessions, null, 2), "utf-8"); + return true; + } catch (error) { + console.error("Fehler beim Schreiben der Sessions:", error); + return false; + } +} +async function hashPassword(password) { + const salt = await bcrypt.genSalt(10); + return await bcrypt.hash(password, salt); +} +async function verifyPassword(password, hash) { + return await bcrypt.compare(password, hash); +} +function generateToken(user) { + return jwt.sign( + { + id: user.id, + email: user.email, + role: user.role + }, + JWT_SECRET, + { expiresIn: "7d" } + ); +} +function verifyToken(token) { + try { + return jwt.verify(token, JWT_SECRET); + } catch (error) { + return null; + } +} +async function getUserById(id) { + const users = await readUsers(); + return users.find((u) => u.id === id); +} +async function getUserFromToken(token) { + const decoded = verifyToken(token); + if (!decoded) return null; + const users = await readUsers(); + return users.find((u) => u.id === decoded.id); +} +async function createSession(userId, token) { + const sessions = await readSessions(); + const session = { + id: Date.now().toString(), + userId, + token, + createdAt: (/* @__PURE__ */ new Date()).toISOString(), + expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString() + // 7 days + }; + sessions.push(session); + await writeSessions(sessions); + return session; +} +async function deleteSession(token) { + const sessions = await readSessions(); + const filtered = sessions.filter((s) => s.token !== token); + await writeSessions(filtered); +} + +export { getUserFromToken as a, verifyToken as b, createSession as c, deleteSession as d, getUserById as e, generateToken as g, hashPassword as h, readUsers as r, verifyPassword as v, writeUsers as w }; +//# sourceMappingURL=auth.mjs.map diff --git a/.output/server/chunks/_/auth.mjs.map b/.output/server/chunks/_/auth.mjs.map new file mode 100644 index 0000000..dd444a2 --- /dev/null +++ b/.output/server/chunks/_/auth.mjs.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.mjs","sources":["../../../../server/utils/auth.js"],"sourcesContent":null,"names":["fs"],"mappings":";;;;;AAKA,MAAM,UAAA,GAAa,OAAA,CAAQ,GAAA,CAAI,UAAA,IAAc,6CAAA;AAG7C,MAAM,WAAA,GAAc,CAAC,QAAA,KAAa;AAChC,EAAA,MAAM,GAAA,GAAM,QAAQ,GAAA,EAAI;AAGxB,EAAA,IAAI,GAAA,CAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AAC3B,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,gBAAA,EAAkB,QAAQ,CAAA;AAAA,EAClD;AAGA,EAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,aAAA,EAAe,QAAQ,CAAA;AAC/C,CAAA;AAEA,MAAM,UAAA,GAAa,YAAY,YAAY,CAAA;AAC3C,MAAM,aAAA,GAAgB,YAAY,eAAe,CAAA;AAGjD,eAAsB,SAAA,GAAY;AAChC,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAMA,QAAA,CAAG,QAAA,CAAS,YAAY,OAAO,CAAA;AAClD,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,wCAAwC,KAAK,CAAA;AAC3D,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAGA,eAAsB,WAAW,KAAA,EAAO;AACtC,EAAA,IAAI;AACF,IAAA,MAAMA,QAAA,CAAG,UAAU,UAAA,EAAY,IAAA,CAAK,UAAU,KAAA,EAAO,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,CAAA;AACtE,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,4CAA4C,KAAK,CAAA;AAC/D,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAGA,eAAsB,YAAA,GAAe;AACnC,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,MAAMA,QAAA,CAAG,QAAA,CAAS,eAAe,OAAO,CAAA;AACrD,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EACxB,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,mCAAmC,KAAK,CAAA;AACtD,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAGA,eAAsB,cAAc,QAAA,EAAU;AAC5C,EAAA,IAAI;AACF,IAAA,MAAMA,QAAA,CAAG,UAAU,aAAA,EAAe,IAAA,CAAK,UAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAA,EAAG,OAAO,CAAA;AAC5E,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,KAAA,CAAM,uCAAuC,KAAK,CAAA;AAC1D,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAGA,eAAsB,aAAa,QAAA,EAAU;AAC3C,EAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,EAAE,CAAA;AACpC,EAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,IAAI,CAAA;AACzC;AAGA,eAAsB,cAAA,CAAe,UAAU,IAAA,EAAM;AACnD,EAAA,OAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,QAAA,EAAU,IAAI,CAAA;AAC5C;AAGO,SAAS,cAAc,IAAA,EAAM;AAClC,EAAA,OAAO,GAAA,CAAI,IAAA;AAAA,IACT;AAAA,MACE,IAAI,IAAA,CAAK,EAAA;AAAA,MACT,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,MAAM,IAAA,CAAK;AAAA,KACb;AAAA,IACA,UAAA;AAAA,IACA,EAAE,WAAW,IAAA;AAAK,GACpB;AACF;AAGO,SAAS,YAAY,KAAA,EAAO;AACjC,EAAA,IAAI;AACF,IAAA,OAAO,GAAA,CAAI,MAAA,CAAO,KAAA,EAAO,UAAU,CAAA;AAAA,EACrC,SAAS,KAAA,EAAO;AACd,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAGA,eAAsB,YAAY,EAAA,EAAI;AACpC,EAAA,MAAM,KAAA,GAAQ,MAAM,SAAA,EAAU;AAC9B,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA;AACpC;AASA,eAAsB,iBAAiB,KAAA,EAAO;AAC5C,EAAA,MAAM,OAAA,GAAU,YAAY,KAAK,CAAA;AACjC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,MAAM,KAAA,GAAQ,MAAM,SAAA,EAAU;AAC9B,EAAA,OAAO,MAAM,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,EAAA,KAAO,QAAQ,EAAE,CAAA;AAC5C;AAGA,eAAsB,aAAA,CAAc,QAAQ,KAAA,EAAO;AACjD,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AACpC,EAAA,MAAM,OAAA,GAAU;AAAA,IACd,EAAA,EAAI,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,EAAS;AAAA,IACxB,MAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IAClC,SAAA,EAAW,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,EAAI,GAAI,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAI,CAAA,CAAE,WAAA;AAAY;AAAA,GACxE;AACA,EAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AACrB,EAAA,MAAM,cAAc,QAAQ,CAAA;AAC5B,EAAA,OAAO,OAAA;AACT;AAGA,eAAsB,cAAc,KAAA,EAAO;AACzC,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AACpC,EAAA,MAAM,WAAW,QAAA,CAAS,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,UAAU,KAAK,CAAA;AACvD,EAAA,MAAM,cAAc,QAAQ,CAAA;AAC9B;;;;"} \ No newline at end of file diff --git a/.output/server/chunks/_/error-500.mjs b/.output/server/chunks/_/error-500.mjs new file mode 100644 index 0000000..a9de2fd --- /dev/null +++ b/.output/server/chunks/_/error-500.mjs @@ -0,0 +1,10 @@ +import { escapeHtml } from '@vue/shared'; + +const _messages = { "appName": "Nuxt", "statusCode": 500, "statusMessage": "Internal server error", "description": "This page is temporarily unavailable.", "refresh": "Refresh this page" }; +const template = (messages) => { + messages = { ..._messages, ...messages }; + return '' + escapeHtml(messages.statusCode) + " - " + escapeHtml(messages.statusMessage) + " | " + escapeHtml(messages.appName) + ` + diff --git a/server/api/members.delete.js b/server/api/members.delete.js new file mode 100644 index 0000000..927ea60 --- /dev/null +++ b/server/api/members.delete.js @@ -0,0 +1,55 @@ +import { verifyToken, getUserById } from '../utils/auth.js' +import { deleteMember } from '../utils/members.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 user = await getUserById(decoded.id) + + // Only admin and vorstand can delete members + if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) { + throw createError({ + statusCode: 403, + message: 'Keine Berechtigung zum Löschen von Mitgliedern.' + }) + } + + const body = await readBody(event) + const { id } = body + + if (!id) { + throw createError({ + statusCode: 400, + message: 'Mitglieds-ID ist erforderlich.' + }) + } + + await deleteMember(id) + + return { + success: true, + message: 'Mitglied erfolgreich gelöscht.' + } + } catch (error) { + console.error('Fehler beim Löschen des Mitglieds:', error) + throw error + } +}) + diff --git a/server/api/members.get.js b/server/api/members.get.js new file mode 100644 index 0000000..a69270a --- /dev/null +++ b/server/api/members.get.js @@ -0,0 +1,113 @@ +import { verifyToken } from '../utils/auth.js' +import { readMembers, readUsers } from '../utils/members.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 = [] + const processedEmails = new Set() + const processedNames = new Set() + + // First, add all manual members + for (const member of manualMembers) { + const normalizedEmail = member.email?.toLowerCase().trim() || '' + const normalizedName = member.name?.toLowerCase().trim() || '' + + mergedMembers.push({ + ...member, + source: 'manual', + editable: true, + hasLogin: false + }) + + if (normalizedEmail) processedEmails.add(normalizedEmail) + if (normalizedName) processedNames.add(normalizedName) + } + + // 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 + let matchedManualIndex = -1 + + // Try to match by email first + if (normalizedEmail) { + matchedManualIndex = mergedMembers.findIndex( + m => m.source === 'manual' && m.email?.toLowerCase().trim() === normalizedEmail + ) + } + + // If no email match, try name + if (matchedManualIndex === -1 && normalizedName) { + matchedManualIndex = mergedMembers.findIndex( + m => m.source === 'manual' && m.name?.toLowerCase().trim() === normalizedName + ) + } + + if (matchedManualIndex !== -1) { + // Merge with existing manual member + mergedMembers[matchedManualIndex] = { + ...mergedMembers[matchedManualIndex], + hasLogin: true, + loginEmail: user.email, + loginRole: user.role, + lastLogin: user.lastLogin + } + } else { + // Add as new member (from login system) + mergedMembers.push({ + id: user.id, + name: user.name, + email: user.email, + phone: user.phone || '', + address: '', + notes: `Rolle: ${user.role}`, + source: 'login', + editable: false, + hasLogin: true, + loginEmail: user.email, + loginRole: user.role, + lastLogin: user.lastLogin + }) + } + } + + // 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 + } +}) + diff --git a/server/api/members.post.js b/server/api/members.post.js new file mode 100644 index 0000000..8f90754 --- /dev/null +++ b/server/api/members.post.js @@ -0,0 +1,62 @@ +import { verifyToken, getUserById } from '../utils/auth.js' +import { saveMember } from '../utils/members.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 user = await getUserById(decoded.id) + + // Only admin and vorstand can edit members + if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) { + throw createError({ + statusCode: 403, + message: 'Keine Berechtigung zum Bearbeiten von Mitgliedern.' + }) + } + + const body = await readBody(event) + const { id, name, email, phone, address, notes } = body + + if (!name) { + throw createError({ + statusCode: 400, + message: 'Name ist erforderlich.' + }) + } + + await saveMember({ + id: id || undefined, + name, + email: email || '', + phone: phone || '', + address: address || '', + notes: notes || '' + }) + + return { + success: true, + message: 'Mitglied erfolgreich gespeichert.' + } + } catch (error) { + console.error('Fehler beim Speichern des Mitglieds:', error) + throw error + } +}) + diff --git a/server/data/members.json b/server/data/members.json index 62773ed..178e89f 100644 --- a/server/data/members.json +++ b/server/data/members.json @@ -1,12 +1,18 @@ [ { - "id": "1", - "name": "Admin", - "email": "admin@harheimertc.de", - "phone": "069-12345678", - "role": "admin", - "memberSince": "2020-01-01", - "active": true + "id": "m1", + "name": "Max Mustermann", + "email": "max@example.com", + "phone": "069 123456", + "address": "Musterstraße 1, 60437 Frankfurt", + "notes": "Herren 1" + }, + { + "id": "m2", + "name": "Anna Schmidt", + "email": "", + "phone": "069 234567", + "address": "Hauptstraße 5, 60437 Frankfurt", + "notes": "Damen" } ] - diff --git a/server/utils/members.js b/server/utils/members.js new file mode 100644 index 0000000..6007fc4 --- /dev/null +++ b/server/utils/members.js @@ -0,0 +1,97 @@ +import { promises as fs } from 'fs' +import path from 'path' + +// Handle both dev and production paths +const getDataPath = (filename) => { + const cwd = process.cwd() + + // In production (.output/server), working dir is .output + if (cwd.endsWith('.output')) { + return path.join(cwd, '../server/data', filename) + } + + // In development, working dir is project root + return path.join(cwd, 'server/data', filename) +} + +const MEMBERS_FILE = getDataPath('members.json') +const USERS_FILE = getDataPath('users.json') + +// Read manual members from file +export async function readMembers() { + try { + const data = await fs.readFile(MEMBERS_FILE, 'utf-8') + return JSON.parse(data) + } catch (error) { + if (error.code === 'ENOENT') { + return [] + } + console.error('Fehler beim Lesen der Mitgliederdaten:', error) + return [] + } +} + +// Write manual members to file +export async function writeMembers(members) { + try { + await fs.writeFile(MEMBERS_FILE, JSON.stringify(members, null, 2), 'utf-8') + return true + } catch (error) { + console.error('Fehler beim Schreiben der Mitgliederdaten:', error) + return false + } +} + +// Read registered users from file +export async function readUsers() { + try { + const data = await fs.readFile(USERS_FILE, 'utf-8') + return JSON.parse(data) + } catch (error) { + if (error.code === 'ENOENT') { + return [] + } + console.error('Fehler beim Lesen der Benutzerdaten:', error) + return [] + } +} + +// Get member by ID +export async function getMemberById(id) { + const members = await readMembers() + return members.find(m => m.id === id) +} + +// Add or update manual member +export async function saveMember(memberData) { + const members = await readMembers() + + if (memberData.id) { + // Update existing + const index = members.findIndex(m => m.id === memberData.id) + if (index !== -1) { + members[index] = { ...members[index], ...memberData } + } else { + throw new Error('Mitglied nicht gefunden') + } + } else { + // Add new + const newMember = { + id: `m${Date.now()}`, + ...memberData + } + members.push(newMember) + } + + await writeMembers(members) + return true +} + +// Delete manual member +export async function deleteMember(id) { + const members = await readMembers() + const filtered = members.filter(m => m.id !== id) + await writeMembers(filtered) + return true +} +