diff --git a/backend/node_modules/.package-lock.json b/backend/node_modules/.package-lock.json index 6da0403..da95e02 100644 --- a/backend/node_modules/.package-lock.json +++ b/backend/node_modules/.package-lock.json @@ -1318,6 +1318,15 @@ "node": ">= 10" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/date-fns": { "version": "2.30.0", "license": "MIT", @@ -1886,6 +1895,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "dev": true, @@ -2004,6 +2036,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/formidable": { "version": "3.5.4", "dev": true, @@ -3155,10 +3199,48 @@ "node": ">=6.0.0" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-ensure": { "version": "0.0.0", "license": "MIT" }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-gyp": { "version": "8.4.1", "dev": true, @@ -5032,6 +5114,15 @@ "dev": true, "license": "MIT" }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "license": "BSD-2-Clause" diff --git a/backend/package-lock.json b/backend/package-lock.json index 7d5e601..4741be0 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -23,6 +23,7 @@ "multer": "^1.4.5-lts.1", "mysql2": "^3.10.3", "node-cron": "^4.2.1", + "node-fetch": "^3.3.2", "nodemailer": "^7.0.9", "pdf-parse": "^1.1.1", "pdfjs-dist": "^5.4.394", @@ -1352,6 +1353,15 @@ "node": ">= 10" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/date-fns": { "version": "2.30.0", "license": "MIT", @@ -1920,6 +1930,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "dev": true, @@ -2038,6 +2071,18 @@ "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/formidable": { "version": "3.5.4", "dev": true, @@ -3189,10 +3234,48 @@ "node": ">=6.0.0" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-ensure": { "version": "0.0.0", "license": "MIT" }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-gyp": { "version": "8.4.1", "dev": true, @@ -5066,6 +5149,15 @@ "dev": true, "license": "MIT" }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "license": "BSD-2-Clause" diff --git a/backend/package.json b/backend/package.json index 0814d64..ddbfa92 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,6 +28,7 @@ "multer": "^1.4.5-lts.1", "mysql2": "^3.10.3", "node-cron": "^4.2.1", + "node-fetch": "^3.3.2", "nodemailer": "^7.0.9", "pdf-parse": "^1.1.1", "pdfjs-dist": "^5.4.394", diff --git a/backend/routes/nuscoreApiRoutes.js b/backend/routes/nuscoreApiRoutes.js index ca2be52..064a1f2 100644 --- a/backend/routes/nuscoreApiRoutes.js +++ b/backend/routes/nuscoreApiRoutes.js @@ -182,4 +182,93 @@ router.get('/meetingdetails/:uuid', async (req, res) => { } }); +// Submit Meeting Report API-Endpunkt +router.put('/submit/:uuid', async (req, res) => { + const { uuid } = req.params; + const reportData = req.body; + + try { + // Hole Cookies für diese UUID (falls vorhanden) + // Versuche zuerst UUID, dann Code als Fallback + let cookies = cookieStore.get(uuid) || {}; + + // Falls keine Cookies für UUID vorhanden, versuche Code zu finden + if (Object.keys(cookies).length === 0 && reportData.gameCode) { + cookies = cookieStore.get(reportData.gameCode) || {}; + } + + const url = `https://ttde-apps.liga.nu/nuliga/rs/tt/2022/meetingentry/reports/${uuid}/submit`; + + const response = await fetch(url, { + method: 'PUT', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'de,en-US;q=0.7,en;q=0.3', + 'Accept-Encoding': 'gzip, deflate, br, zstd', + 'Content-Type': 'application/json', + 'Referer': `https://ttde-apps.liga.nu/nuliga/nuscore-tt/meeting/${uuid}/report-clearance/review`, + 'Origin': 'https://ttde-apps.liga.nu', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin', + 'Connection': 'keep-alive', + ...(Object.keys(cookies).length > 0 && { 'Cookie': formatCookies(cookies) }) + }, + body: JSON.stringify(reportData) + }); + + const responseText = await response.text(); + let responseData; + + try { + responseData = JSON.parse(responseText); + } catch (e) { + // Falls keine JSON-Antwort, verwende Text + responseData = { message: responseText }; + } + + if (!response.ok) { + console.error(`❌ Submit fehlgeschlagen: HTTP ${response.status}`, responseData); + return res.status(response.status).json({ + error: 'Fehler beim Absenden des Spielberichts', + details: responseData, + status: response.status, + statusText: response.statusText + }); + } + + // Speichere neue Cookies falls vorhanden + const newCookies = extractCookies(response.headers.raw()['set-cookie']); + if (Object.keys(newCookies).length > 0) { + cookieStore.set(uuid, { ...cookies, ...newCookies }); + if (reportData.gameCode) { + cookieStore.set(reportData.gameCode, { ...cookies, ...newCookies }); + } + } + + // CORS-Header setzen + res.set({ + 'Content-Type': 'application/json; charset=utf-8', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type', + 'Cache-Control': 'no-cache, no-store, must-revalidate' + }); + + res.json({ + success: true, + data: responseData, + message: 'Spielbericht erfolgreich abgesendet' + }); + + } catch (error) { + console.error(`❌ Fehler beim Absenden des Spielberichts für UUID ${uuid}:`, error); + res.status(500).json({ + error: 'Fehler beim Absenden des Spielberichts', + details: error.message + }); + } +}); + export default router; diff --git a/backend/server.js b/backend/server.js index 1efab7d..d265a5f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -176,6 +176,14 @@ app.get('*', (req, res) => { await renameColumnIfExists('official_tournaments', 'meldeschluesse', 'registration_deadlines', 'TEXT NULL'); const isDev = process.env.STAGE === 'dev'; + + // Foreign Keys temporär deaktivieren für MySQL + try { + await sequelize.query('SET FOREIGN_KEY_CHECKS = 0'); + } catch (e) { + console.warn('[sync] Could not disable foreign key checks:', e?.message); + } + const safeSync = async (model) => { try { if (isDev) { @@ -235,6 +243,15 @@ app.get('*', (req, res) => { await safeSync(ApiLog); await safeSync(MemberTransferConfig); await safeSync(MemberContact); + await safeSync(ClubTeam); + await safeSync(TeamDocument); + + // Foreign Keys wieder aktivieren + try { + await sequelize.query('SET FOREIGN_KEY_CHECKS = 1'); + } catch (e) { + console.warn('[sync] Could not enable foreign key checks:', e?.message); + } // Start scheduler service schedulerService.start(); diff --git a/frontend/src/components/MatchReportApiDialog.vue b/frontend/src/components/MatchReportApiDialog.vue index 35dbe82..2fda516 100644 --- a/frontend/src/components/MatchReportApiDialog.vue +++ b/frontend/src/components/MatchReportApiDialog.vue @@ -113,6 +113,19 @@ +
+ + +
+
{{ (meetingDetails && meetingDetails.playMode) || meetingData.playMode || meetingData.matchSystem || meetingData.system }} @@ -526,8 +539,9 @@ type="password" class="pin-input" :placeholder="match.homePin || 'PIN eingeben'" - :disabled="isMatchCompleted" + :disabled="isMatchCompleted || teamNotAppeared === 'home'" /> + (nicht erforderlich - Mannschaft nicht angetreten)
@@ -538,8 +552,9 @@ type="password" class="pin-input" :placeholder="match.guestPin || 'PIN eingeben'" - :disabled="isMatchCompleted" + :disabled="isMatchCompleted || teamNotAppeared === 'guest'" /> + (nicht erforderlich - Mannschaft nicht angetreten)
@@ -652,7 +667,7 @@