diff --git a/.output/nitro.json b/.output/nitro.json
index 74bd4e8..1c52214 100644
--- a/.output/nitro.json
+++ b/.output/nitro.json
@@ -1,5 +1,5 @@
{
- "date": "2025-11-05T12:46:44.995Z",
+ "date": "2025-11-05T13:24:50.100Z",
"preset": "node-server",
"framework": {
"name": "nuxt",
diff --git a/.output/public/_nuxt/builds/latest.json b/.output/public/_nuxt/builds/latest.json
index f7f6ff6..0a06055 100644
--- a/.output/public/_nuxt/builds/latest.json
+++ b/.output/public/_nuxt/builds/latest.json
@@ -1 +1 @@
-{"id":"9438b35d-0d10-4203-a329-f7a1e287e2a1","timestamp":1762346796211}
\ No newline at end of file
+{"id":"8af76f9e-6e85-416c-9e2f-92c68e0dfd76","timestamp":1762349079882}
\ No newline at end of file
diff --git a/.output/server/chunks/_/members.mjs b/.output/server/chunks/_/members.mjs
index 55ba343..4f55e49 100644
--- a/.output/server/chunks/_/members.mjs
+++ b/.output/server/chunks/_/members.mjs
@@ -72,16 +72,57 @@ async function writeMembers(members) {
return false;
}
}
+function normalizeDate(dateString) {
+ if (!dateString) return "";
+ try {
+ const date = new Date(dateString);
+ if (isNaN(date.getTime())) return dateString.trim();
+ return date.toISOString().split("T")[0];
+ } catch (e) {
+ return dateString.trim();
+ }
+}
+function findDuplicateMember(members, firstName, lastName, geburtsdatum) {
+ const normalizedFirstName = (firstName || "").trim().toLowerCase();
+ const normalizedLastName = (lastName || "").trim().toLowerCase();
+ const normalizedDate = normalizeDate(geburtsdatum);
+ return members.find((m) => {
+ 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 !== "";
+ });
+}
async function saveMember(memberData) {
const members = await readMembers();
if (memberData.id) {
const index = members.findIndex((m) => m.id === memberData.id);
if (index !== -1) {
+ const duplicate = findDuplicateMember(
+ members.filter((m) => m.id !== memberData.id),
+ memberData.firstName,
+ memberData.lastName,
+ memberData.geburtsdatum
+ );
+ if (duplicate) {
+ throw new Error("Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.");
+ }
members[index] = { ...members[index], ...memberData };
} else {
throw new Error("Mitglied nicht gefunden");
}
} else {
+ if (memberData.firstName && memberData.lastName && memberData.geburtsdatum) {
+ const duplicate = findDuplicateMember(
+ members,
+ memberData.firstName,
+ memberData.lastName,
+ memberData.geburtsdatum
+ );
+ if (duplicate) {
+ throw new Error("Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.");
+ }
+ }
const newMember = {
...memberData,
id: randomUUID()
@@ -99,5 +140,5 @@ async function deleteMember(id) {
return true;
}
-export { deleteMember as d, readMembers as r, saveMember as s };
+export { deleteMember as d, normalizeDate as n, readMembers as r, saveMember as s, writeMembers as w };
//# sourceMappingURL=members.mjs.map
diff --git a/.output/server/chunks/_/members.mjs.map b/.output/server/chunks/_/members.mjs.map
index 3c614b6..214358a 100644
--- a/.output/server/chunks/_/members.mjs.map
+++ b/.output/server/chunks/_/members.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"members.mjs","sources":["../../../../server/utils/members.js"],"sourcesContent":null,"names":["fs"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"members.mjs","sources":["../../../../server/utils/members.js"],"sourcesContent":null,"names":["fs"],"mappings":"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"}
\ No newline at end of file
diff --git a/.output/server/chunks/build/client.manifest.mjs b/.output/server/chunks/build/client.manifest.mjs
index cc1d958..3d29731 100644
--- a/.output/server/chunks/build/client.manifest.mjs
+++ b/.output/server/chunks/build/client.manifest.mjs
@@ -296,7 +296,7 @@ const client_manifest = {
"module": true,
"prefetch": true,
"preload": true,
- "file": "oL_Xi1h-.js",
+ "file": "CxKxaas7.js",
"name": "entry",
"src": "node_modules/nuxt/dist/app/entry.js",
"isEntry": true,
@@ -306,14 +306,14 @@ const client_manifest = {
"node_modules/nuxt/dist/app/components/error-500.vue"
],
"css": [
- "entry.CYvTzE5H.css"
+ "entry.wEUbgGlA.css"
],
"assets": [
"Harheimer TC.CKfYAfp1.svg"
]
},
- "entry.CYvTzE5H.css": {
- "file": "entry.CYvTzE5H.css",
+ "entry.wEUbgGlA.css": {
+ "file": "entry.wEUbgGlA.css",
"resourceType": "style",
"prefetch": true,
"preload": true
@@ -699,6 +699,20 @@ const client_manifest = {
"_Qy3ajxTk.js"
]
},
+ "pages/mitgliederbereich/api.vue": {
+ "resourceType": "script",
+ "module": true,
+ "prefetch": true,
+ "preload": true,
+ "file": "r4K7wPft.js",
+ "name": "api",
+ "src": "pages/mitgliederbereich/api.vue",
+ "isDynamicEntry": true,
+ "imports": [
+ "_Qy3ajxTk.js",
+ "node_modules/nuxt/dist/app/entry.js"
+ ]
+ },
"pages/mitgliederbereich/index.vue": {
"resourceType": "script",
"module": true,
@@ -721,7 +735,7 @@ const client_manifest = {
"module": true,
"prefetch": true,
"preload": true,
- "file": "CGzqx_GW.js",
+ "file": "BKaEDYu3.js",
"name": "mitglieder",
"src": "pages/mitgliederbereich/mitglieder.vue",
"isDynamicEntry": true,
diff --git a/.output/server/chunks/build/server.mjs b/.output/server/chunks/build/server.mjs
index 8d30b1c..281ec21 100644
--- a/.output/server/chunks/build/server.mjs
+++ b/.output/server/chunks/build/server.mjs
@@ -397,6 +397,9 @@ const unhead_k2P3m_ZDyjlr2mMYnoDPwavjsDN8hBlk9cFai0bbopU = /* @__PURE__ */ defin
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
+const __nuxt_page_meta$8 = {
+ layout: "default"
+};
const __nuxt_page_meta$7 = {
layout: "default"
};
@@ -430,7 +433,7 @@ const _routes = [
{
name: "login",
path: "/login",
- meta: __nuxt_page_meta$7 || {},
+ meta: __nuxt_page_meta$8 || {},
component: () => import('./login-P5Yg6Pmv.mjs')
},
{
@@ -466,7 +469,7 @@ const _routes = [
{
name: "cms",
path: "/cms",
- meta: { ...__nuxt_page_meta$6 || {}, ...{ "middleware": "auth" } },
+ meta: { ...__nuxt_page_meta$7 || {}, ...{ "middleware": "auth" } },
component: () => import('./index-CQoOFH9O.mjs')
},
{
@@ -503,7 +506,7 @@ const _routes = [
{
name: "cms-termine",
path: "/cms/termine",
- meta: { ...__nuxt_page_meta$5 || {}, ...{ "middleware": "auth" } },
+ meta: { ...__nuxt_page_meta$6 || {}, ...{ "middleware": "auth" } },
component: () => import('./termine-D0MhYqRR.mjs')
},
{
@@ -579,7 +582,7 @@ const _routes = [
{
name: "cms-einstellungen",
path: "/cms/einstellungen",
- meta: { ...__nuxt_page_meta$4 || {}, ...{ "middleware": "auth" } },
+ meta: { ...__nuxt_page_meta$5 || {}, ...{ "middleware": "auth" } },
component: () => import('./einstellungen-D4Ua-Zgq.mjs')
},
{
@@ -622,6 +625,12 @@ const _routes = [
path: "/mannschaften/jugend",
component: () => import('./jugend-D2PYrcrQ.mjs')
},
+ {
+ name: "mitgliederbereich-api",
+ path: "/mitgliederbereich/api",
+ meta: { ...__nuxt_page_meta$4 || {}, ...{ "middleware": "auth" } },
+ component: () => import('./api-2KvLR2u_.mjs')
+ },
{
name: "mitgliederbereich-news",
path: "/mitgliederbereich/news",
@@ -665,7 +674,7 @@ const _routes = [
name: "mitgliederbereich-mitglieder",
path: "/mitgliederbereich/mitglieder",
meta: { ...__nuxt_page_meta || {}, ...{ "middleware": "auth" } },
- component: () => import('./mitglieder-BUT03wq4.mjs')
+ component: () => import('./mitglieder-yWh5DTR0.mjs')
}
];
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g;
@@ -1930,6 +1939,23 @@ const _sfc_main$5 = {
}),
_: 1
}, _parent));
+ _push(`
`);
+ _push(ssrRenderComponent(_component_NuxtLink, {
+ to: "/mitgliederbereich/api",
+ class: "px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all",
+ "active-class": "text-white bg-primary-600"
+ }, {
+ default: withCtx((_, _push2, _parent2, _scopeId) => {
+ if (_push2) {
+ _push2(` API-Dokumentation `);
+ } else {
+ return [
+ createTextVNode(" API-Dokumentation ")
+ ];
+ }
+ }),
+ _: 1
+ }, _parent));
if (isAdmin.value) {
_push(`
CMS `);
_push(ssrRenderComponent(unref(ChevronDown), {
diff --git a/.output/server/chunks/build/server.mjs.map b/.output/server/chunks/build/server.mjs.map
index 3a498af..4622e32 100644
--- a/.output/server/chunks/build/server.mjs.map
+++ b/.output/server/chunks/build/server.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"server.mjs","sources":["../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Ffetch.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fglobal-polyfills.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fnuxt.config.mjs","../../../../node_modules/nuxt/dist/app/nuxt.js","../../../../node_modules/nuxt/dist/app/components/injections.js","../../../../node_modules/nuxt/dist/app/utils.js","../../../../node_modules/nuxt/dist/app/composables/router.js","../../../../node_modules/nuxt/dist/app/composables/error.js","../../../../node_modules/nuxt/dist/app/composables/manifest.js","../../../../node_modules/nuxt/dist/app/composables/payload.js","../../../../node_modules/@pinia/nuxt/dist/runtime/payload-plugin.js","../../../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js","../../../../node_modules/nuxt/dist/pages/runtime/utils.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Froutes.mjs","../../../../node_modules/nuxt/dist/app/components/utils.js","../../../../node_modules/nuxt/dist/pages/runtime/router.options.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Frouter.options.mjs","../../../../node_modules/nuxt/dist/pages/runtime/validate.js","../../../../stores/auth.js","../../../../middleware/auth.global.js","../../../../node_modules/nuxt/dist/app/middleware/manifest-route-rule.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fmiddleware.mjs","../../../../node_modules/nuxt/dist/pages/runtime/plugins/router.js","../../../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js","../../../../node_modules/nuxt/dist/app/components/server-placeholder.js","../../../../node_modules/nuxt/dist/app/components/client-only.js","../../../../node_modules/nuxt/dist/app/components/nuxt-link.js","../../../../node_modules/@pinia/nuxt/dist/runtime/plugin.vue3.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fcomponents.plugin.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fplugins.server.mjs","../../../../node_modules/nuxt/dist/app/components/route-provider.js","../../../../node_modules/nuxt/dist/pages/runtime/page.js","../../../../assets/images/logos/Harheimer TC.svg","../../../../components/Navigation.vue","../../../../components/Footer.vue","../../../../components/ModalDialog.vue","../../../../app.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-error-page.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-root.vue","../../../../node_modules/nuxt/dist/app/entry.js"],"sourcesContent":null,"names":["$fetch","plugin","provide","plugins","createH3Error","createRadixRouter","login1RYyYL8mxx17qR_nmdKvywxx7lKOLXMFu8pTLfvTLYwMeta","indexBAhz9QmEiLA6QGDd6cSKraFX9E0RhZLvBUZorkJgAVkMeta","termineaIqWCm431cOVikspmAA3_6UhrHiffuFLM_t95ydtEd4Meta","einstellungen3Fhl_VQPzY4eklctozY70Drhj8cWMt_FVlyEKQh9aAAMeta","newsPHTXuxdDl4fgPHQQbukE_XsTEemyPOWVSYs64f8qjC4Meta","indexqVbusfljIJ04j42RIA_KP4bSP7XytQIXdqcVfR3kPUAMeta","profilyVBy_UvZ8KvchY44_0SJbB0NHOad6MC_S9C8wfDdUWEMeta","mitglieder9TWncRC_sSS_zPWzxBDlRiNjjFojXSJny_uernMe_tAMeta","__executeAsync","createRouter","entry","payload_plugin_1_bEQpMjikuQhbV8UJ0PxUqmSvPdmV1jDa5DURnKW4M","router_GNCWhvtYfLTYRZZ135CdFAEjxdMexN0ixiUYCAN_tpw","plugin_vue3_CQ_pO3THrTGIeYc0dvC91V75hY8qpo9B_8yZzOW5SFs","useRoute","_ssrRenderAttrs","_mergeProps","_push","_parent","_ssrRenderAttr","_imports_0","_createVNode","_ssrRenderClass","_unref","_ssrRenderList","_ssrInterpolate","_createTextVNode","_toDisplayString","useRouter","_ssrRenderComponent","showError","ErrorComponent","RootComponent"],"mappings":"","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20,21,22,23,24,25,26,27,28,29,30,31,37,38,39]}
\ No newline at end of file
+{"version":3,"file":"server.mjs","sources":["../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Ffetch.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fglobal-polyfills.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fnuxt.config.mjs","../../../../node_modules/nuxt/dist/app/nuxt.js","../../../../node_modules/nuxt/dist/app/components/injections.js","../../../../node_modules/nuxt/dist/app/utils.js","../../../../node_modules/nuxt/dist/app/composables/router.js","../../../../node_modules/nuxt/dist/app/composables/error.js","../../../../node_modules/nuxt/dist/app/composables/manifest.js","../../../../node_modules/nuxt/dist/app/composables/payload.js","../../../../node_modules/@pinia/nuxt/dist/runtime/payload-plugin.js","../../../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js","../../../../node_modules/nuxt/dist/pages/runtime/utils.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Froutes.mjs","../../../../node_modules/nuxt/dist/app/components/utils.js","../../../../node_modules/nuxt/dist/pages/runtime/router.options.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Frouter.options.mjs","../../../../node_modules/nuxt/dist/pages/runtime/validate.js","../../../../stores/auth.js","../../../../middleware/auth.global.js","../../../../node_modules/nuxt/dist/app/middleware/manifest-route-rule.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fmiddleware.mjs","../../../../node_modules/nuxt/dist/pages/runtime/plugins/router.js","../../../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js","../../../../node_modules/nuxt/dist/app/components/server-placeholder.js","../../../../node_modules/nuxt/dist/app/components/client-only.js","../../../../node_modules/nuxt/dist/app/components/nuxt-link.js","../../../../node_modules/@pinia/nuxt/dist/runtime/plugin.vue3.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fcomponents.plugin.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fplugins.server.mjs","../../../../node_modules/nuxt/dist/app/components/route-provider.js","../../../../node_modules/nuxt/dist/pages/runtime/page.js","../../../../assets/images/logos/Harheimer TC.svg","../../../../components/Navigation.vue","../../../../components/Footer.vue","../../../../components/ModalDialog.vue","../../../../app.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-error-page.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-root.vue","../../../../node_modules/nuxt/dist/app/entry.js"],"sourcesContent":null,"names":["$fetch","plugin","provide","plugins","createH3Error","createRadixRouter","login1RYyYL8mxx17qR_nmdKvywxx7lKOLXMFu8pTLfvTLYwMeta","indexBAhz9QmEiLA6QGDd6cSKraFX9E0RhZLvBUZorkJgAVkMeta","termineaIqWCm431cOVikspmAA3_6UhrHiffuFLM_t95ydtEd4Meta","einstellungen3Fhl_VQPzY4eklctozY70Drhj8cWMt_FVlyEKQh9aAAMeta","apis3_Iku1IZkQQ3FilRpoRZFd20ZCr3RAgIorGyDHhuNIMeta","newsPHTXuxdDl4fgPHQQbukE_XsTEemyPOWVSYs64f8qjC4Meta","indexqVbusfljIJ04j42RIA_KP4bSP7XytQIXdqcVfR3kPUAMeta","profilyVBy_UvZ8KvchY44_0SJbB0NHOad6MC_S9C8wfDdUWEMeta","mitglieder9TWncRC_sSS_zPWzxBDlRiNjjFojXSJny_uernMe_tAMeta","__executeAsync","createRouter","entry","payload_plugin_1_bEQpMjikuQhbV8UJ0PxUqmSvPdmV1jDa5DURnKW4M","router_GNCWhvtYfLTYRZZ135CdFAEjxdMexN0ixiUYCAN_tpw","plugin_vue3_CQ_pO3THrTGIeYc0dvC91V75hY8qpo9B_8yZzOW5SFs","useRoute","_ssrRenderAttrs","_mergeProps","_push","_parent","_ssrRenderAttr","_imports_0","_createVNode","_ssrRenderClass","_unref","_ssrRenderList","_ssrInterpolate","_createTextVNode","_toDisplayString","useRouter","_ssrRenderComponent","showError","ErrorComponent","RootComponent"],"mappings":"","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20,21,22,23,24,25,26,27,28,29,30,31,37,38,39]}
\ No newline at end of file
diff --git a/.output/server/chunks/build/styles.mjs b/.output/server/chunks/build/styles.mjs
index 6a50fec..d459343 100644
--- a/.output/server/chunks/build/styles.mjs
+++ b/.output/server/chunks/build/styles.mjs
@@ -4,9 +4,9 @@ const styles = {
"node_modules/nuxt/dist/app/components/error-500.vue": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault),
"components/Hero.vue": () => import('./Hero-styles.CNXkzfUC.mjs').then(interopDefault),
"components/PublicNews.vue": () => import('./PublicNews-styles.Muc48wAK.mjs').then(interopDefault),
+ "components/Hero.vue?vue&type=style&index=0&scoped=28200092&lang.css": () => import('./Hero-styles.CNXkzfUC.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-404.vue?vue&type=style&index=0&scoped=b728498f&lang.css": () => import('./error-404-styles.BsF5Lbhq.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-500.vue?vue&type=style&index=0&scoped=70d84538&lang.css": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault),
- "components/Hero.vue?vue&type=style&index=0&scoped=28200092&lang.css": () => import('./Hero-styles.CNXkzfUC.mjs').then(interopDefault),
"components/PublicNews.vue?vue&type=style&index=0&scoped=c57f605c&lang.css": () => import('./PublicNews-styles.Muc48wAK.mjs').then(interopDefault)
};
diff --git a/.output/server/chunks/nitro/nitro.mjs b/.output/server/chunks/nitro/nitro.mjs
index b623a0c..f761464 100644
--- a/.output/server/chunks/nitro/nitro.mjs
+++ b/.output/server/chunks/nitro/nitro.mjs
@@ -4309,7 +4309,7 @@ function _expandFromEnv(value) {
const _inlineRuntimeConfig = {
"app": {
"baseURL": "/",
- "buildId": "9438b35d-0d10-4203-a329-f7a1e287e2a1",
+ "buildId": "8af76f9e-6e85-416c-9e2f-92c68e0dfd76",
"buildAssetsDir": "/_nuxt/",
"cdnURL": ""
},
@@ -4763,663 +4763,670 @@ const plugins = [
];
const assets = {
- "/documents/Tischtennisregeln light.pdf": {
- "type": "application/pdf",
- "etag": "\"5177b-y/88q2+Y3RRechJMqWhse21KRdQ\"",
- "mtime": "2025-11-05T12:46:41.629Z",
- "size": 333691,
- "path": "../public/documents/Tischtennisregeln light.pdf"
- },
- "/documents/beitrittserklärung_template.pdf": {
- "type": "application/pdf",
- "etag": "\"6ac0-KgukOaJtKwdNaJOX7uoc6iYn7og\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 27328,
- "path": "../public/documents/beitrittserklärung_template.pdf"
- },
- "/documents/satzung.pdf": {
- "type": "application/pdf",
- "etag": "\"5c7cf-L0A3nT8D24T9sD57FFbij3QRpzw\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 378831,
- "path": "../public/documents/satzung.pdf"
+ "/images/club_about_us.png": {
+ "type": "image/png",
+ "etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 2109014,
+ "path": "../public/images/club_about_us.png"
},
"/data/mannschaften.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"858-l94GKn8Q0I5RQnhrM0ZPJsYUmcw\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 2136,
"path": "../public/data/mannschaften.csv"
},
"/data/spielplan.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"8d38-QOShwHtHkSWtRusSJQlU5hXJcmk\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 36152,
"path": "../public/data/spielplan.csv"
},
"/data/spielsysteme.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"9bc-4npLrNHYClsD0TKV5vSifxitfV0\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 2492,
"path": "../public/data/spielsysteme.csv"
},
"/data/termine.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"a3-/pLg8n6Q4Adh8q/vZuoBiMsPHKY\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 163,
"path": "../public/data/termine.csv"
},
"/data/vereinsmeisterschaften.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"99b-ovCRxbAET3nNRs52LvINNjQLWPY\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 2459,
"path": "../public/data/vereinsmeisterschaften.csv"
},
- "/images/club_about_us.png": {
- "type": "image/png",
- "etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
- "mtime": "2025-11-05T12:46:41.629Z",
- "size": 2109014,
- "path": "../public/images/club_about_us.png"
- },
- "/spielplaene/1. Mannschaft 2025⁄2026.pdf": {
+ "/documents/Tischtennisregeln light.pdf": {
"type": "application/pdf",
- "etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
- "mtime": "2025-11-05T12:46:41.629Z",
- "size": 25798,
- "path": "../public/spielplaene/1. Mannschaft 2025⁄2026.pdf"
+ "etag": "\"5177b-y/88q2+Y3RRechJMqWhse21KRdQ\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 333691,
+ "path": "../public/documents/Tischtennisregeln light.pdf"
},
- "/spielplaene/2. Mannschaft 2025⁄2026.pdf": {
+ "/documents/beitrittserklärung_template.pdf": {
"type": "application/pdf",
- "etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 23546,
- "path": "../public/spielplaene/2. Mannschaft 2025⁄2026.pdf"
+ "etag": "\"6ac0-KgukOaJtKwdNaJOX7uoc6iYn7og\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 27328,
+ "path": "../public/documents/beitrittserklärung_template.pdf"
},
- "/spielplaene/3. Mannschaft 2025⁄2026.pdf": {
+ "/documents/satzung.pdf": {
"type": "application/pdf",
- "etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 29767,
- "path": "../public/spielplaene/3. Mannschaft 2025⁄2026.pdf"
- },
- "/spielplaene/4. Mannschaft 2025⁄2026.pdf": {
- "type": "application/pdf",
- "etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 27291,
- "path": "../public/spielplaene/4. Mannschaft 2025⁄2026.pdf"
- },
- "/spielplaene/5. Mannschaft 2025⁄2026.pdf": {
- "type": "application/pdf",
- "etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 25891,
- "path": "../public/spielplaene/5. Mannschaft 2025⁄2026.pdf"
- },
- "/spielplaene/Jugend 11 2025⁄2026.pdf": {
- "type": "application/pdf",
- "etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
- "mtime": "2025-11-05T12:46:41.630Z",
- "size": 21225,
- "path": "../public/spielplaene/Jugend 11 2025⁄2026.pdf"
+ "etag": "\"5c7cf-L0A3nT8D24T9sD57FFbij3QRpzw\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 378831,
+ "path": "../public/documents/satzung.pdf"
},
"/_nuxt/48ve60fm.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"557-e04q6bcr4Wja5BTiX/uCY9jcaOk\"",
- "mtime": "2025-11-05T12:46:41.624Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 1367,
"path": "../public/_nuxt/48ve60fm.js"
},
"/_nuxt/6EY4_GXp.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"19e-CzjelZROfgCPWyXIjP0DPtd+UHQ\"",
- "mtime": "2025-11-05T12:46:41.624Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 414,
"path": "../public/_nuxt/6EY4_GXp.js"
},
"/_nuxt/9IiqlUD-.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2835-WiVWi4XZDT1Nsy21w92+uGqrUhI\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 10293,
"path": "../public/_nuxt/9IiqlUD-.js"
},
"/_nuxt/B0O50Q40.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2346-VOt5BwQrjzVpCVYv0biaieLYXbw\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 9030,
"path": "../public/_nuxt/B0O50Q40.js"
},
"/_nuxt/B1VMaWHv.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"16a7-ABFT0Hkz1mWSRL8qDQgQuhCoswc\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 5799,
"path": "../public/_nuxt/B1VMaWHv.js"
},
"/_nuxt/B4mSF5Ac.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"185-hHs3mU4qOcQAkGQaPrUYGaG0yao\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 389,
"path": "../public/_nuxt/B4mSF5Ac.js"
},
"/_nuxt/B9LKmM6w.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2970-DQ16XS35U2+PB0IaZFhNA7g4mJk\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.321Z",
"size": 10608,
"path": "../public/_nuxt/B9LKmM6w.js"
},
"/_nuxt/B9lUQxCj.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"c21-cIS5lR8JDKJkr1N/Y8gKIIRgLqI\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3105,
"path": "../public/_nuxt/B9lUQxCj.js"
},
"/_nuxt/BC4PNGtJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"175-33lu59Ps/+kwbPv/hVeUdrq4wmI\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 373,
"path": "../public/_nuxt/BC4PNGtJ.js"
},
"/_nuxt/BIgEmhqk.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"3513-NZ/G+tsRsF+o3kKxOyYiTJ+ozJ0\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 13587,
"path": "../public/_nuxt/BIgEmhqk.js"
},
+ "/_nuxt/BKaEDYu3.js": {
+ "type": "text/javascript; charset=utf-8",
+ "etag": "\"5916-HkP5iPO4XP/4n2fQHChYeSwJHKc\"",
+ "mtime": "2025-11-05T13:24:46.322Z",
+ "size": 22806,
+ "path": "../public/_nuxt/BKaEDYu3.js"
+ },
"/_nuxt/BOhpfOYZ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"b10-T1pZGL5tW1rfqWHftATE5VhNr1g\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 2832,
"path": "../public/_nuxt/BOhpfOYZ.js"
},
"/_nuxt/BQ1JAN-t.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"4d4-D2c9LzMtu3t/oTdiDqO210OhOq0\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 1236,
"path": "../public/_nuxt/BQ1JAN-t.js"
},
"/_nuxt/BRIjNg-H.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2820-w8/G0Dy6wRFecNHJSR+NwEJ2AeE\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 10272,
"path": "../public/_nuxt/BRIjNg-H.js"
},
"/_nuxt/BSgvdOgQ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1827-zMnsckCG+udmw6Jb4QbWFvVU40I\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 6183,
"path": "../public/_nuxt/BSgvdOgQ.js"
},
"/_nuxt/BVRiFo7f.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"dbf-bs5Lp4co8JtdiCR4NBZg2xEEpZE\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3519,
"path": "../public/_nuxt/BVRiFo7f.js"
},
"/_nuxt/BWE4JIU7.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"d97-Mz4vlsBagIqTc5OFwjogM6kfacU\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3479,
"path": "../public/_nuxt/BWE4JIU7.js"
},
"/_nuxt/BWWcyQAZ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"685-pCeqOgBg8QXGBjOkvDQ5Miivcwk\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 1669,
"path": "../public/_nuxt/BWWcyQAZ.js"
},
"/_nuxt/BeFU4xfJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1744-/7IohGje8DDLlxCyMiEkw3Thq+I\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 5956,
"path": "../public/_nuxt/BeFU4xfJ.js"
},
"/_nuxt/Bg-xR82b.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1860-q2IZycL1/cXKqiR+YJN8gMIGQ70\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 6240,
"path": "../public/_nuxt/Bg-xR82b.js"
},
"/_nuxt/BihH_yuU.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"dbb-d//fGgPcM4CQDXXuyaBVA0etrcE\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3515,
"path": "../public/_nuxt/BihH_yuU.js"
},
"/_nuxt/BteKZQ9T.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1ea-kmrGdt5SPmt15EiBI7kR9gXMQM0\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 490,
"path": "../public/_nuxt/BteKZQ9T.js"
},
"/_nuxt/BuN7Api-.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"dcb-vEM3YBEgZbfkrV6Gw0ZoyLCb21I\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3531,
"path": "../public/_nuxt/BuN7Api-.js"
},
"/_nuxt/BzVlU_Az.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"3b44-YIew5nSdOke4bV7T76Jp4fRwf4k\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 15172,
"path": "../public/_nuxt/BzVlU_Az.js"
},
"/_nuxt/C0hTJwq1.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1588-AjSEZ5SBctJSg8q99Sy8Zj2YxXY\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 5512,
"path": "../public/_nuxt/C0hTJwq1.js"
},
"/_nuxt/C27DLx1c.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"19ec-tkDTgwwh9gT7TzWUiNpwZMn2Uhc\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 6636,
"path": "../public/_nuxt/C27DLx1c.js"
},
"/_nuxt/C5SyyWEb.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2a5-06iX+CL3i0ysaqW9nu7Eg2YzDhQ\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 677,
"path": "../public/_nuxt/C5SyyWEb.js"
},
"/_nuxt/C8kQt0fa.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"197-7X99z1xphxry8OnMwU7Ofs/uE0Q\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 407,
"path": "../public/_nuxt/C8kQt0fa.js"
},
"/_nuxt/CED8cBGM.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1c75-5CrjjdvSrpIsV0jgPgmLQ/Ag8Kw\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 7285,
"path": "../public/_nuxt/CED8cBGM.js"
},
- "/_nuxt/CGzqx_GW.js": {
- "type": "text/javascript; charset=utf-8",
- "etag": "\"33b7-szQpwlQCIrWy2Yt6HyekQOiyQu4\"",
- "mtime": "2025-11-05T12:46:41.625Z",
- "size": 13239,
- "path": "../public/_nuxt/CGzqx_GW.js"
- },
"/_nuxt/CHQH_CwJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1168-mBOIwVjM5LsxIx13Cs7oT3+LJ+k\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 4456,
"path": "../public/_nuxt/CHQH_CwJ.js"
},
"/_nuxt/CHkqy7mB.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"201d-h4fS2PBhZIObIxeqvtLlHIK7w00\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 8221,
"path": "../public/_nuxt/CHkqy7mB.js"
},
"/_nuxt/CUq_0rkE.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"12d-JV4KW1fgT85/V3Ap13X4q2h9U3g\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 301,
"path": "../public/_nuxt/CUq_0rkE.js"
},
"/_nuxt/Cb8HxufC.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"406f-TyhzTK47O3LPFk0luA/Fak0NoYQ\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 16495,
"path": "../public/_nuxt/Cb8HxufC.js"
},
"/_nuxt/CbiYaI1j.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"faa-35cv2+MngLgY4pLUjpDBDLx6vV0\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 4010,
"path": "../public/_nuxt/CbiYaI1j.js"
},
"/_nuxt/CgxdOZQk.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"16e6-XCN/eSTrNul1blsGspgsYeA873w\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 5862,
"path": "../public/_nuxt/CgxdOZQk.js"
},
"/_nuxt/Ci7R5KNW.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2320-2xahv071b+7opOi1O0ljQRcwIBY\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 8992,
"path": "../public/_nuxt/Ci7R5KNW.js"
},
"/_nuxt/CkzaQq3X.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"17d-+xKrHjeww4bpFFkkjUNLD/ebn5A\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 381,
"path": "../public/_nuxt/CkzaQq3X.js"
},
"/_nuxt/CrCcIvVp.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"274-9U7hEMtgHqdnQopnKeJsBKqKyKw\"",
- "mtime": "2025-11-05T12:46:41.625Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 628,
"path": "../public/_nuxt/CrCcIvVp.js"
},
"/_nuxt/CtbKwxql.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"3090-xfpT19k0ndSinB29602tM2WBsKA\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 12432,
"path": "../public/_nuxt/CtbKwxql.js"
},
+ "/_nuxt/CxKxaas7.js": {
+ "type": "text/javascript; charset=utf-8",
+ "etag": "\"35ed1-HTnyh8f24j/i4Ubtc6/SgRBeEaY\"",
+ "mtime": "2025-11-05T13:24:46.322Z",
+ "size": 220881,
+ "path": "../public/_nuxt/CxKxaas7.js"
+ },
"/_nuxt/DAACT36i.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1f9-dVOk5jAwb0VlMLJevIcT+s2NTgM\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 505,
"path": "../public/_nuxt/DAACT36i.js"
},
"/_nuxt/DB4lwRH4.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"285-Fn2A1Bk4lxBPdxYBguEMM5AsI0M\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 645,
"path": "../public/_nuxt/DB4lwRH4.js"
},
"/_nuxt/DEC6XYYJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"37e2-qLNdHj2LfvfQ4r7oP9pxJ4234W8\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 14306,
"path": "../public/_nuxt/DEC6XYYJ.js"
},
"/_nuxt/DODWL2Pr.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"e7c-ZCkB0/XZCFSnRy2p9SAs25cftgQ\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.322Z",
"size": 3708,
"path": "../public/_nuxt/DODWL2Pr.js"
},
"/_nuxt/DTKApGrI.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1e3a-m8ONRzHj9hcIv08Kb9ZDGKcKKgE\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 7738,
"path": "../public/_nuxt/DTKApGrI.js"
},
"/_nuxt/DUm-savV.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"378-gmau5tfuGGvvXo5HRs29CR7slTs\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 888,
"path": "../public/_nuxt/DUm-savV.js"
},
"/_nuxt/DaSgy0Cl.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"11f-soKnh1qfNJj5nvt+IcgQXYvg/z4\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 287,
"path": "../public/_nuxt/DaSgy0Cl.js"
},
"/_nuxt/DaUHoOti.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"177f-xSuV2n3wrsWDBysy5o9XsakrXDw\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 6015,
"path": "../public/_nuxt/DaUHoOti.js"
},
"/_nuxt/DdHhmCne.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"22d-uKYfhsDcUsz2NrXOJmxptUGZdyE\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 557,
"path": "../public/_nuxt/DdHhmCne.js"
},
"/_nuxt/Dhn1q0tB.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"280-soHXdceT8+kvIFtgcE96cO/TCIU\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 640,
"path": "../public/_nuxt/Dhn1q0tB.js"
},
"/_nuxt/DkeYb0_S.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1ce-xiaAbRvqQ+zffTXF3Gc7rq14R0U\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 462,
"path": "../public/_nuxt/DkeYb0_S.js"
},
"/_nuxt/DlAUqK2U.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"5b-eFCz/UrraTh721pgAl0VxBNR1es\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 91,
"path": "../public/_nuxt/DlAUqK2U.js"
},
"/_nuxt/DoQDnat2.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"120d-q7H0efEMaeQo04Ily4N/PFQjhpA\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 4621,
"path": "../public/_nuxt/DoQDnat2.js"
},
"/_nuxt/DruLqh86.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"bf0-+1jS/uLKW36S7SjKCe7lwpzJbjw\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 3056,
"path": "../public/_nuxt/DruLqh86.js"
},
"/_nuxt/Dy4dzDTt.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"e61-TfoI+tUM1IJUgJ3e0hEAuF/ainI\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 3681,
"path": "../public/_nuxt/Dy4dzDTt.js"
},
"/_nuxt/FF_cyd6S.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"203-rairGPfunSg/yCk4txod3zRSZus\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 515,
"path": "../public/_nuxt/FF_cyd6S.js"
},
"/_nuxt/FwQxzXsL.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"4c6b-WDmRZ/fG0kITIIN7KN6MuTnrzVc\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 19563,
"path": "../public/_nuxt/FwQxzXsL.js"
},
"/_nuxt/GPPAkzQU.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"24cb-KnKpZhja4N6Fk7TQXNuaa4j4dms\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 9419,
"path": "../public/_nuxt/GPPAkzQU.js"
},
"/_nuxt/Harheimer TC.CKfYAfp1.svg": {
"type": "image/svg+xml",
"etag": "\"1d2535-Tx2lTuuFn2hBqGZOnDan3/OdRU0\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 1910069,
"path": "../public/_nuxt/Harheimer TC.CKfYAfp1.svg"
},
"/_nuxt/IxY-j45w.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1b54-yd8vSMFwTUtDVcZx0AViG0asyoM\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 6996,
"path": "../public/_nuxt/IxY-j45w.js"
},
"/_nuxt/KxVBmS-6.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"190-F0LVEAqwB2LwyGzW0v9yzLx0v/0\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 400,
"path": "../public/_nuxt/KxVBmS-6.js"
},
"/_nuxt/PsGBFO20.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"34fa-VfXYDHH2Csr24waHFWok/UZ+AUQ\"",
- "mtime": "2025-11-05T12:46:41.626Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 13562,
"path": "../public/_nuxt/PsGBFO20.js"
},
"/_nuxt/Qy3ajxTk.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"13f-LgrH17St2xFg+RPGvT3uJRaRfFw\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 319,
"path": "../public/_nuxt/Qy3ajxTk.js"
},
"/_nuxt/R6Iy1jPP.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"137-QCUizOitouzMVC2drCYFTAZmqPU\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 311,
"path": "../public/_nuxt/R6Iy1jPP.js"
},
"/_nuxt/YJHbYJtA.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"19f-nQw578pUen9o8yYaMA8Bwag6xho\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 415,
"path": "../public/_nuxt/YJHbYJtA.js"
},
"/_nuxt/ZrOCUSmD.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"ee2-knvq//8tl4tcmRjFy6nWAy0dRBk\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 3810,
"path": "../public/_nuxt/ZrOCUSmD.js"
},
- "/_nuxt/entry.CYvTzE5H.css": {
+ "/_nuxt/entry.wEUbgGlA.css": {
"type": "text/css; charset=utf-8",
- "etag": "\"d359-K8d7YngVKCHFgXk5rvr7JvAWgRM\"",
- "mtime": "2025-11-05T12:46:41.627Z",
- "size": 54105,
- "path": "../public/_nuxt/entry.CYvTzE5H.css"
+ "etag": "\"d470-jKdybOkNd/weF4HvmA1i+mSz4pw\"",
+ "mtime": "2025-11-05T13:24:46.323Z",
+ "size": 54384,
+ "path": "../public/_nuxt/entry.wEUbgGlA.css"
},
"/_nuxt/error-404.CbXQcqJW.css": {
"type": "text/css; charset=utf-8",
"etag": "\"97e-Ty5bTTSEudJkO/DsGUoIf37xYxc\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 2430,
"path": "../public/_nuxt/error-404.CbXQcqJW.css"
},
"/_nuxt/error-500.L485xXhD.css": {
"type": "text/css; charset=utf-8",
"etag": "\"773-jNt1QdCa+iqaSZb1mv/IQWC5p6w\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 1907,
"path": "../public/_nuxt/error-500.L485xXhD.css"
},
"/_nuxt/index.BfDdQit3.css": {
"type": "text/css; charset=utf-8",
"etag": "\"1db-8CoCw55otGXQVF3jz4vWPcGWmuY\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 475,
"path": "../public/_nuxt/index.BfDdQit3.css"
},
- "/_nuxt/oL_Xi1h-.js": {
- "type": "text/javascript; charset=utf-8",
- "etag": "\"35cb0-7x109VLkapMthU3oJP0Ty7wYQpk\"",
- "mtime": "2025-11-05T12:46:41.627Z",
- "size": 220336,
- "path": "../public/_nuxt/oL_Xi1h-.js"
- },
"/_nuxt/oN0_bS6A.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1d3-DKhiaT2RUlSXk55jBttctUuTQQI\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.323Z",
"size": 467,
"path": "../public/_nuxt/oN0_bS6A.js"
},
+ "/_nuxt/r4K7wPft.js": {
+ "type": "text/javascript; charset=utf-8",
+ "etag": "\"4c51-7zSVu+BLyGsMT1uGYy/MrmouUkg\"",
+ "mtime": "2025-11-05T13:24:46.323Z",
+ "size": 19537,
+ "path": "../public/_nuxt/r4K7wPft.js"
+ },
"/_nuxt/rVkivqmM.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"161a-zK8c8X4CzA56nT/n6kqKbIto9uw\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.324Z",
"size": 5658,
"path": "../public/_nuxt/rVkivqmM.js"
},
"/_nuxt/t1jwZBF5.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1dc5-WYQUOK4isA/E8pj6xugVdZaBBKo\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.324Z",
"size": 7621,
"path": "../public/_nuxt/t1jwZBF5.js"
},
"/_nuxt/t6YtlcxA.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"575-I2e3m9LKSy82cY3tyiVI/I7zXz4\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.324Z",
"size": 1397,
"path": "../public/_nuxt/t6YtlcxA.js"
},
"/_nuxt/tgU1A5jl.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"576-TwB1Mj1tLB6Z9zhK9LTHVXbtW8k\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.324Z",
"size": 1398,
"path": "../public/_nuxt/tgU1A5jl.js"
},
"/_nuxt/vygoqABK.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"28f-AjrUgCN/auxu+4F13IlxhdOmeFk\"",
- "mtime": "2025-11-05T12:46:41.627Z",
+ "mtime": "2025-11-05T13:24:46.324Z",
"size": 655,
"path": "../public/_nuxt/vygoqABK.js"
},
+ "/spielplaene/1. Mannschaft 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 25798,
+ "path": "../public/spielplaene/1. Mannschaft 2025⁄2026.pdf"
+ },
+ "/spielplaene/2. Mannschaft 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 23546,
+ "path": "../public/spielplaene/2. Mannschaft 2025⁄2026.pdf"
+ },
+ "/spielplaene/3. Mannschaft 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 29767,
+ "path": "../public/spielplaene/3. Mannschaft 2025⁄2026.pdf"
+ },
+ "/spielplaene/4. Mannschaft 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 27291,
+ "path": "../public/spielplaene/4. Mannschaft 2025⁄2026.pdf"
+ },
+ "/spielplaene/5. Mannschaft 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 25891,
+ "path": "../public/spielplaene/5. Mannschaft 2025⁄2026.pdf"
+ },
+ "/spielplaene/Jugend 11 2025⁄2026.pdf": {
+ "type": "application/pdf",
+ "etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
+ "mtime": "2025-11-05T13:24:46.326Z",
+ "size": 21225,
+ "path": "../public/spielplaene/Jugend 11 2025⁄2026.pdf"
+ },
"/documents/spielplaene/README.md": {
"type": "text/markdown; charset=utf-8",
"etag": "\"229-MzuxTQlQrWciSShfjF7Fr2QGclE\"",
- "mtime": "2025-11-05T12:46:41.629Z",
+ "mtime": "2025-11-05T13:24:46.326Z",
"size": 553,
"path": "../public/documents/spielplaene/README.md"
},
"/_nuxt/builds/latest.json": {
"type": "application/json",
- "etag": "\"47-DSEOTikTqgipcQKtRl6C4eJX/eM\"",
- "mtime": "2025-11-05T12:46:41.615Z",
+ "etag": "\"47-VzWK2iAeXt6NcwoFjDqObFk+lQ4\"",
+ "mtime": "2025-11-05T13:24:46.310Z",
"size": 71,
"path": "../public/_nuxt/builds/latest.json"
},
- "/_nuxt/builds/meta/9438b35d-0d10-4203-a329-f7a1e287e2a1.json": {
+ "/_nuxt/builds/meta/8af76f9e-6e85-416c-9e2f-92c68e0dfd76.json": {
"type": "application/json",
- "etag": "\"8b-J28GxNhENWmuwxbIXkR3zmEQygk\"",
- "mtime": "2025-11-05T12:46:41.613Z",
+ "etag": "\"8b-cx0wXpS3b8fz/gEJyMvOnottRyQ\"",
+ "mtime": "2025-11-05T13:24:46.308Z",
"size": 139,
- "path": "../public/_nuxt/builds/meta/9438b35d-0d10-4203-a329-f7a1e287e2a1.json"
+ "path": "../public/_nuxt/builds/meta/8af76f9e-6e85-416c-9e2f-92c68e0dfd76.json"
}
};
@@ -5652,6 +5659,7 @@ const _lazy_EK_x5_ = () => import('../routes/api/galerie.get.mjs');
const _lazy_jYLuY1 = () => import('../routes/api/members.delete.mjs');
const _lazy_HGAbG3 = () => import('../routes/api/members.get.mjs');
const _lazy_XNetVh = () => import('../routes/api/members.post.mjs');
+const _lazy_HlxJ2p = () => import('../routes/api/members/bulk.post.mjs');
const _lazy_AgsX_N = () => import('../routes/api/membership/applications.get.mjs');
const _lazy_7df2qh = () => import('../routes/api/membership/download/_id_.get.mjs');
const _lazy_Od0rSJ = () => import('../routes/api/membership/generate-pdf.post.mjs');
@@ -5694,6 +5702,7 @@ const handlers = [
{ route: '/api/members', handler: _lazy_jYLuY1, lazy: true, middleware: false, method: "delete" },
{ route: '/api/members', handler: _lazy_HGAbG3, lazy: true, middleware: false, method: "get" },
{ route: '/api/members', handler: _lazy_XNetVh, lazy: true, middleware: false, method: "post" },
+ { route: '/api/members/bulk', handler: _lazy_HlxJ2p, lazy: true, middleware: false, method: "post" },
{ route: '/api/membership/applications', handler: _lazy_AgsX_N, lazy: true, middleware: false, method: "get" },
{ route: '/api/membership/download/:id', handler: _lazy_7df2qh, lazy: true, middleware: false, method: "get" },
{ route: '/api/membership/generate-pdf', handler: _lazy_Od0rSJ, lazy: true, middleware: false, method: "post" },
diff --git a/.output/server/chunks/routes/api/auth/login.post.mjs b/.output/server/chunks/routes/api/auth/login.post.mjs
index e734ce6..e845848 100644
--- a/.output/server/chunks/routes/api/auth/login.post.mjs
+++ b/.output/server/chunks/routes/api/auth/login.post.mjs
@@ -61,6 +61,8 @@ const login_post = defineEventHandler(async (event) => {
});
return {
success: true,
+ token,
+ // Token auch im Body für externe API-Clients
user: {
id: user.id,
email: user.email,
diff --git a/.output/server/chunks/routes/api/auth/login.post.mjs.map b/.output/server/chunks/routes/api/auth/login.post.mjs.map
index 11d0a8c..1bb7acb 100644
--- a/.output/server/chunks/routes/api/auth/login.post.mjs.map
+++ b/.output/server/chunks/routes/api/auth/login.post.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"login.post.mjs","sources":["../../../../../../server/api/auth/login.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAEA,mBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,MAAA,SAAA,EAAA;AACA,IAAA,MAAA,IAAA,GAAA,KAAA,CAAA,IAAA,CAAA,CAAA,CAAA,KAAA,CAAA,CAAA,MAAA,WAAA,EAAA,KAAA,KAAA,CAAA,WAAA,EAAA,CAAA;AAEA,IAAA,IAAA,CAAA,IAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,IAAA,IAAA,CAAA,WAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,OAAA,GAAA,MAAA,cAAA,CAAA,QAAA,EAAA,KAAA,QAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,cAAA,IAAA,CAAA;AAGA,IAAA,MAAA,aAAA,CAAA,IAAA,CAAA,EAAA,EAAA,KAAA,CAAA;AAGA,IAAA,IAAA,CAAA,SAAA,GAAA,iBAAA,IAAA,IAAA,EAAA,EAAA,WAAA,EAAA;AACA,IAAA,MAAA,YAAA,GAAA,MAAA,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,KAAA,IAAA,CAAA,EAAA,GAAA,IAAA,GAAA,CAAA,CAAA;AACA,IAAA,MAAA,WAAA,YAAA,CAAA;AAGA,IAAA,SAAA,CAAA,KAAA,EAAA,cAAA,KAAA,EAAA;AAAA,MACA,QAAA,EAAA,IAAA;AAAA,MACA,MAAA,EAAA,KAAA;AAAA;AAAA,MACA,QAAA,EAAA,KAAA;AAAA,MACA,MAAA,EAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA;AAAA;AAAA,KACA,CAAA;AAGA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,IAAA,EAAA;AAAA,QACA,IAAA,IAAA,CAAA,EAAA;AAAA,QACA,OAAA,IAAA,CAAA,KAAA;AAAA,QACA,MAAA,IAAA,CAAA,IAAA;AAAA,QACA,MAAA,IAAA,CAAA;AAAA;AACA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,iBAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
\ No newline at end of file
+{"version":3,"file":"login.post.mjs","sources":["../../../../../../server/api/auth/login.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAEA,mBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,MAAA,SAAA,EAAA;AACA,IAAA,MAAA,IAAA,GAAA,KAAA,CAAA,IAAA,CAAA,CAAA,CAAA,KAAA,CAAA,CAAA,MAAA,WAAA,EAAA,KAAA,KAAA,CAAA,WAAA,EAAA,CAAA;AAEA,IAAA,IAAA,CAAA,IAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,IAAA,IAAA,CAAA,WAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,OAAA,GAAA,MAAA,cAAA,CAAA,QAAA,EAAA,KAAA,QAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,cAAA,IAAA,CAAA;AAGA,IAAA,MAAA,aAAA,CAAA,IAAA,CAAA,EAAA,EAAA,KAAA,CAAA;AAGA,IAAA,IAAA,CAAA,SAAA,GAAA,iBAAA,IAAA,IAAA,EAAA,EAAA,WAAA,EAAA;AACA,IAAA,MAAA,YAAA,GAAA,MAAA,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,KAAA,IAAA,CAAA,EAAA,GAAA,IAAA,GAAA,CAAA,CAAA;AACA,IAAA,MAAA,WAAA,YAAA,CAAA;AAGA,IAAA,SAAA,CAAA,KAAA,EAAA,cAAA,KAAA,EAAA;AAAA,MACA,QAAA,EAAA,IAAA;AAAA,MACA,MAAA,EAAA,KAAA;AAAA;AAAA,MACA,QAAA,EAAA,KAAA;AAAA,MACA,MAAA,EAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA;AAAA;AAAA,KACA,CAAA;AAGA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,KAAA;AAAA;AAAA,MACA,IAAA,EAAA;AAAA,QACA,IAAA,IAAA,CAAA,EAAA;AAAA,QACA,OAAA,IAAA,CAAA,KAAA;AAAA,QACA,MAAA,IAAA,CAAA,IAAA;AAAA,QACA,MAAA,IAAA,CAAA;AAAA;AACA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,iBAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
\ No newline at end of file
diff --git a/.output/server/chunks/routes/api/members.post.mjs b/.output/server/chunks/routes/api/members.post.mjs
index b7a1434..cb6b607 100644
--- a/.output/server/chunks/routes/api/members.post.mjs
+++ b/.output/server/chunks/routes/api/members.post.mjs
@@ -1,4 +1,4 @@
-import { d as defineEventHandler, g as getCookie, c as createError, r as readBody } from '../../nitro/nitro.mjs';
+import { d as defineEventHandler, g as getCookie, b as getHeader, c as createError, r as readBody } from '../../nitro/nitro.mjs';
import { b as verifyToken, e as getUserById } from '../../_/auth.mjs';
import { s as saveMember } from '../../_/members.mjs';
import 'node:http';
@@ -18,11 +18,17 @@ import 'crypto';
const members_post = defineEventHandler(async (event) => {
try {
- const token = getCookie(event, "auth_token");
+ 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."
+ message: "Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen."
});
}
const decoded = verifyToken(token);
@@ -33,36 +39,65 @@ const members_post = defineEventHandler(async (event) => {
});
}
const user = await getUserById(decoded.id);
- if (!user || user.role !== "admin" && user.role !== "vorstand") {
+ if (!user) {
+ throw createError({
+ statusCode: 401,
+ message: "Benutzer nicht gefunden."
+ });
+ }
+ if (user.role !== "admin" && user.role !== "vorstand") {
throw createError({
statusCode: 403,
- message: "Keine Berechtigung zum Bearbeiten von Mitgliedern."
+ message: "Keine Berechtigung zum Hinzuf\xFCgen/Bearbeiten von Mitgliedern. Erforderlich: admin oder vorstand Rolle."
});
}
const body = await readBody(event);
- const { id, firstName, lastName, email, phone, address, notes } = body;
+ const { id, firstName, lastName, geburtsdatum, email, phone, address, notes } = body;
if (!firstName || !lastName) {
throw createError({
statusCode: 400,
message: "Vorname und Nachname sind erforderlich."
});
}
- await saveMember({
- id: id || void 0,
- firstName,
- lastName,
- email: email || "",
- phone: phone || "",
- address: address || "",
- notes: notes || ""
- });
- return {
- success: true,
- message: "Mitglied erfolgreich gespeichert."
- };
+ if (!geburtsdatum) {
+ throw createError({
+ statusCode: 400,
+ message: "Geburtsdatum ist erforderlich, um Duplikate zu vermeiden."
+ });
+ }
+ try {
+ await saveMember({
+ id: id || void 0,
+ firstName,
+ lastName,
+ geburtsdatum: geburtsdatum || "",
+ email: email || "",
+ phone: phone || "",
+ address: address || "",
+ notes: notes || ""
+ });
+ return {
+ success: true,
+ message: "Mitglied erfolgreich gespeichert."
+ };
+ } catch (memberError) {
+ if (memberError.message && memberError.message.includes("existiert bereits")) {
+ throw createError({
+ statusCode: 409,
+ message: memberError.message
+ });
+ }
+ throw memberError;
+ }
} catch (error) {
console.error("Fehler beim Speichern des Mitglieds:", error);
- throw error;
+ if (error.statusCode) {
+ throw error;
+ }
+ throw createError({
+ statusCode: error.statusCode || 500,
+ message: error.message || "Fehler beim Speichern des Mitglieds."
+ });
}
});
diff --git a/.output/server/chunks/routes/api/members.post.mjs.map b/.output/server/chunks/routes/api/members.post.mjs.map
index 39404a3..0f3f134 100644
--- a/.output/server/chunks/routes/api/members.post.mjs.map
+++ b/.output/server/chunks/routes/api/members.post.mjs.map
@@ -1 +1 @@
-{"version":3,"file":"members.post.mjs","sources":["../../../../../server/api/members.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAGA,qBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,KAAA,GAAA,SAAA,CAAA,KAAA,EAAA,YAAA,CAAA;AAEA,IAAA,IAAA,CAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,OAAA,GAAA,YAAA,KAAA,CAAA;AAEA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,WAAA,CAAA,OAAA,CAAA,EAAA,CAAA;AAGA,IAAA,IAAA,CAAA,IAAA,IAAA,IAAA,CAAA,SAAA,OAAA,IAAA,IAAA,CAAA,SAAA,UAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,IAAA,SAAA,EAAA,QAAA,EAAA,OAAA,KAAA,EAAA,OAAA,EAAA,OAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,SAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,UAAA,CAAA;AAAA,MACA,IAAA,EAAA,IAAA,KAAA,CAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA,KAAA,IAAA,EAAA;AAAA,MACA,OAAA,KAAA,IAAA,EAAA;AAAA,MACA,SAAA,OAAA,IAAA,EAAA;AAAA,MACA,OAAA,KAAA,IAAA;AAAA,KACA,CAAA;AAEA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,OAAA,EAAA;AAAA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,wCAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
\ No newline at end of file
+{"version":3,"file":"members.post.mjs","sources":["../../../../../server/api/members.post.js"],"sourcesContent":null,"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/.output/server/chunks/routes/renderer.mjs b/.output/server/chunks/routes/renderer.mjs
index 6d9d5ca..d870bb3 100644
--- a/.output/server/chunks/routes/renderer.mjs
+++ b/.output/server/chunks/routes/renderer.mjs
@@ -272,7 +272,7 @@ async function renderInlineStyles(usedModules) {
const renderSSRHeadOptions = {"omitLineBreaks":true};
-const entryFileName = "oL_Xi1h-.js";
+const entryFileName = "CxKxaas7.js";
globalThis.__buildAssetsURL = buildAssetsURL;
globalThis.__publicAssetsURL = publicAssetsURL;
diff --git a/components/Navigation.vue b/components/Navigation.vue
index ee96cbd..569b2c7 100644
--- a/components/Navigation.vue
+++ b/components/Navigation.vue
@@ -5,18 +5,14 @@
-
-
-
-
- Harheimer TC
-
-
+
+
+
+
+ Harheimer TC
+
+
@@ -57,18 +53,13 @@
Termine
-
Galerie
-
Intern
@@ -81,203 +72,192 @@
-
-
-
-
- Über uns
-
-
- Vorstand
-
-
- Geschichte
-
-
- Satzung
-
-
- Vereinsmeisterschaften
-
-
-
-
-
-
- Übersicht
-
-
-
-
+
+
+
- {{ mannschaft.mannschaft }}
+ Über uns
+
+
+ Vorstand
+
+
+ Geschichte
+
+
+ Satzung
+
+
+ Vereinsmeisterschaften
-
-
- Spielpläne
-
-
- Spielsysteme
-
-
-
-
-
- Trainingszeiten
-
-
- Trainer
-
-
- Anfänger
-
-
- TT-Regeln
-
-
-
-
-
-
- Übersicht
-
-
-
- Mitgliederliste
-
-
- News
-
-
- Mein Profil
-
-
+
+
+
+ Übersicht
+
-
-
- CMS
-
-
-
-
-
-
- Übersicht
-
-
-
- Über uns
-
-
- Geschichte
-
-
- TT-Regeln
-
-
- Satzung
-
-
- Vereinsmeisterschaften
-
-
-
- News
-
-
- Termine
-
-
- Spielpläne
-
-
- Mitglieder
-
-
-
- Einstellungen
-
-
- Mitgliedschaftsanträge
-
-
- Benutzerverwaltung
-
-
-
+
+
+ {{ mannschaft.mannschaft }}
+
+
+
+
+ Spielpläne
+
+
+ Spielsysteme
+
-
+
+
+
+
+ Trainingszeiten
+
+
+ Trainer
+
+
+ Anfänger
+
+
+ TT-Regeln
+
+
+
+
+
+
+ Übersicht
+
+
+
+ Mitgliederliste
+
+
+ News
+
+
+ Mein Profil
+
+
+
+ API-Dokumentation
+
+
+
+
+
+ CMS
+
+
+
+
+
+
+ Übersicht
+
+
+
+ Über uns
+
+
+ Geschichte
+
+
+ TT-Regeln
+
+
+ Satzung
+
+
+ Vereinsmeisterschaften
+
+
+
+ News
+
+
+ Termine
+
+
+ Spielpläne
+
+
+ Mitglieder
+
+
+
+ Einstellungen
+
+
+ Mitgliedschaftsanträge
+
+
+ Benutzerverwaltung
+
+
+
+
+
+
-
Geschichte
-
- Satzung
-
-
- Vereinsmeisterschaften
-
-
-
+
+ Satzung
+
+
+ Vereinsmeisterschaften
+
+
+
@@ -351,24 +331,22 @@
Übersicht
-
{{ mannschaft.mannschaft }}
-
- Spielpläne
-
-
- Spielsysteme
-
-
-
+
+ Spielpläne
+
+
+ Spielsysteme
+
+
+
@@ -387,16 +365,16 @@
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
Trainer
-
- Anfänger
-
-
- TT-Regeln
-
-
-
+
+ Anfänger
+
+
+ TT-Regeln
+
+
+
@@ -408,10 +386,7 @@
Termine
-
Galerie
@@ -526,24 +501,24 @@ const showCmsDropdown = ref(false)
const isLoggedIn = computed(() => authStore.isLoggedIn)
const isAdmin = computed(() => authStore.isAdmin)
- // Automatisches Setzen des Submenus basierend auf der Route
- const currentSubmenu = computed(() => {
- const path = route.path
- if (path.startsWith('/verein/') || path.startsWith('/vorstand') ||
- path.startsWith('/vereinsmeisterschaften')) {
- return 'verein'
- }
- if (path.startsWith('/mannschaften') || path.startsWith('/spielsysteme')) {
- return 'mannschaften'
- }
- if (path.startsWith('/training') || path.startsWith('/tt-regeln')) {
- return 'training'
- }
- if (path.startsWith('/mitgliederbereich') || path.startsWith('/cms')) {
- return 'intern'
- }
- return null
- })
+// Automatisches Setzen des Submenus basierend auf der Route
+const currentSubmenu = computed(() => {
+ const path = route.path
+ if (path.startsWith('/verein/') || path.startsWith('/vorstand') ||
+ path.startsWith('/vereinsmeisterschaften')) {
+ return 'verein'
+ }
+ if (path.startsWith('/mannschaften') || path.startsWith('/spielsysteme')) {
+ return 'mannschaften'
+ }
+ if (path.startsWith('/training') || path.startsWith('/tt-regeln')) {
+ return 'training'
+ }
+ if (path.startsWith('/mitgliederbereich') || path.startsWith('/cms')) {
+ return 'intern'
+ }
+ return null
+})
// Manuelles Toggle für Click-Events
const manualSubmenu = ref(null)
@@ -556,21 +531,21 @@ const loadMannschaften = async () => {
try {
const response = await fetch('/data/mannschaften.csv')
if (!response.ok) return
-
+
const csv = await response.text()
const lines = csv.split('\n').filter(line => line.trim() !== '')
-
+
if (lines.length < 2) return
-
+
mannschaften.value = lines.slice(1).map(line => {
// Besserer CSV-Parser: Respektiert Anführungszeichen
const values = []
let current = ''
let inQuotes = false
-
+
for (let i = 0; i < line.length; i++) {
const char = line[i]
-
+
if (char === '"') {
inQuotes = !inQuotes
} else if (char === ',' && !inQuotes) {
@@ -581,9 +556,9 @@ const loadMannschaften = async () => {
}
}
values.push(current.trim())
-
+
if (values.length < 10) return null
-
+
return {
mannschaft: values[0].trim(),
slug: values[0].trim().toLowerCase().replace(/\s+/g, '-')
@@ -612,7 +587,7 @@ onMounted(() => {
loadMannschaften()
checkGalleryImages()
authStore.checkAuth()
-
+
// Close CMS dropdown when clicking outside
document.addEventListener('click', (e) => {
if (!e.target.closest('.relative.inline-block')) {
@@ -621,19 +596,19 @@ onMounted(() => {
})
})
- const toggleSubmenu = (menu) => {
- // Wenn wir schon im richtigen Bereich sind, nichts tun (Submenu bleibt offen)
- // Wenn nicht, zur Hauptseite navigieren
- const path = route.path
+const toggleSubmenu = (menu) => {
+ // Wenn wir schon im richtigen Bereich sind, nichts tun (Submenu bleibt offen)
+ // Wenn nicht, zur Hauptseite navigieren
+ const path = route.path
- if (menu === 'verein' && !path.startsWith('/verein/') && !path.startsWith('/vorstand') && !path.startsWith('/vereinsmeisterschaften')) {
- navigateTo('/verein/ueber-uns')
- } else if (menu === 'mannschaften' && !path.startsWith('/mannschaften') && !path.startsWith('/spielsysteme')) {
- navigateTo('/mannschaften')
- } else if (menu === 'training' && !path.startsWith('/training') && !path.startsWith('/tt-regeln')) {
- navigateTo('/training')
- } else if (menu === 'intern' && !path.startsWith('/mitgliederbereich') && !path.startsWith('/cms')) {
- navigateTo('/mitgliederbereich')
- }
- }
+ if (menu === 'verein' && !path.startsWith('/verein/') && !path.startsWith('/vorstand') && !path.startsWith('/vereinsmeisterschaften')) {
+ navigateTo('/verein/ueber-uns')
+ } else if (menu === 'mannschaften' && !path.startsWith('/mannschaften') && !path.startsWith('/spielsysteme')) {
+ navigateTo('/mannschaften')
+ } else if (menu === 'training' && !path.startsWith('/training') && !path.startsWith('/tt-regeln')) {
+ navigateTo('/training')
+ } else if (menu === 'intern' && !path.startsWith('/mitgliederbereich') && !path.startsWith('/cms')) {
+ navigateTo('/mitgliederbereich')
+ }
+}
diff --git a/pages/mitgliederbereich/api.vue b/pages/mitgliederbereich/api.vue
new file mode 100644
index 0000000..f247fba
--- /dev/null
+++ b/pages/mitgliederbereich/api.vue
@@ -0,0 +1,549 @@
+
+
+
+
+
+ API-Dokumentation
+
+
+
+ Übersicht über alle verfügbaren API-Endpoints und deren Verwendung
+
+
+
+
+
+
Authentifizierung
+
+ Alle API-Endpoints erfordern Authentifizierung (außer Login). Es werden zwei Methoden unterstützt:
+
+
+
+
1. Cookie-basiert:
+
Nach dem Login über /api/auth/login wird automatisch ein Cookie gesetzt.
+
+
+
2. Authorization Header:
+
Header: Authorization: Bearer <token>
+
Der Token wird im Login-Response im Feld token zurückgegeben.
+
+
+
+
+
+
+
+
+ Authentifizierung
+
+
+
+
+
+
POST /api/auth/login
+ Öffentlich
+
+
Benutzer einloggen und Token erhalten
+
+
+
Request Body:
+
{
+ "email": "benutzer@example.com",
+ "password": "passwort"
+}
+
+
+
+
Response:
+
{
+ "success": true,
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+ "user": {
+ "id": "user-id",
+ "email": "benutzer@example.com",
+ "name": "Max Mustermann",
+ "role": "mitglied"
+ }
+}
+
+
+
+
+
+
+
POST /api/auth/logout
+ Auth erforderlich
+
+
Benutzer ausloggen
+
+
+
Response:
+
{
+ "success": true,
+ "message": "Erfolgreich ausgeloggt"
+}
+
+
+
+
+
+
+
GET /api/auth/status
+ Auth erforderlich
+
+
Aktuellen Authentifizierungsstatus abrufen
+
+
+
Response:
+
{
+ "isLoggedIn": true,
+ "user": {
+ "id": "user-id",
+ "email": "benutzer@example.com",
+ "name": "Max Mustermann",
+ "role": "mitglied"
+ },
+ "role": "mitglied"
+}
+
+
+
+
+
+
+
+ Mitglieder
+
+
+
+
+
+
GET /api/members
+ Auth erforderlich
+
+
Alle Mitglieder abrufen (mit Merge aus registrierten Benutzern)
+
+
+
Response:
+
{
+ "success": true,
+ "members": [
+ {
+ "id": "member-id",
+ "firstName": "Max",
+ "lastName": "Mustermann",
+ "geburtsdatum": "1990-01-15",
+ "email": "max@example.com",
+ "phone": "0123456789",
+ "address": "Musterstraße 1",
+ "source": "manual",
+ "editable": true,
+ "hasLogin": false
+ }
+ ]
+}
+
+
+
+
+
+
+
POST /api/members
+ admin/vorstand
+
+
Neues Mitglied hinzufügen oder bestehendes bearbeiten
+
+
+
Request Body:
+
{
+ "id": "optional-für-update",
+ "firstName": "Max",
+ "lastName": "Mustermann",
+ "geburtsdatum": "1990-01-15",
+ "email": "max@example.com",
+ "phone": "0123456789",
+ "address": "Musterstraße 1, 12345 Musterstadt",
+ "notes": "Optional"
+}
+
+
+
+
Response:
+
{
+ "success": true,
+ "message": "Mitglied erfolgreich gespeichert."
+}
+
+
+
+
+ Hinweis: Ohne id wird ein neues Mitglied erstellt. Mit id wird ein bestehendes Mitglied aktualisiert. geburtsdatum ist Pflichtfeld zur Duplikatsprüfung (Format: YYYY-MM-DD).
+
+
+
+
+
+
+
+
POST /api/members/bulk
+ admin/vorstand
+
+
Mehrere Mitglieder auf einmal importieren (Bulk-Import)
+
+
+
Request Body:
+
{
+ "members": [
+ {
+ "firstName": "Max",
+ "lastName": "Mustermann",
+ "geburtsdatum": "1990-01-15",
+ "email": "max@example.com",
+ "phone": "0123456789",
+ "address": "Musterstraße 1",
+ "notes": "Optional"
+ },
+ {
+ "firstName": "Anna",
+ "lastName": "Schmidt",
+ "geburtsdatum": "1985-03-20",
+ "email": "anna@example.com"
+ }
+ ]
+}
+
+
+
+
Response:
+
{
+ "success": true,
+ "summary": {
+ "total": 2,
+ "imported": 2,
+ "duplicates": 0,
+ "errors": 0
+ },
+ "results": {
+ "success": [
+ {
+ "index": 1,
+ "member": { ... }
+ }
+ ],
+ "duplicates": [],
+ "errors": []
+ }
+}
+
+
+
+
+ Features:
+
+
+ Duplikatsprüfung gegen bestehende Mitglieder
+ Duplikatsprüfung innerhalb des Imports
+ Validierung aller Daten vor dem Import
+ Detaillierte Fehlerberichte für jeden Eintrag
+ Nur erfolgreiche Einträge werden gespeichert
+
+
+
+
+
+
+
+
DELETE /api/members
+ admin/vorstand
+
+
Mitglied löschen
+
+
+
Request Body:
+
{
+ "id": "member-id"
+}
+
+
+
+
Response:
+
{
+ "success": true,
+ "message": "Mitglied erfolgreich gelöscht."
+}
+
+
+
+
+
+
+
+ News
+
+
+
+
+
+
GET /api/news
+ Auth erforderlich
+
+
Alle News abrufen (inkl. interner News)
+
+
+
+
+
+
POST /api/news
+ admin/vorstand
+
+
Neue News erstellen oder bestehende bearbeiten
+
+
+
Request Body:
+
{
+ "id": "optional-für-update",
+ "title": "Titel der News",
+ "content": "Inhalt der News",
+ "isPublic": true,
+ "expiresAt": "2025-12-31T23:59:59.000Z",
+ "isHidden": false
+}
+
+
+
+
+
+
+
DELETE /api/news
+ admin/vorstand
+
+
News löschen
+
+
+
Request Body:
+
{
+ "id": "news-id"
+}
+
+
+
+
+
+
+
+ Termine
+
+
+
+
+
+
GET /api/termine-manage
+ admin/vorstand
+
+
Alle Termine abrufen (für Verwaltung)
+
+
+
+
+
+
POST /api/termine-manage
+ admin/vorstand
+
+
Neuen Termin erstellen
+
+
+
Request Body:
+
{
+ "datum": "2025-12-25",
+ "uhrzeit": "19:00",
+ "titel": "Weihnachtsfeier",
+ "beschreibung": "Gemeinsame Feier",
+ "kategorie": "Veranstaltung"
+}
+
+
+
+
+
+
+
DELETE /api/termine-manage
+ admin/vorstand
+
+
Termin löschen
+
+
+
Query Parameters:
+
?datum=2025-12-25&uhrzeit=19:00&titel=Weihnachtsfeier&beschreibung=...&kategorie=...
+
+
+
+
+
+
+
+ Konfiguration
+
+
+
+
+
+
GET /api/config
+ admin/vorstand
+
+
Vereinskonfiguration abrufen
+
+
+
+
+
+
PUT /api/config
+ admin/vorstand
+
+
Vereinskonfiguration aktualisieren
+
+
+
Request Body:
+
Komplettes Config-Objekt mit allen Einstellungen
+
+
+
+
+
+
+
+
+
Beispiel-Usage
+
+
+
+
cURL Beispiel:
+
# Login und Token erhalten
+curl -X POST http://localhost:3100/api/auth/login \
+ -H "Content-Type: application/json" \
+ -d '{"email": "admin@example.com", "password": "passwort"}'
+
+# Mitglied hinzufügen mit Token
+curl -X POST http://localhost:3100/api/members \
+ -H "Authorization: Bearer YOUR_TOKEN_HERE" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "firstName": "Max",
+ "lastName": "Mustermann",
+ "geburtsdatum": "1990-01-15",
+ "email": "max@example.com",
+ "phone": "0123456789"
+ }'
+
+# Bulk-Import von Mitgliedern
+curl -X POST http://localhost:3100/api/members/bulk \
+ -H "Authorization: Bearer YOUR_TOKEN_HERE" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "members": [
+ {
+ "firstName": "Max",
+ "lastName": "Mustermann",
+ "geburtsdatum": "1990-01-15",
+ "email": "max@example.com"
+ },
+ {
+ "firstName": "Anna",
+ "lastName": "Schmidt",
+ "geburtsdatum": "1985-03-20",
+ "email": "anna@example.com"
+ }
+ ]
+ }'
+
+
+
+
JavaScript/Fetch Beispiel:
+
// Login
+const loginResponse = await fetch('/api/auth/login', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ email: 'admin@example.com',
+ password: 'passwort'
+ })
+})
+const { token } = await loginResponse.json()
+
+// Mitglied hinzufügen
+const memberResponse = await fetch('/api/members', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ firstName: 'Max',
+ lastName: 'Mustermann',
+ geburtsdatum: '1990-01-15',
+ email: 'max@example.com'
+ })
+})
+
+// Bulk-Import
+const bulkResponse = await fetch('/api/members/bulk', {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ members: [
+ {
+ firstName: 'Max',
+ lastName: 'Mustermann',
+ geburtsdatum: '1990-01-15',
+ email: 'max@example.com'
+ },
+ {
+ firstName: 'Anna',
+ lastName: 'Schmidt',
+ geburtsdatum: '1985-03-20',
+ email: 'anna@example.com'
+ }
+ ]
+ })
+})
+const result = await bulkResponse.json()
+console.log(`Importiert: ${result.summary.imported}, Duplikate: ${result.summary.duplicates}`)
+
+
+
+
+
+
+
Legende
+
+
+ Öffentlich
+ Keine Authentifizierung erforderlich
+
+
+ Auth erforderlich
+ Jeder eingeloggte Benutzer
+
+
+ admin/vorstand
+ Nur Admin oder Vorstand
+
+
+
+
+
+
+
+
+
diff --git a/pages/mitgliederbereich/mitglieder.vue b/pages/mitgliederbereich/mitglieder.vue
index e6db8b4..7ce3040 100644
--- a/pages/mitgliederbereich/mitglieder.vue
+++ b/pages/mitgliederbereich/mitglieder.vue
@@ -16,6 +16,16 @@
{{ viewMode === 'cards' ? 'Tabelle' : 'Karten' }}
+
+
+
+
+ Bulk-Import
+
+
+
Geburtsdatum *
+
+
Wird zur eindeutigen Identifizierung benötigt
+
+
E-Mail
+
+
+
+
+
+ Bulk-Import von Mitgliedern
+
+
+
+
+
CSV-Datei hochladen
+
+
+
+
+
CSV-Datei hochladen
+
Klicken Sie hier oder ziehen Sie eine CSV-Datei hierher
+
{{ bulkSelectedFile.name }}
+
+
+
+
+
+
+
Erwartetes CSV-Format:
+
+
• Erste Zeile: Spaltenüberschriften (firstName, lastName, geburtsdatum, email, phone, address, notes)
+
• Pflichtfelder: firstName, lastName, geburtsdatum
+
• Geburtsdatum: Format YYYY-MM-DD (z.B. 1990-01-15)
+
• Trennzeichen: Komma (,) oder Semikolon (;)
+
+
+
+
+
+
Vorschau ({{ bulkPreviewData.length }} Einträge)
+
+
+
+
+ Vorname
+ Nachname
+ Geburtsdatum
+ E-Mail
+
+
+
+
+ {{ row.firstName || '-' }}
+ {{ row.lastName || '-' }}
+ {{ row.geburtsdatum || '-' }}
+ {{ row.email || '-' }}
+
+
+
+
+ ... und {{ bulkPreviewData.length - 10 }} weitere
+
+
+
+
+
+
+
+
Import-Ergebnisse
+
+
+
{{ bulkImportResults.summary.imported }}
+
Importiert
+
+
+
{{ bulkImportResults.summary.duplicates }}
+
Duplikate
+
+
+
{{ bulkImportResults.summary.errors }}
+
Fehler
+
+
+
+
+
Duplikate:
+
+
+ Zeile {{ dup.index }}: {{ dup.member.firstName }} {{ dup.member.lastName }} - {{ dup.reason }}
+
+
+
+
+
+
Fehler:
+
+
+ Zeile {{ err.index }}: {{ err.error }}
+
+
+
+
+
+
+
+
+ Schließen
+
+
+
+ {{ isBulkImporting ? 'Importiert...' : 'Importieren' }}
+
+
+
+
@@ -318,9 +478,19 @@ const editingMember = ref(null)
const errorMessage = ref('')
const viewMode = ref('cards') // 'table' or 'cards'
+// Bulk import state
+const showBulkImportModal = ref(false)
+const bulkFileInput = ref(null)
+const bulkSelectedFile = ref(null)
+const bulkPreviewData = ref([])
+const isBulkImporting = ref(false)
+const bulkImportResults = ref(null)
+const isDragOver = ref(false)
+
const formData = ref({
firstName: '',
lastName: '',
+ geburtsdatum: '',
email: '',
phone: '',
address: '',
@@ -355,6 +525,7 @@ const openAddModal = () => {
formData.value = {
firstName: '',
lastName: '',
+ geburtsdatum: '',
email: '',
phone: '',
address: '',
@@ -369,6 +540,7 @@ const openEditModal = (member) => {
formData.value = {
firstName: member.firstName || '',
lastName: member.lastName || '',
+ geburtsdatum: member.geburtsdatum || '',
email: member.email || '',
phone: member.phone || '',
address: member.address || '',
@@ -399,8 +571,18 @@ const saveMember = async () => {
closeModal()
await loadMembers()
+ if (window.showSuccessModal) {
+ window.showSuccessModal('Erfolg', 'Mitglied erfolgreich gespeichert.')
+ }
} catch (error) {
- errorMessage.value = error.data?.message || 'Fehler beim Speichern des Mitglieds.'
+ console.error('Fehler beim Speichern:', error)
+ const errorMsg = error.data?.message || error.message || 'Fehler beim Speichern des Mitglieds.'
+ errorMessage.value = errorMsg
+
+ // If it's a duplicate error (409), show it prominently
+ if ((error.statusCode === 409 || error.status === 409) && window.showErrorModal) {
+ window.showErrorModal('Duplikat gefunden', errorMsg)
+ }
} finally {
isSaving.value = false
}
@@ -435,6 +617,131 @@ const formatDate = (dateString) => {
})
}
+// Bulk import functions
+const triggerBulkFileInput = () => {
+ bulkFileInput.value?.click()
+}
+
+const handleBulkFileSelect = (event) => {
+ const file = event.target.files?.[0]
+ if (file) {
+ processBulkCSV(file)
+ }
+}
+
+const handleBulkFileDrop = (event) => {
+ isDragOver.value = false
+ const file = event.dataTransfer?.files?.[0]
+ if (file && file.type === 'text/csv') {
+ processBulkCSV(file)
+ }
+}
+
+const processBulkCSV = async (file) => {
+ bulkSelectedFile.value = file
+ bulkImportResults.value = null
+
+ try {
+ const text = await file.text()
+ const lines = text.split('\n').filter(line => line.trim() !== '')
+
+ if (lines.length < 2) {
+ window.showErrorModal('Fehler', 'CSV-Datei muss mindestens eine Kopfzeile und eine Datenzeile enthalten')
+ return
+ }
+
+ // Detect delimiter
+ const parseCSVLine = (line) => {
+ const tabCount = (line.match(/\t/g) || []).length
+ const semicolonCount = (line.match(/;/g) || []).length
+ const delimiter = tabCount > semicolonCount ? '\t' : (semicolonCount > 0 ? ';' : ',')
+ return line.split(delimiter).map(value => value.trim().replace(/^"|"$/g, ''))
+ }
+
+ // Parse header
+ const headers = parseCSVLine(lines[0]).map(h => h.toLowerCase())
+
+ // Find column indices
+ const firstNameIdx = headers.findIndex(h => h.includes('firstname') || h.includes('vorname'))
+ const lastNameIdx = headers.findIndex(h => h.includes('lastname') || h.includes('nachname'))
+ const geburtsdatumIdx = headers.findIndex(h => h.includes('geburtsdatum') || h.includes('birthdate') || h.includes('geburt'))
+ const emailIdx = headers.findIndex(h => h.includes('email') || h.includes('e-mail'))
+ const phoneIdx = headers.findIndex(h => h.includes('phone') || h.includes('telefon') || h.includes('tel'))
+ const addressIdx = headers.findIndex(h => h.includes('address') || h.includes('adresse'))
+ const notesIdx = headers.findIndex(h => h.includes('note') || h.includes('notiz') || h.includes('bemerkung'))
+
+ if (firstNameIdx === -1 || lastNameIdx === -1 || geburtsdatumIdx === -1) {
+ window.showErrorModal('Fehler', 'CSV muss Spalten für firstName, lastName und geburtsdatum enthalten')
+ return
+ }
+
+ // Parse data rows
+ bulkPreviewData.value = lines.slice(1).map((line, index) => {
+ const values = parseCSVLine(line)
+ return {
+ firstName: values[firstNameIdx] || '',
+ lastName: values[lastNameIdx] || '',
+ geburtsdatum: values[geburtsdatumIdx] || '',
+ email: emailIdx !== -1 ? (values[emailIdx] || '') : '',
+ phone: phoneIdx !== -1 ? (values[phoneIdx] || '') : '',
+ address: addressIdx !== -1 ? (values[addressIdx] || '') : '',
+ notes: notesIdx !== -1 ? (values[notesIdx] || '') : ''
+ }
+ }).filter(row => row.firstName && row.lastName && row.geburtsdatum)
+
+ } catch (error) {
+ console.error('Fehler beim Parsen der CSV:', error)
+ window.showErrorModal('Fehler', 'Fehler beim Lesen der CSV-Datei: ' + error.message)
+ }
+}
+
+const processBulkImport = async () => {
+ if (!bulkPreviewData.value.length) return
+
+ isBulkImporting.value = true
+ bulkImportResults.value = null
+
+ try {
+ const response = await $fetch('/api/members/bulk', {
+ method: 'POST',
+ body: {
+ members: bulkPreviewData.value
+ }
+ })
+
+ bulkImportResults.value = response
+
+ if (response.summary.imported > 0) {
+ await loadMembers()
+ window.showSuccessModal(
+ 'Import erfolgreich',
+ `${response.summary.imported} Mitglieder wurden erfolgreich importiert.`
+ )
+ }
+
+ if (response.summary.duplicates > 0 || response.summary.errors > 0) {
+ // Results are already displayed in the modal
+ }
+ } catch (error) {
+ console.error('Fehler beim Bulk-Import:', error)
+ const errorMsg = error.data?.message || error.message || 'Fehler beim Import'
+ window.showErrorModal('Import-Fehler', errorMsg)
+ } finally {
+ isBulkImporting.value = false
+ }
+}
+
+const closeBulkImportModal = () => {
+ showBulkImportModal.value = false
+ bulkSelectedFile.value = null
+ bulkPreviewData.value = []
+ bulkImportResults.value = null
+ isDragOver.value = false
+ if (bulkFileInput.value) {
+ bulkFileInput.value.value = ''
+ }
+}
+
onMounted(() => {
loadMembers()
})
diff --git a/server/api/auth/login.post.js b/server/api/auth/login.post.js
index 49a50be..c2e7811 100644
--- a/server/api/auth/login.post.js
+++ b/server/api/auth/login.post.js
@@ -59,9 +59,10 @@ export default defineEventHandler(async (event) => {
maxAge: 60 * 60 * 24 * 7 // 7 days
})
- // Return user data (without password)
+ // Return user data (without password) and token for API usage
return {
success: true,
+ token: token, // Token auch im Body für externe API-Clients
user: {
id: user.id,
email: user.email,
diff --git a/server/api/members.post.js b/server/api/members.post.js
index 21fb15a..49297eb 100644
--- a/server/api/members.post.js
+++ b/server/api/members.post.js
@@ -3,12 +3,21 @@ import { saveMember } from '../utils/members.js'
export default defineEventHandler(async (event) => {
try {
- const token = getCookie(event, 'auth_token')
+ // Support both Cookie and Authorization Header
+ let token = getCookie(event, 'auth_token')
+
+ // If no cookie token, try Authorization header (Bearer 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.'
+ message: 'Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen.'
})
}
@@ -23,16 +32,23 @@ export default defineEventHandler(async (event) => {
const user = await getUserById(decoded.id)
- // Only admin and vorstand can edit members
- if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
+ if (!user) {
+ throw createError({
+ statusCode: 401,
+ message: 'Benutzer nicht gefunden.'
+ })
+ }
+
+ // Only admin and vorstand can add/edit members
+ if (user.role !== 'admin' && user.role !== 'vorstand') {
throw createError({
statusCode: 403,
- message: 'Keine Berechtigung zum Bearbeiten von Mitgliedern.'
+ message: 'Keine Berechtigung zum Hinzufügen/Bearbeiten von Mitgliedern. Erforderlich: admin oder vorstand Rolle.'
})
}
const body = await readBody(event)
- const { id, firstName, lastName, email, phone, address, notes } = body
+ const { id, firstName, lastName, geburtsdatum, email, phone, address, notes } = body
if (!firstName || !lastName) {
throw createError({
@@ -41,23 +57,51 @@ export default defineEventHandler(async (event) => {
})
}
- await saveMember({
- id: id || undefined,
- firstName,
- lastName,
- email: email || '',
- phone: phone || '',
- address: address || '',
- notes: notes || ''
- })
+ if (!geburtsdatum) {
+ throw createError({
+ statusCode: 400,
+ message: 'Geburtsdatum ist erforderlich, um Duplikate zu vermeiden.'
+ })
+ }
- return {
- success: true,
- message: 'Mitglied erfolgreich gespeichert.'
+ try {
+ await saveMember({
+ id: id || undefined,
+ firstName,
+ lastName,
+ geburtsdatum: geburtsdatum || '',
+ email: email || '',
+ phone: phone || '',
+ address: address || '',
+ notes: notes || ''
+ })
+
+ return {
+ success: true,
+ message: 'Mitglied erfolgreich gespeichert.'
+ }
+ } catch (memberError) {
+ // Check if it's a duplicate error
+ if (memberError.message && memberError.message.includes('existiert bereits')) {
+ throw createError({
+ statusCode: 409,
+ message: memberError.message
+ })
+ }
+ // Re-throw other errors
+ throw memberError
}
} catch (error) {
console.error('Fehler beim Speichern des Mitglieds:', error)
- throw error
+ // If it's already a createError, re-throw it
+ if (error.statusCode) {
+ throw error
+ }
+ // Otherwise wrap it
+ throw createError({
+ statusCode: error.statusCode || 500,
+ message: error.message || 'Fehler beim Speichern des Mitglieds.'
+ })
}
})
diff --git a/server/api/members/bulk.post.js b/server/api/members/bulk.post.js
new file mode 100644
index 0000000..bec3207
--- /dev/null
+++ b/server/api/members/bulk.post.js
@@ -0,0 +1,192 @@
+import { verifyToken, getUserById } 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 (user.role !== 'admin' && user.role !== '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.'
+ })
+ }
+})
+
diff --git a/server/utils/members.js b/server/utils/members.js
index cd8da6a..13a986c 100644
--- a/server/utils/members.js
+++ b/server/utils/members.js
@@ -106,6 +106,37 @@ export async function getMemberById(id) {
return members.find(m => m.id === id)
}
+// Normalize date string for comparison (handles different date formats)
+export function normalizeDate(dateString) {
+ if (!dateString) return ''
+ // Try to parse and normalize to ISO format (YYYY-MM-DD)
+ try {
+ const date = new Date(dateString)
+ if (isNaN(date.getTime())) return dateString.trim()
+ return date.toISOString().split('T')[0]
+ } catch (e) {
+ return dateString.trim()
+ }
+}
+
+// Check for duplicate member based on firstName, lastName, and geburtsdatum
+function findDuplicateMember(members, firstName, lastName, geburtsdatum) {
+ const normalizedFirstName = (firstName || '').trim().toLowerCase()
+ const normalizedLastName = (lastName || '').trim().toLowerCase()
+ const normalizedDate = normalizeDate(geburtsdatum)
+
+ return members.find(m => {
+ 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 !== '' // Only match if date is provided for both
+ })
+}
+
// Add or update manual member
export async function saveMember(memberData) {
const members = await readMembers()
@@ -114,11 +145,37 @@ export async function saveMember(memberData) {
// Update existing
const index = members.findIndex(m => m.id === memberData.id)
if (index !== -1) {
+ // Check for duplicate (excluding current member)
+ const duplicate = findDuplicateMember(
+ members.filter(m => m.id !== memberData.id),
+ memberData.firstName,
+ memberData.lastName,
+ memberData.geburtsdatum
+ )
+
+ if (duplicate) {
+ throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
+ }
+
members[index] = { ...members[index], ...memberData }
} else {
throw new Error('Mitglied nicht gefunden')
}
} else {
+ // Add new - check for duplicate first
+ if (memberData.firstName && memberData.lastName && memberData.geburtsdatum) {
+ const duplicate = findDuplicateMember(
+ members,
+ memberData.firstName,
+ memberData.lastName,
+ memberData.geburtsdatum
+ )
+
+ if (duplicate) {
+ throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
+ }
+ }
+
// Add new - use UUID for guaranteed uniqueness
const newMember = {
...memberData,