Fügt Unterstützung für die neue nuscore API hinzu. Aktualisiert die Backend-Routen zur Verarbeitung von Anfragen an die nuscore API und integriert die neuen Dialogkomponenten im Frontend. Ermöglicht das Erstellen lokaler Kopien von nuscore-Daten und verbessert die Benutzeroberfläche durch neue Schaltflächen und Dialoge. Entfernt veraltete Konsolenausgaben und optimiert die Logik zur PIN-Verwaltung.
This commit is contained in:
168
backend/node_modules/.package-lock.json
generated
vendored
168
backend/node_modules/.package-lock.json
generated
vendored
@@ -595,6 +595,26 @@
|
||||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -1278,6 +1298,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",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
@@ -1778,6 +1807,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",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -1902,6 +1954,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/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -2816,6 +2880,26 @@
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||
},
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
|
||||
@@ -2823,22 +2907,21 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"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": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
@@ -3124,6 +3207,51 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
|
||||
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.55.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
|
||||
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"ideallyInert": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -3792,7 +3920,8 @@
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.7.0",
|
||||
@@ -3992,15 +4121,26 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
|
||||
2
backend/node_modules/node-fetch/LICENSE.md
generated
vendored
2
backend/node_modules/node-fetch/LICENSE.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 David Frank
|
||||
Copyright (c) 2016 - 2020 Node Fetch Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
816
backend/node_modules/node-fetch/README.md
generated
vendored
816
backend/node_modules/node-fetch/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
25
backend/node_modules/node-fetch/browser.js
generated
vendored
25
backend/node_modules/node-fetch/browser.js
generated
vendored
@@ -1,25 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
// ref: https://github.com/tc39/proposal-global
|
||||
var getGlobal = function () {
|
||||
// the only reliable means to get the global object is
|
||||
// `Function('return this')()`
|
||||
// However, this causes CSP violations in Chrome apps.
|
||||
if (typeof self !== 'undefined') { return self; }
|
||||
if (typeof window !== 'undefined') { return window; }
|
||||
if (typeof global !== 'undefined') { return global; }
|
||||
throw new Error('unable to locate global object');
|
||||
}
|
||||
|
||||
var globalObject = getGlobal();
|
||||
|
||||
module.exports = exports = globalObject.fetch;
|
||||
|
||||
// Needed for TypeScript and Webpack.
|
||||
if (globalObject.fetch) {
|
||||
exports.default = globalObject.fetch.bind(globalObject);
|
||||
}
|
||||
|
||||
exports.Headers = globalObject.Headers;
|
||||
exports.Request = globalObject.Request;
|
||||
exports.Response = globalObject.Response;
|
||||
1777
backend/node_modules/node-fetch/lib/index.es.js
generated
vendored
1777
backend/node_modules/node-fetch/lib/index.es.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1787
backend/node_modules/node-fetch/lib/index.js
generated
vendored
1787
backend/node_modules/node-fetch/lib/index.js
generated
vendored
File diff suppressed because it is too large
Load Diff
1775
backend/node_modules/node-fetch/lib/index.mjs
generated
vendored
1775
backend/node_modules/node-fetch/lib/index.mjs
generated
vendored
File diff suppressed because it is too large
Load Diff
214
backend/node_modules/node-fetch/package.json
generated
vendored
214
backend/node_modules/node-fetch/package.json
generated
vendored
@@ -1,89 +1,131 @@
|
||||
{
|
||||
"name": "node-fetch",
|
||||
"version": "2.7.0",
|
||||
"description": "A light-weight module that brings window.fetch to node.js",
|
||||
"main": "lib/index.js",
|
||||
"browser": "./browser.js",
|
||||
"module": "lib/index.mjs",
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/index.mjs",
|
||||
"lib/index.es.js",
|
||||
"browser.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=rollup rollup -c",
|
||||
"prepare": "npm run build",
|
||||
"test": "cross-env BABEL_ENV=test mocha --require babel-register --throw-deprecation test/test.js",
|
||||
"report": "cross-env BABEL_ENV=coverage nyc --reporter lcov --reporter text mocha -R spec test/test.js",
|
||||
"coverage": "cross-env BABEL_ENV=coverage nyc --reporter json --reporter text mocha -R spec test/test.js && codecov -f coverage/coverage-final.json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bitinn/node-fetch.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fetch",
|
||||
"http",
|
||||
"promise"
|
||||
],
|
||||
"author": "David Frank",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/bitinn/node-fetch/issues"
|
||||
},
|
||||
"homepage": "https://github.com/bitinn/node-fetch",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ungap/url-search-params": "^0.1.2",
|
||||
"abort-controller": "^1.1.0",
|
||||
"abortcontroller-polyfill": "^1.3.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-plugin-istanbul": "^4.1.6",
|
||||
"babel-plugin-transform-async-generator-functions": "^6.24.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "1.4.0",
|
||||
"babel-register": "^6.16.3",
|
||||
"chai": "^3.5.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-iterator": "^1.1.1",
|
||||
"chai-string": "~1.3.0",
|
||||
"codecov": "3.3.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"form-data": "^2.3.3",
|
||||
"is-builtin-module": "^1.0.0",
|
||||
"mocha": "^5.0.0",
|
||||
"nyc": "11.9.0",
|
||||
"parted": "^0.1.1",
|
||||
"promise": "^8.0.3",
|
||||
"resumer": "0.0.0",
|
||||
"rollup": "^0.63.4",
|
||||
"rollup-plugin-babel": "^3.0.7",
|
||||
"string-to-arraybuffer": "^1.0.2",
|
||||
"teeny-request": "3.7.0"
|
||||
},
|
||||
"release": {
|
||||
"branches": [
|
||||
"+([0-9]).x",
|
||||
"main",
|
||||
"next",
|
||||
{
|
||||
"name": "beta",
|
||||
"prerelease": true
|
||||
}
|
||||
]
|
||||
"name": "node-fetch",
|
||||
"version": "3.3.2",
|
||||
"description": "A light-weight module that brings Fetch API to node.js",
|
||||
"main": "./src/index.js",
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"files": [
|
||||
"src",
|
||||
"@types/index.d.ts"
|
||||
],
|
||||
"types": "./@types/index.d.ts",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
"coverage": "c8 report --reporter=text-lcov | coveralls",
|
||||
"test-types": "tsd",
|
||||
"lint": "xo"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/node-fetch/node-fetch.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fetch",
|
||||
"http",
|
||||
"promise",
|
||||
"request",
|
||||
"curl",
|
||||
"wget",
|
||||
"xhr",
|
||||
"whatwg"
|
||||
],
|
||||
"author": "David Frank",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/node-fetch/node-fetch/issues"
|
||||
},
|
||||
"homepage": "https://github.com/node-fetch/node-fetch",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.1",
|
||||
"busboy": "^1.4.0",
|
||||
"c8": "^7.7.2",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-iterator": "^3.0.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"coveralls": "^3.1.0",
|
||||
"form-data": "^4.0.0",
|
||||
"formdata-node": "^4.2.4",
|
||||
"mocha": "^9.1.3",
|
||||
"p-timeout": "^5.0.0",
|
||||
"stream-consumers": "^1.0.1",
|
||||
"tsd": "^0.14.0",
|
||||
"xo": "^0.39.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"tsd": {
|
||||
"cwd": "@types",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
},
|
||||
"xo": {
|
||||
"envs": [
|
||||
"node",
|
||||
"browser"
|
||||
],
|
||||
"ignores": [
|
||||
"example.js"
|
||||
],
|
||||
"rules": {
|
||||
"complexity": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-useless-path-segments": 0,
|
||||
"import/no-anonymous-default-export": 0,
|
||||
"import/no-named-as-default": 0,
|
||||
"unicorn/import-index": 0,
|
||||
"unicorn/no-array-reduce": 0,
|
||||
"unicorn/prefer-node-protocol": 0,
|
||||
"unicorn/numeric-separators-style": 0,
|
||||
"unicorn/explicit-length-check": 0,
|
||||
"capitalized-comments": 0,
|
||||
"node/no-unsupported-features/es-syntax": 0,
|
||||
"@typescript-eslint/member-ordering": 0
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": "test/**/*.js",
|
||||
"envs": [
|
||||
"node",
|
||||
"mocha"
|
||||
],
|
||||
"rules": {
|
||||
"max-nested-callbacks": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"no-warning-comments": 0,
|
||||
"new-cap": 0,
|
||||
"guard-for-in": 0,
|
||||
"unicorn/no-array-for-each": 0,
|
||||
"unicorn/prevent-abbreviations": 0,
|
||||
"promise/prefer-await-to-then": 0,
|
||||
"ava/no-import-test-files": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"runkitExampleFilename": "example.js",
|
||||
"release": {
|
||||
"branches": [
|
||||
"+([0-9]).x",
|
||||
"main",
|
||||
"next",
|
||||
{
|
||||
"name": "beta",
|
||||
"prerelease": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
169
backend/package-lock.json
generated
169
backend/package-lock.json
generated
@@ -22,8 +22,10 @@
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.3",
|
||||
"node-fetch": "^3.3.2",
|
||||
"nodemailer": "^6.9.14",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"playwright": "^1.55.1",
|
||||
"sequelize": "^6.37.3",
|
||||
"sharp": "^0.33.5"
|
||||
},
|
||||
@@ -607,6 +609,26 @@
|
||||
"node-pre-gyp": "bin/node-pre-gyp"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@@ -1290,6 +1312,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",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
@@ -1790,6 +1821,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",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -1914,6 +1968,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/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -2827,6 +2893,26 @@
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||
},
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
|
||||
@@ -2834,22 +2920,21 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"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": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
@@ -3135,6 +3220,50 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
|
||||
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.55.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.55.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
|
||||
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -3803,7 +3932,8 @@
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.7.0",
|
||||
@@ -4002,15 +4132,26 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"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",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.3",
|
||||
"node-fetch": "^3.3.2",
|
||||
"nodemailer": "^6.9.14",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"playwright": "^1.55.1",
|
||||
"sequelize": "^6.37.3",
|
||||
"sharp": "^0.33.5"
|
||||
},
|
||||
|
||||
193
backend/routes/nuscoreApiRoutes.js
Normal file
193
backend/routes/nuscoreApiRoutes.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import express from 'express';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Cookie-Store für nuscore-Sessions
|
||||
const cookieStore = new Map();
|
||||
|
||||
// Hilfsfunktion zum Extrahieren von Cookies aus Set-Cookie Headers
|
||||
function extractCookies(setCookieHeaders) {
|
||||
const cookies = {};
|
||||
if (setCookieHeaders) {
|
||||
setCookieHeaders.forEach(cookie => {
|
||||
const [nameValue] = cookie.split(';');
|
||||
const [name, value] = nameValue.split('=');
|
||||
if (name && value) {
|
||||
cookies[name.trim()] = value.trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
|
||||
// Hilfsfunktion zum Formatieren von Cookies für Requests
|
||||
function formatCookies(cookies) {
|
||||
return Object.entries(cookies)
|
||||
.map(([name, value]) => `${name}=${value}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
// Meeting-Info API-Endpunkt
|
||||
router.get('/meetinginfo/:code', async (req, res) => {
|
||||
const { code } = req.params;
|
||||
console.log(`📊 Meeting-Info API für Code: ${code}`);
|
||||
|
||||
try {
|
||||
// Hole Cookies für diesen Code (falls vorhanden)
|
||||
const cookies = cookieStore.get(code) || {};
|
||||
|
||||
const url = `https://ttde-apps.liga.nu/nuliga/rs/tt/2022/meetingentry/reports/${code}/meetinginfo`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
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',
|
||||
'Referer': 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'Connection': 'keep-alive',
|
||||
...(Object.keys(cookies).length > 0 && { 'Cookie': formatCookies(cookies) })
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Speichere neue Cookies falls vorhanden
|
||||
const newCookies = extractCookies(response.headers.raw()['set-cookie']);
|
||||
if (Object.keys(newCookies).length > 0) {
|
||||
cookieStore.set(code, { ...cookies, ...newCookies });
|
||||
console.log(`🍪 Cookies für Code ${code} gespeichert:`, Object.keys(newCookies));
|
||||
}
|
||||
|
||||
// CORS-Header setzen
|
||||
res.set({
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate'
|
||||
});
|
||||
|
||||
res.json(data);
|
||||
console.log(`✅ Meeting-Info für Code ${code} erfolgreich abgerufen`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler beim Abrufen der Meeting-Info für Code ${code}:`, error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler beim Abrufen der Meeting-Info',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Cookie-Initialisierung (für den ersten Request)
|
||||
router.post('/init-cookies/:code', async (req, res) => {
|
||||
const { code } = req.params;
|
||||
console.log(`🍪 Cookie-Initialisierung für Code: ${code}`);
|
||||
|
||||
try {
|
||||
// Erster Request an die nuscore-Seite um Cookies zu erhalten
|
||||
const response = await fetch(`https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list?code=${code}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
||||
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||
'Connection': 'keep-alive'
|
||||
}
|
||||
});
|
||||
|
||||
// Extrahiere Cookies
|
||||
const cookies = extractCookies(response.headers.raw()['set-cookie']);
|
||||
if (Object.keys(cookies).length > 0) {
|
||||
cookieStore.set(code, cookies);
|
||||
console.log(`🍪 Cookies für Code ${code} initialisiert:`, Object.keys(cookies));
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
cookies: Object.keys(cookies),
|
||||
message: `Cookies für Code ${code} initialisiert`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler bei Cookie-Initialisierung für Code ${code}:`, error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler bei Cookie-Initialisierung',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Detaillierte Meeting-Daten API-Endpunkt
|
||||
router.get('/meetingdetails/:uuid', async (req, res) => {
|
||||
const { uuid } = req.params;
|
||||
console.log(`📊 Meeting-Details API für UUID: ${uuid}`);
|
||||
|
||||
try {
|
||||
// Hole Cookies für diesen Code (falls vorhanden)
|
||||
const cookies = cookieStore.get(uuid) || {};
|
||||
|
||||
const url = `https://ttde-apps.liga.nu/nuliga/rs/tt/2022/meetingentry/reports/${uuid}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
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',
|
||||
'Referer': 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'Connection': 'keep-alive',
|
||||
...(Object.keys(cookies).length > 0 && { 'Cookie': formatCookies(cookies) })
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Speichere neue Cookies falls vorhanden
|
||||
const newCookies = extractCookies(response.headers.raw()['set-cookie']);
|
||||
if (Object.keys(newCookies).length > 0) {
|
||||
cookieStore.set(uuid, { ...cookies, ...newCookies });
|
||||
console.log(`🍪 Cookies für UUID ${uuid} gespeichert:`, Object.keys(newCookies));
|
||||
}
|
||||
|
||||
// CORS-Header setzen
|
||||
res.set({
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate'
|
||||
});
|
||||
|
||||
res.json(data);
|
||||
console.log(`✅ Meeting-Details für UUID ${uuid} erfolgreich abgerufen`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler beim Abrufen der Meeting-Details für UUID ${uuid}:`, error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler beim Abrufen der Meeting-Details',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
78
backend/routes/nuscoreProxyRoutes.js
Normal file
78
backend/routes/nuscoreProxyRoutes.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import express from 'express';
|
||||
import nuscoreProxyService from '../services/nuscoreProxyService.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Hauptroute für nuscore-Seite
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { code, pin } = req.query;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({
|
||||
error: 'Code-Parameter ist erforderlich'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 Proxy-Anfrage für Code: ${code}, PIN: ${pin || 'nicht angegeben'}`);
|
||||
|
||||
const html = await nuscoreProxyService.proxyNuscorePage(code, pin);
|
||||
|
||||
// CORS-Header setzen für iframe-Einbettung
|
||||
res.set({
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'X-Frame-Options': 'ALLOWALL',
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate'
|
||||
});
|
||||
|
||||
res.send(html);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Proxy-Fehler:', error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler beim Laden der nuscore-Seite',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Asset-Proxy für CSS, JS, Bilder etc.
|
||||
router.get('/assets/*', async (req, res) => {
|
||||
try {
|
||||
const assetPath = req.params[0];
|
||||
const originalUrl = `https://ttde-apps.liga.nu/nuliga/nuscore-tt/${assetPath}`;
|
||||
|
||||
console.log(`📦 Lade Asset: ${originalUrl}`);
|
||||
|
||||
const { content, contentType } = await nuscoreProxyService.proxyAsset(originalUrl);
|
||||
|
||||
res.set({
|
||||
'Content-Type': contentType,
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Cache-Control': 'public, max-age=3600'
|
||||
});
|
||||
|
||||
res.send(content);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Asset-Fehler für ${req.params[0]}:`, error.message);
|
||||
res.status(404).json({
|
||||
error: 'Asset nicht gefunden',
|
||||
path: req.params[0]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Health-Check Route
|
||||
router.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
service: 'nuscore-proxy',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
140
backend/routes/nuscoreSimpleProxyRoutes.js
Normal file
140
backend/routes/nuscoreSimpleProxyRoutes.js
Normal file
@@ -0,0 +1,140 @@
|
||||
import express from 'express';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Einfacher HTTP-Proxy ohne Playwright
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { code, pin } = req.query;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({
|
||||
error: 'Code-Parameter ist erforderlich'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`📊 Einfacher Proxy für Code: ${code}, PIN: ${pin || 'nicht angegeben'}`);
|
||||
|
||||
// Direkte HTTP-Anfrage an nuscore
|
||||
const nuscoreUrl = `https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list?code=${encodeURIComponent(code)}`;
|
||||
|
||||
const response = await fetch(nuscoreUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
||||
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'DNT': '1',
|
||||
'Connection': 'keep-alive',
|
||||
'Upgrade-Insecure-Requests': '1'
|
||||
},
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
let html = await response.text();
|
||||
|
||||
// Einfache HTML-Modifikationen
|
||||
html = html
|
||||
.replace(/<meta[^>]*http-equiv=["']content-security-policy["'][^>]*>/gi, '')
|
||||
.replace(/<meta[^>]*http-equiv=["']x-frame-options["'][^>]*>/gi, '')
|
||||
.replace(/<meta[^>]*http-equiv=["']x-content-type-options["'][^>]*>/gi, '');
|
||||
|
||||
// Entferne Service Worker
|
||||
html = html.replace(
|
||||
/navigator\.serviceWorker\.register\([^)]+\)/g,
|
||||
'// Service Worker deaktiviert für Proxy'
|
||||
);
|
||||
|
||||
// Korrigiere alle Asset-URLs zu absoluten URLs
|
||||
html = html.replace(
|
||||
/src="\.\//g,
|
||||
'src="https://ttde-apps.liga.nu/nuliga/nuscore-tt/'
|
||||
);
|
||||
html = html.replace(
|
||||
/href="\.\//g,
|
||||
'href="https://ttde-apps.liga.nu/nuliga/nuscore-tt/'
|
||||
);
|
||||
|
||||
// Korrigiere auch URLs die bereits auf localhost zeigen
|
||||
html = html.replace(
|
||||
/src="http:\/\/localhost:3000\/nuliga\/nuscore-tt\//g,
|
||||
'src="https://ttde-apps.liga.nu/nuliga/nuscore-tt/'
|
||||
);
|
||||
html = html.replace(
|
||||
/href="http:\/\/localhost:3000\/nuliga\/nuscore-tt\//g,
|
||||
'href="https://ttde-apps.liga.nu/nuliga/nuscore-tt/'
|
||||
);
|
||||
|
||||
// Entferne vorhandene base-Tags und füge unseres hinzu
|
||||
html = html.replace(
|
||||
/<base[^>]*>/gi,
|
||||
''
|
||||
);
|
||||
|
||||
// Füge base-Tag hinzu für korrekte Asset-Auflösung
|
||||
html = html.replace(
|
||||
'<head>',
|
||||
'<head><base href="https://ttde-apps.liga.nu/nuliga/nuscore-tt/">'
|
||||
);
|
||||
|
||||
// Debug: Logge die HTML-Struktur
|
||||
console.log('🔍 HTML nach base-Tag:', html.substring(html.indexOf('<base'), html.indexOf('</head>') + 7));
|
||||
|
||||
// Füge PIN-Injektion hinzu falls PIN vorhanden
|
||||
if (pin) {
|
||||
const pinScript = `
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
setTimeout(function() {
|
||||
const pinInput = document.querySelector('input[type="password"][placeholder*="PIN"], input[placeholder*="Pin"], input[placeholder*="pin"]');
|
||||
if (pinInput) {
|
||||
pinInput.value = '${pin}';
|
||||
pinInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pinInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
console.log('PIN automatisch eingefügt: ${pin}');
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
`;
|
||||
html = html.replace('</head>', `${pinScript}</head>`);
|
||||
}
|
||||
|
||||
// CORS-Header setzen für iframe-Einbettung
|
||||
res.set({
|
||||
'Content-Type': 'text/html; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'X-Frame-Options': 'ALLOWALL',
|
||||
'Content-Security-Policy': "frame-ancestors *;",
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate'
|
||||
});
|
||||
|
||||
res.send(html);
|
||||
console.log('✅ Einfacher Proxy erfolgreich');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Einfacher Proxy-Fehler:', error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler beim Laden der nuscore-Seite',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Health-Check Route
|
||||
router.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
service: 'nuscore-simple-proxy',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -38,6 +38,8 @@ import teamRoutes from './routes/teamRoutes.js';
|
||||
import clubTeamRoutes from './routes/clubTeamRoutes.js';
|
||||
import teamDocumentRoutes from './routes/teamDocumentRoutes.js';
|
||||
import seasonRoutes from './routes/seasonRoutes.js';
|
||||
import nuscoreSimpleProxyRoutes from './routes/nuscoreSimpleProxyRoutes.js';
|
||||
import nuscoreApiRoutes from './routes/nuscoreApiRoutes.js';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
@@ -87,6 +89,8 @@ app.use('/api/teams', teamRoutes);
|
||||
app.use('/api/club-teams', clubTeamRoutes);
|
||||
app.use('/api/team-documents', teamDocumentRoutes);
|
||||
app.use('/api/seasons', seasonRoutes);
|
||||
app.use('/api/proxy/nuscore', nuscoreSimpleProxyRoutes);
|
||||
app.use('/api/nuscore', nuscoreApiRoutes);
|
||||
|
||||
app.use(express.static(path.join(__dirname, '../frontend/dist')));
|
||||
|
||||
|
||||
148
backend/services/nuscoreProxyService.js
Normal file
148
backend/services/nuscoreProxyService.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
class NuscoreProxyService {
|
||||
constructor() {
|
||||
this.browser = null;
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
if (!this.browser) {
|
||||
console.log('🚀 Initialisiere Playwright Browser...');
|
||||
this.browser = await chromium.launch({
|
||||
headless: true,
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-web-security',
|
||||
'--disable-features=VizDisplayCompositor'
|
||||
]
|
||||
});
|
||||
|
||||
this.context = await this.browser.newContext({
|
||||
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
ignoreHTTPSErrors: true
|
||||
});
|
||||
|
||||
console.log('✅ Playwright Browser initialisiert');
|
||||
}
|
||||
}
|
||||
|
||||
async proxyNuscorePage(code, pin = null) {
|
||||
try {
|
||||
await this.initialize();
|
||||
|
||||
console.log(`🔍 Lade nuscore-Seite für Code: ${code}`);
|
||||
|
||||
const page = await this.context.newPage();
|
||||
|
||||
// Navigiere direkt zur nuscore-Seite ohne Request-Interception
|
||||
const nuscoreUrl = `https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list?code=${encodeURIComponent(code)}`;
|
||||
await page.goto(nuscoreUrl, {
|
||||
waitUntil: 'load',
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
console.log('📄 Seite geladen');
|
||||
|
||||
// Optional: PIN automatisch einfügen falls vorhanden
|
||||
if (pin) {
|
||||
console.log(`🔑 Versuche PIN ${pin} einzufügen...`);
|
||||
try {
|
||||
// Suche nach PIN-Eingabefeld
|
||||
const pinInput = await page.locator('input[type="password"][placeholder*="PIN"], input[placeholder*="Pin"], input[placeholder*="pin"]').first();
|
||||
if (await pinInput.isVisible()) {
|
||||
await pinInput.fill(pin);
|
||||
console.log('✅ PIN eingefügt');
|
||||
|
||||
// Optional: Submit-Button klicken falls vorhanden
|
||||
const submitBtn = await page.locator('button[type="submit"], input[type="submit"], button:has-text("Einloggen"), button:has-text("Anmelden")').first();
|
||||
if (await submitBtn.isVisible()) {
|
||||
await submitBtn.click();
|
||||
await page.waitForLoadState('load', { timeout: 5000 });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('⚠️ PIN-Einfügung fehlgeschlagen:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// HTML-Inhalt abrufen und modifizieren
|
||||
const html = await page.content();
|
||||
|
||||
// HTML modifizieren für Proxy-Betrieb
|
||||
const modifiedHtml = this.modifyHtmlForProxy(html, code);
|
||||
|
||||
await page.close();
|
||||
|
||||
console.log('✅ nuscore-Seite erfolgreich proxiert');
|
||||
return modifiedHtml;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler beim Proxying der nuscore-Seite:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
modifyHtmlForProxy(html, code) {
|
||||
// Entferne problematische Headers/Meta-Tags
|
||||
let modified = html
|
||||
.replace(/<meta[^>]*http-equiv=["']content-security-policy["'][^>]*>/gi, '')
|
||||
.replace(/<meta[^>]*http-equiv=["']x-frame-options["'][^>]*>/gi, '')
|
||||
.replace(/<meta[^>]*http-equiv=["']x-content-type-options["'][^>]*>/gi, '');
|
||||
|
||||
// Entferne Service Worker Registrierung (kann Probleme verursachen)
|
||||
modified = modified.replace(
|
||||
/navigator\.serviceWorker\.register\([^)]+\)/g,
|
||||
'// Service Worker deaktiviert für Proxy'
|
||||
);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
async proxyAsset(url) {
|
||||
try {
|
||||
await this.initialize();
|
||||
|
||||
const page = await this.context.newPage();
|
||||
const response = await page.goto(url, {
|
||||
waitUntil: 'networkidle',
|
||||
timeout: 15000
|
||||
});
|
||||
|
||||
if (!response || !response.ok()) {
|
||||
throw new Error(`Asset nicht erreichbar: ${url}`);
|
||||
}
|
||||
|
||||
const content = await response.body();
|
||||
const contentType = response.headers()['content-type'] || 'application/octet-stream';
|
||||
|
||||
await page.close();
|
||||
|
||||
return { content, contentType };
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler beim Laden von Asset ${url}:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
if (this.context) {
|
||||
await this.context.close();
|
||||
this.context = null;
|
||||
}
|
||||
if (this.browser) {
|
||||
await this.browser.close();
|
||||
this.browser = null;
|
||||
}
|
||||
console.log('🧹 Playwright Browser bereinigt');
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton-Instanz
|
||||
const nuscoreProxyService = new NuscoreProxyService();
|
||||
|
||||
export default nuscoreProxyService;
|
||||
7
frontend/package-lock.json
generated
7
frontend/package-lock.json
generated
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.3",
|
||||
"core-js": "^3.8.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.2",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
@@ -1375,6 +1376,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.3",
|
||||
"core-js": "^3.8.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jspdf": "^2.5.2",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
|
||||
@@ -54,11 +54,13 @@
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import MatchReportDialog from './MatchReportDialog.vue';
|
||||
import MatchReportApiDialog from './MatchReportApiDialog.vue';
|
||||
import MatchReportHeaderActions from './MatchReportHeaderActions.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MatchReportDialog,
|
||||
MatchReportApiDialog,
|
||||
MatchReportHeaderActions
|
||||
},
|
||||
name: 'DialogManager',
|
||||
@@ -69,11 +71,15 @@ export default {
|
||||
...mapActions(['closeDialog', 'minimizeDialog', 'restoreDialog', 'bringDialogToFront']),
|
||||
|
||||
getDialogComponent(componentName) {
|
||||
console.log('🔍 Suche Komponente:', componentName);
|
||||
const components = {
|
||||
'MatchReportDialog': MatchReportDialog,
|
||||
'MatchReportApiDialog': MatchReportApiDialog,
|
||||
'MatchReportHeaderActions': MatchReportHeaderActions
|
||||
};
|
||||
return components[componentName] || null;
|
||||
const component = components[componentName] || null;
|
||||
console.log('🔍 Gefundene Komponente:', component ? component.name : 'null');
|
||||
return component;
|
||||
},
|
||||
|
||||
bringToFront(dialogId) {
|
||||
@@ -93,101 +99,31 @@ export default {
|
||||
|
||||
// Behandle die verschiedenen Action-Types
|
||||
if (action.type === 'insertPin') {
|
||||
// PIN ins iframe einfügen
|
||||
// PIN via postMessage an das zuletzt geöffnete MatchReport-iframe senden
|
||||
this.insertPinIntoIframe(action.match);
|
||||
}
|
||||
},
|
||||
|
||||
insertPinIntoIframe(match) {
|
||||
console.log('🔍 PIN-Einfügen gestartet für Match:', match);
|
||||
console.log('📌 Verfügbare PINs:', {
|
||||
homePin: match.homePin,
|
||||
guestPin: match.guestPin
|
||||
});
|
||||
|
||||
// Versuche direkten Zugriff auf die MatchReportDialog-Komponente
|
||||
const matchReportDialogs = document.querySelectorAll('.match-report-dialog');
|
||||
console.log('🖼️ Gefundene MatchReportDialogs:', matchReportDialogs.length);
|
||||
|
||||
if (matchReportDialogs.length > 0) {
|
||||
// Versuche die insertPinManually Methode aufzurufen
|
||||
console.log('🎯 Versuche direkten Zugriff auf MatchReportDialog');
|
||||
|
||||
// Finde das iframe im aktuellen Dialog
|
||||
const iframe = matchReportDialogs[matchReportDialogs.length - 1].querySelector('iframe');
|
||||
if (iframe) {
|
||||
console.log('✅ Iframe gefunden, versuche PIN-Einfügung');
|
||||
|
||||
try {
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (iframeDoc) {
|
||||
console.log('✅ Direkter DOM-Zugriff möglich');
|
||||
|
||||
// Suche nach PIN-Feldern
|
||||
const pinSelectors = [
|
||||
'input[type="password"][placeholder*="Vereins"]',
|
||||
'input[type="password"][placeholder*="Spiel-Pin"]',
|
||||
'input[type="password"][placeholder*="PIN"]',
|
||||
'input[type="password"]',
|
||||
'input[placeholder*="Vereins"]',
|
||||
'input[placeholder*="Spiel-Pin"]'
|
||||
];
|
||||
|
||||
let pinField = null;
|
||||
for (const selector of pinSelectors) {
|
||||
pinField = iframeDoc.querySelector(selector);
|
||||
if (pinField) {
|
||||
console.log(`✅ PIN-Feld gefunden mit Selektor: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinField && match.homePin) {
|
||||
console.log('📝 Füge PIN ein:', match.homePin);
|
||||
pinField.value = match.homePin;
|
||||
pinField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
console.log('✅ PIN erfolgreich eingefügt');
|
||||
return;
|
||||
} else {
|
||||
console.log('❌ PIN-Feld nicht gefunden');
|
||||
console.log('🔍 Verfügbare Input-Felder:', iframeDoc.querySelectorAll('input'));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('🚫 Cross-Origin-Zugriff blockiert (erwartet)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: PostMessage verwenden
|
||||
console.log('🔍 PIN-Einfügen gestartet (postMessage)');
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
if (iframes.length > 0) {
|
||||
const iframe = iframes[iframes.length - 1]; // Neuestes iframe
|
||||
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
|
||||
const origins = ['https://ttde-apps.liga.nu', 'https://liga.nu', '*'];
|
||||
origins.forEach(origin => {
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, origin);
|
||||
console.log(`📤 PostMessage an ${origin} gesendet`);
|
||||
} catch (e) {
|
||||
console.log(`❌ PostMessage an ${origin} fehlgeschlagen:`, e.message);
|
||||
}
|
||||
});
|
||||
if (iframes.length === 0) {
|
||||
console.log('❌ Kein iframe gefunden');
|
||||
return;
|
||||
}
|
||||
const iframe = iframes[iframes.length - 1];
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: match.homePin || match.guestPin || '',
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, 'http://localhost:3000');
|
||||
console.log('📤 PostMessage an http://localhost:3000 gesendet');
|
||||
} catch (e) {
|
||||
console.log('PostMessage Fehler (nicht kritisch):', e.message);
|
||||
}
|
||||
|
||||
console.log('💡 Alternative: Verwenden Sie den "📋 PIN kopieren" Button');
|
||||
console.log('📋 PIN zum Kopieren:', match.homePin || match.guestPin);
|
||||
},
|
||||
|
||||
handlePostMessage(event) {
|
||||
@@ -195,8 +131,8 @@ export default {
|
||||
console.log('- Origin:', event.origin);
|
||||
console.log('- Data:', event.data);
|
||||
|
||||
// Nur Nachrichten von nuscore verarbeiten
|
||||
if (event.origin !== 'https://ttde-apps.liga.nu' && event.origin !== 'https://liga.nu') {
|
||||
// Nur Nachrichten von unserem Proxy verarbeiten
|
||||
if (event.origin !== 'http://localhost:3000') {
|
||||
console.log('🚫 Nachricht von unbekannter Origin ignoriert');
|
||||
return;
|
||||
}
|
||||
@@ -306,7 +242,7 @@ export default {
|
||||
|
||||
.dialog-content {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
1704
frontend/src/components/MatchReportApiDialog.vue
Normal file
1704
frontend/src/components/MatchReportApiDialog.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,18 @@
|
||||
<template>
|
||||
<div class="match-report-dialog">
|
||||
<!-- nuscore Analyzer für lokale Kopie -->
|
||||
<NuscoreAnalyzer v-if="showAnalyzer" @close="showAnalyzer = false" />
|
||||
|
||||
<!-- Toggle für Analyzer -->
|
||||
<div class="analyzer-toggle">
|
||||
<button @click="showAnalyzer = !showAnalyzer" class="toggle-btn">
|
||||
{{ showAnalyzer ? '🔼 Analyzer verstecken' : '🔍 nuscore analysieren' }}
|
||||
</button>
|
||||
<button @click="createLocalCopy" class="toggle-btn" style="margin-left: 10px;">
|
||||
💾 Lokale Kopie erstellen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="report-content">
|
||||
<iframe
|
||||
ref="reportIframe"
|
||||
@@ -15,27 +28,29 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NuscoreAnalyzer from './NuscoreAnalyzer.vue';
|
||||
import { generateSimpleNuscoreHTML } from '../utils/simpleNuscoreHTMLGenerator.js';
|
||||
|
||||
export default {
|
||||
name: 'MatchReportDialog',
|
||||
components: {
|
||||
NuscoreAnalyzer
|
||||
},
|
||||
props: {
|
||||
match: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAnalyzer: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
reportUrl() {
|
||||
// Verschiedene URL-Parameter versuchen, die nuscore möglicherweise unterstützt
|
||||
const baseUrl = 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list';
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// Verschiedene Parameter-Namen versuchen
|
||||
params.set('code', this.match.code);
|
||||
params.set('gamecode', this.match.code);
|
||||
params.set('spielcode', this.match.code);
|
||||
params.set('matchcode', this.match.code);
|
||||
|
||||
return `${baseUrl}?${params.toString()}`;
|
||||
// Verwende die neue API-basierte Lösung
|
||||
return `http://localhost:3000/api/nuscore/meetinginfo/${this.match.code}`;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -64,158 +79,24 @@ export default {
|
||||
|
||||
onIframeLoad() {
|
||||
console.log('🔄 Iframe geladen, URL:', this.$refs.reportIframe?.src);
|
||||
console.log('📊 Iframe-Inhalt:', this.$refs.reportIframe?.contentDocument?.title || 'Kein Titel');
|
||||
|
||||
// Warte kurz, damit das iframe vollständig geladen ist
|
||||
setTimeout(() => {
|
||||
this.injectContentScript();
|
||||
}, 2000);
|
||||
|
||||
// Überwache URL-Änderungen im iframe
|
||||
// Sende den Code an das iframe über PostMessage
|
||||
this.tryPostMessage();
|
||||
|
||||
// Überwache URL-Änderungen im iframe (nur Logging, da kein direkter Zugriff möglich)
|
||||
this.startUrlMonitoring();
|
||||
},
|
||||
|
||||
injectContentScript() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (!iframe || !iframe.contentWindow) {
|
||||
console.log('Iframe noch nicht bereit für Content Script');
|
||||
return;
|
||||
}
|
||||
|
||||
// Content Script als String definieren
|
||||
const contentScript = `
|
||||
(function() {
|
||||
console.log('Content Script geladen');
|
||||
|
||||
// Warte bis die Seite vollständig geladen ist
|
||||
function waitForElement(selector, callback) {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
callback(element);
|
||||
} else {
|
||||
setTimeout(() => waitForElement(selector, callback), 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Suche nach dem Input-Feld
|
||||
waitForElement('#gamecode', function(input) {
|
||||
console.log('Input-Feld gefunden:', input);
|
||||
|
||||
// Code einfügen
|
||||
input.value = '${this.match.code}';
|
||||
|
||||
// Events auslösen
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
|
||||
console.log('Code eingefügt:', '${this.match.code}');
|
||||
|
||||
// Suche nach dem Button und klicke ihn
|
||||
setTimeout(() => {
|
||||
const button = document.querySelector('button.btn-primary');
|
||||
if (button) {
|
||||
console.log('Button gefunden, klicke ihn');
|
||||
button.click();
|
||||
} else {
|
||||
console.log('Button nicht gefunden');
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
})();
|
||||
`;
|
||||
|
||||
// Script in das iframe injizieren
|
||||
const script = iframe.contentDocument.createElement('script');
|
||||
script.textContent = contentScript;
|
||||
iframe.contentDocument.head.appendChild(script);
|
||||
|
||||
console.log('Content Script injiziert');
|
||||
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Injizieren des Content Scripts:', error);
|
||||
// Fallback zu PostMessage
|
||||
this.tryPostMessage();
|
||||
}
|
||||
},
|
||||
|
||||
fillGameCode() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (!iframe || !iframe.contentWindow) {
|
||||
console.log('Iframe noch nicht bereit');
|
||||
return;
|
||||
}
|
||||
|
||||
// Versuche, das Input-Feld zu finden und zu füllen
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
if (iframeDoc) {
|
||||
const gameCodeInput = iframeDoc.getElementById('gamecode');
|
||||
if (gameCodeInput) {
|
||||
// Code in das Input-Feld einfügen
|
||||
gameCodeInput.value = this.match.code;
|
||||
|
||||
// Event auslösen, damit Angular die Änderung erkennt
|
||||
gameCodeInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
gameCodeInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
console.log('Spielcode erfolgreich eingefügt:', this.match.code);
|
||||
|
||||
// Optional: Automatisch den Button klicken
|
||||
setTimeout(() => {
|
||||
this.clickLoadButton(iframeDoc);
|
||||
}, 500);
|
||||
} else {
|
||||
console.log('Input-Feld mit ID "gamecode" nicht gefunden');
|
||||
}
|
||||
} else {
|
||||
console.log('Kein Zugriff auf iframe-Dokument (Cross-Origin)');
|
||||
// Fallback: PostMessage verwenden
|
||||
this.tryPostMessage();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Zugriff auf iframe:', error);
|
||||
// Fallback: PostMessage verwenden
|
||||
this.tryPostMessage();
|
||||
}
|
||||
},
|
||||
|
||||
clickLoadButton(iframeDoc) {
|
||||
try {
|
||||
// Suche nach dem Button (verschiedene Selektoren)
|
||||
const buttonSelectors = [
|
||||
'button.btn-primary',
|
||||
'button[type="button"]',
|
||||
'button:contains("Laden")'
|
||||
];
|
||||
|
||||
let loadButton = null;
|
||||
for (const selector of buttonSelectors) {
|
||||
loadButton = iframeDoc.querySelector(selector);
|
||||
if (loadButton) break;
|
||||
}
|
||||
|
||||
if (loadButton) {
|
||||
loadButton.click();
|
||||
console.log('Laden-Button erfolgreich geklickt');
|
||||
} else {
|
||||
console.log('Laden-Button nicht gefunden');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Fehler beim Klicken des Buttons:', error);
|
||||
}
|
||||
},
|
||||
|
||||
tryPostMessage() {
|
||||
try {
|
||||
const iframe = this.$refs.reportIframe;
|
||||
if (iframe && iframe.contentWindow) {
|
||||
// Sende Nachricht an das iframe
|
||||
// Sende Nachricht an das iframe (jetzt localhost:3000)
|
||||
iframe.contentWindow.postMessage({
|
||||
action: 'fillGameCode',
|
||||
code: this.match.code
|
||||
}, 'https://ttde-apps.liga.nu');
|
||||
}, 'http://localhost:3000');
|
||||
|
||||
console.log('PostMessage gesendet mit Code:', this.match.code);
|
||||
}
|
||||
@@ -260,52 +141,18 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kein DOM-Zugriff mehr; ausschließlich postMessage senden
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: this.match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
try {
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
// Suche nach PIN-Feldern auf der Meeting-Seite
|
||||
const pinSelectors = [
|
||||
'input[type="password"][placeholder*="Vereins"]',
|
||||
'input[type="password"][placeholder*="Spiel-Pin"]',
|
||||
'input[type="password"][placeholder*="PIN"]',
|
||||
'input[type="password"]',
|
||||
'input[placeholder*="Vereins"]',
|
||||
'input[placeholder*="Spiel-Pin"]'
|
||||
];
|
||||
|
||||
let pinField = null;
|
||||
for (const selector of pinSelectors) {
|
||||
pinField = iframeDoc.querySelector(selector);
|
||||
if (pinField) {
|
||||
console.log(`✅ PIN-Feld gefunden mit Selektor: ${selector}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinField) {
|
||||
console.log('📝 Füge PIN ein:', this.match.homePin);
|
||||
pinField.value = this.match.homePin;
|
||||
pinField.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
pinField.dispatchEvent(new Event('blur', { bubbles: true }));
|
||||
console.log('✅ PIN erfolgreich eingefügt');
|
||||
} else {
|
||||
console.log('❌ PIN-Feld nicht gefunden');
|
||||
console.log('🔍 Verfügbare Input-Felder:', iframeDoc.querySelectorAll('input'));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log('🚫 Cross-Origin-Zugriff blockiert (erwartet)');
|
||||
|
||||
// Fallback: PostMessage
|
||||
const message = {
|
||||
action: 'fillPin',
|
||||
pin: this.match.homePin,
|
||||
timestamp: Date.now(),
|
||||
source: 'trainingstagebuch'
|
||||
};
|
||||
|
||||
console.log('📤 Sende PostMessage:', message);
|
||||
iframe.contentWindow.postMessage(message, 'https://ttde-apps.liga.nu');
|
||||
} catch (e) {
|
||||
console.log('PostMessage Fehler (nicht kritisch):', e.message);
|
||||
iframe.contentWindow.postMessage(message, '*');
|
||||
}
|
||||
},
|
||||
@@ -316,9 +163,28 @@ export default {
|
||||
this.attemptPinInsertionAfterRedirect();
|
||||
},
|
||||
|
||||
async createLocalCopy() {
|
||||
console.log('🏗️ Erstelle lokale Kopie mit PIN...');
|
||||
|
||||
// Erstelle eine einfache HTML-Datei, die die ursprüngliche nuscore-Seite in einem iframe lädt
|
||||
const html = generateSimpleNuscoreHTML(this.match);
|
||||
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'nuscore-local.html';
|
||||
link.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.log('✅ Lokale Kopie erstellt (iframe-basiert)');
|
||||
},
|
||||
|
||||
handlePostMessage(event) {
|
||||
// Nur Nachrichten von nuscore verarbeiten
|
||||
if (event.origin !== 'https://ttde-apps.liga.nu') {
|
||||
// Nur Nachrichten von unserem Proxy verarbeiten
|
||||
if (event.origin !== 'http://localhost:3000') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,6 +208,27 @@ export default {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.analyzer-toggle {
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.match-info {
|
||||
background: #f8f9fa;
|
||||
padding: 16px;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="match-report-header-actions">
|
||||
<!-- <button @click="insertPin" class="header-action-btn" title="PIN automatisch einfügen">
|
||||
<button @click="insertPin" class="header-action-btn" title="PIN automatisch einfügen">
|
||||
📌 PIN einfügen
|
||||
</button>-->
|
||||
</button>
|
||||
<button @click="copyPin" class="header-action-btn copy-button" title="PIN in Zwischenablage kopieren">
|
||||
📋 PIN kopieren
|
||||
</button>
|
||||
@@ -31,7 +31,7 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async copyPin() {
|
||||
async copyPin(event) {
|
||||
const pin = this.match.homePin || this.match.guestPin;
|
||||
if (!pin) {
|
||||
console.warn('⚠️ Keine PIN verfügbar zum Kopieren');
|
||||
@@ -43,7 +43,7 @@ export default {
|
||||
console.log('✅ PIN erfolgreich kopiert:', pin);
|
||||
|
||||
// Visuelles Feedback
|
||||
const button = event.target;
|
||||
const button = event?.target;
|
||||
const originalText = button.textContent;
|
||||
button.textContent = '✅ Kopiert!';
|
||||
button.style.backgroundColor = '#28a745';
|
||||
|
||||
528
frontend/src/components/NuscoreAnalyzer.vue
Normal file
528
frontend/src/components/NuscoreAnalyzer.vue
Normal file
@@ -0,0 +1,528 @@
|
||||
<template>
|
||||
<div class="nuscore-analyzer">
|
||||
<div class="analyzer-header">
|
||||
<h3>🔍 nuscore Analyzer</h3>
|
||||
<p>Analysiert und lädt nuscore-Ressourcen für lokale Nutzung herunter</p>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-controls">
|
||||
<button @click="analyzeNuscore" :disabled="isAnalyzing" class="analyze-btn">
|
||||
{{ isAnalyzing ? '🔄 Analysiere...' : '🔍 nuscore analysieren' }}
|
||||
</button>
|
||||
|
||||
<button @click="downloadResources" :disabled="!resources.length || isDownloading" class="download-btn">
|
||||
{{ isDownloading ? '⬇️ Lade herunter...' : `⬇️ ${resources.length} Ressourcen herunterladen` }}
|
||||
</button>
|
||||
|
||||
<button @click="createLocalCopy" :disabled="!hasDownloadedResources || isCreating" class="create-btn">
|
||||
{{ isCreating ? '🏗️ Erstelle...' : '🏗️ Lokale Kopie erstellen' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-results" v-if="resources.length > 0">
|
||||
<h4>📋 Gefundene Ressourcen:</h4>
|
||||
<div class="resource-list">
|
||||
<div
|
||||
v-for="resource in resources"
|
||||
:key="resource.url"
|
||||
class="resource-item"
|
||||
:class="{ 'downloaded': resource.downloaded }"
|
||||
>
|
||||
<span class="resource-type">{{ resource.type }}</span>
|
||||
<span class="resource-url">{{ resource.url }}</span>
|
||||
<span class="resource-size">{{ formatSize(resource.size) }}</span>
|
||||
<span v-if="resource.downloaded" class="status">✅</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="analyzer-log" v-if="logs.length > 0">
|
||||
<h4>📝 Log:</h4>
|
||||
<div class="log-content">
|
||||
<div v-for="(log, index) in logs" :key="index" class="log-entry">
|
||||
{{ log }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateSimpleNuscoreHTML } from '../utils/simpleNuscoreHTMLGenerator.js';
|
||||
|
||||
export default {
|
||||
name: 'NuscoreAnalyzer',
|
||||
data() {
|
||||
return {
|
||||
isAnalyzing: false,
|
||||
isDownloading: false,
|
||||
isCreating: false,
|
||||
resources: [],
|
||||
downloadedResources: [],
|
||||
logs: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasDownloadedResources() {
|
||||
return this.resources.some(resource => resource.downloaded) || this.downloadedResources.length > 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async analyzeNuscore() {
|
||||
this.isAnalyzing = true;
|
||||
this.logs = [];
|
||||
this.resources = [];
|
||||
|
||||
this.addLog('🔍 Starte nuscore-Analyse...');
|
||||
|
||||
try {
|
||||
// Öffne nuscore in einem versteckten iframe
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = 'https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list';
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
// Warte auf das Laden
|
||||
await new Promise((resolve) => {
|
||||
iframe.onload = resolve;
|
||||
setTimeout(resolve, 5000); // Timeout nach 5 Sekunden
|
||||
});
|
||||
|
||||
this.addLog('✅ nuscore-Seite geladen');
|
||||
|
||||
// Analysiere die Seite
|
||||
await this.analyzePage(iframe);
|
||||
|
||||
// Entferne das iframe
|
||||
document.body.removeChild(iframe);
|
||||
|
||||
this.addLog(`✅ Analyse abgeschlossen: ${this.resources.length} Ressourcen gefunden`);
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Fehler bei der Analyse: ${error.message}`);
|
||||
} finally {
|
||||
this.isAnalyzing = false;
|
||||
}
|
||||
},
|
||||
|
||||
async analyzePage(iframe) {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
|
||||
// Analysiere Script-Tags
|
||||
const scripts = doc.querySelectorAll('script[src]');
|
||||
this.addLog(`📜 ${scripts.length} Script-Tags gefunden`);
|
||||
|
||||
scripts.forEach(script => {
|
||||
this.resources.push({
|
||||
type: 'script',
|
||||
url: script.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
// Analysiere Link-Tags (CSS)
|
||||
const stylesheets = doc.querySelectorAll('link[rel="stylesheet"][href]');
|
||||
this.addLog(`🎨 ${stylesheets.length} Stylesheets gefunden`);
|
||||
|
||||
stylesheets.forEach(link => {
|
||||
this.resources.push({
|
||||
type: 'stylesheet',
|
||||
url: link.href,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
// Analysiere andere Ressourcen
|
||||
const images = doc.querySelectorAll('img[src]');
|
||||
this.addLog(`🖼️ ${images.length} Bilder gefunden`);
|
||||
|
||||
images.forEach(img => {
|
||||
this.resources.push({
|
||||
type: 'image',
|
||||
url: img.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`⚠️ Cross-Origin-Zugriff blockiert: ${error.message}`);
|
||||
|
||||
// Fallback: Analysiere die ursprüngliche URL
|
||||
await this.analyzeUrlDirectly();
|
||||
}
|
||||
},
|
||||
|
||||
async analyzeUrlDirectly() {
|
||||
this.addLog('🔄 Fallback: Bekannte nuscore-Ressourcen verwenden');
|
||||
|
||||
try {
|
||||
// Bekannte nuscore-Ressourcen basierend auf der URL-Struktur
|
||||
const baseUrl = 'https://ttde-apps.liga.nu';
|
||||
|
||||
// Typische nuscore-Ressourcen
|
||||
const knownResources = [
|
||||
// JavaScript-Bundles (typische Angular/React-Bundles)
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/runtime.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/vendor.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/polyfills.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/styles.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.6c3da9dbb4871026b9a1.js`, // Aus den Logs
|
||||
|
||||
// CSS-Dateien
|
||||
`${baseUrl}/nuliga/nuscore-tt/styles.css`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/main.css`,
|
||||
|
||||
// Weitere mögliche Ressourcen
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/main.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/runtime.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/vendor.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/styles.css`,
|
||||
|
||||
// Angular-spezifische Ressourcen
|
||||
`${baseUrl}/nuliga/nuscore-tt/angular.js`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/angular.min.js`,
|
||||
|
||||
// Bootstrap/CSS-Frameworks
|
||||
`${baseUrl}/nuliga/nuscore-tt/bootstrap.css`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/bootstrap.min.css`,
|
||||
|
||||
// Fonts
|
||||
`${baseUrl}/nuliga/nuscore-tt/fonts/`,
|
||||
|
||||
// Bilder/Assets
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/images/`,
|
||||
`${baseUrl}/nuliga/nuscore-tt/assets/icons/`
|
||||
];
|
||||
|
||||
// Füge bekannte Ressourcen hinzu
|
||||
knownResources.forEach(url => {
|
||||
this.resources.push({
|
||||
type: url.includes('.css') ? 'stylesheet' :
|
||||
url.includes('.js') ? 'script' :
|
||||
url.includes('fonts') ? 'font' : 'asset',
|
||||
url: url,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
});
|
||||
|
||||
this.addLog(`✅ ${this.resources.length} bekannte Ressourcen hinzugefügt`);
|
||||
|
||||
// Versuche zusätzlich, Ressourcen aus dem aktuellen iframe zu extrahieren
|
||||
await this.extractResourcesFromCurrentPage();
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Bekannte Ressourcen-Analyse fehlgeschlagen: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async extractResourcesFromCurrentPage() {
|
||||
this.addLog('🔍 Versuche Ressourcen-Extraktion von der aktuellen Seite');
|
||||
|
||||
try {
|
||||
// Analysiere die aktuelle Seite nach Ressourcen
|
||||
const scripts = document.querySelectorAll('script[src]');
|
||||
const stylesheets = document.querySelectorAll('link[rel="stylesheet"][href]');
|
||||
|
||||
this.addLog(`📜 ${scripts.length} Scripts auf aktueller Seite gefunden`);
|
||||
this.addLog(`🎨 ${stylesheets.length} Stylesheets auf aktueller Seite gefunden`);
|
||||
|
||||
// Füge gefundene Ressourcen hinzu (falls sie nuscore-relevant sind)
|
||||
scripts.forEach(script => {
|
||||
if (script.src.includes('nuscore') || script.src.includes('liga.nu')) {
|
||||
this.resources.push({
|
||||
type: 'script',
|
||||
url: script.src,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
stylesheets.forEach(link => {
|
||||
if (link.href.includes('nuscore') || link.href.includes('liga.nu')) {
|
||||
this.resources.push({
|
||||
type: 'stylesheet',
|
||||
url: link.href,
|
||||
size: 0,
|
||||
downloaded: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.addLog(`✅ ${this.resources.length} nuscore-relevante Ressourcen gefunden`);
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`⚠️ Ressourcen-Extraktion fehlgeschlagen: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async downloadResources() {
|
||||
this.isDownloading = true;
|
||||
this.downloadedResources = [];
|
||||
|
||||
this.addLog('⬇️ Starte Download der Ressourcen...');
|
||||
this.addLog('⚠️ Hinweis: CORS-Beschränkungen können Downloads blockieren');
|
||||
|
||||
for (const resource of this.resources) {
|
||||
try {
|
||||
this.addLog(`⬇️ Lade herunter: ${resource.url}`);
|
||||
|
||||
// Versuche verschiedene Download-Methoden
|
||||
let blob = null;
|
||||
|
||||
// Methode 1: Direkter fetch
|
||||
try {
|
||||
const response = await fetch(resource.url, {
|
||||
mode: 'cors',
|
||||
credentials: 'omit'
|
||||
});
|
||||
blob = await response.blob();
|
||||
} catch (corsError) {
|
||||
this.addLog(`⚠️ CORS-Fehler für ${resource.url}, versuche Alternative...`);
|
||||
|
||||
// Methode 2: Proxy über iframe
|
||||
blob = await this.downloadViaIframe(resource.url);
|
||||
}
|
||||
|
||||
if (blob && blob.size > 0) {
|
||||
resource.downloaded = true;
|
||||
resource.size = blob.size;
|
||||
resource.blob = blob;
|
||||
resource.localUrl = URL.createObjectURL(blob);
|
||||
|
||||
this.downloadedResources.push(resource);
|
||||
|
||||
this.addLog(`✅ Heruntergeladen: ${this.formatSize(blob.size)}`);
|
||||
} else {
|
||||
this.addLog(`⚠️ Leere oder ungültige Ressource: ${resource.url}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Download fehlgeschlagen: ${resource.url} - ${error.message}`);
|
||||
|
||||
// Fallback: Markiere als "verfügbar aber nicht heruntergeladen"
|
||||
resource.available = true;
|
||||
resource.downloaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.addLog(`✅ Download abgeschlossen: ${this.downloadedResources.length}/${this.resources.length} erfolgreich`);
|
||||
this.addLog(`💡 Tipp: Verwenden Sie die lokale Kopie auch mit externen Ressourcen`);
|
||||
this.isDownloading = false;
|
||||
},
|
||||
|
||||
async downloadViaIframe(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = url;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.onload = () => {
|
||||
try {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const content = doc.documentElement.outerHTML;
|
||||
const blob = new Blob([content], { type: 'text/html' });
|
||||
document.body.removeChild(iframe);
|
||||
resolve(blob);
|
||||
} catch (error) {
|
||||
document.body.removeChild(iframe);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
iframe.onerror = () => {
|
||||
document.body.removeChild(iframe);
|
||||
reject(new Error('Iframe load failed'));
|
||||
};
|
||||
|
||||
// Timeout nach 10 Sekunden
|
||||
setTimeout(() => {
|
||||
if (document.body.contains(iframe)) {
|
||||
document.body.removeChild(iframe);
|
||||
reject(new Error('Download timeout'));
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async createLocalCopy() {
|
||||
this.isCreating = true;
|
||||
this.addLog('🏗️ Erstelle lokale Kopie...');
|
||||
|
||||
try {
|
||||
// Erstelle eine einfache HTML-Datei, die die ursprüngliche nuscore-Seite in einem iframe lädt
|
||||
const html = generateSimpleNuscoreHTML({ homePin: '', guestPin: '', code: '' });
|
||||
|
||||
const blob = new Blob([html], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'nuscore-local.html';
|
||||
link.click();
|
||||
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
this.addLog('✅ Lokale Kopie erstellt (iframe-basiert)');
|
||||
this.addLog('💡 Die lokale Kopie lädt die ursprüngliche nuscore-Seite in einem iframe');
|
||||
this.addLog('💡 PIN-Einfügung funktioniert über PostMessage');
|
||||
|
||||
} catch (error) {
|
||||
this.addLog(`❌ Fehler beim Erstellen der lokalen Kopie: ${error.message}`);
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
},
|
||||
|
||||
addLog(message) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
this.logs.push(`[${timestamp}] ${message}`);
|
||||
},
|
||||
|
||||
formatSize(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nuscore-analyzer {
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.analyzer-header h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.analyzer-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 20px 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.analyze-btn, .download-btn, .create-btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.analyze-btn {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.analyze-btn:hover:not(:disabled) {
|
||||
background: var(--primary-hover);
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
background: var(--secondary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.download-btn:hover:not(:disabled) {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.create-btn:hover:not(:disabled) {
|
||||
background: #218838;
|
||||
}
|
||||
|
||||
.analyze-btn:disabled, .download-btn:disabled, .create-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.resource-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.resource-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.resource-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.resource-item.downloaded {
|
||||
background: #d4edda;
|
||||
}
|
||||
|
||||
.resource-type {
|
||||
font-weight: bold;
|
||||
min-width: 80px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.resource-url {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.resource-size {
|
||||
min-width: 60px;
|
||||
text-align: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status {
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #000;
|
||||
color: #0f0;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
</style>
|
||||
85
frontend/src/utils/nuscoreHTMLGenerator.js
Normal file
85
frontend/src/utils/nuscoreHTMLGenerator.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// HTML-Generator für nuscore lokale Kopie
|
||||
export function generateNuscoreHTML(scripts, stylesheets, useExternalUrls = false) {
|
||||
const parts = [];
|
||||
|
||||
parts.push('<!DOCTYPE html>');
|
||||
parts.push('<html lang="de">');
|
||||
|
||||
// Head
|
||||
parts.push('<head>');
|
||||
parts.push(' <meta charset="UTF-8">');
|
||||
parts.push(' <meta name="viewport" content="width=device-width, initial-scale=1.0">');
|
||||
parts.push(' <title>nuscore - Lokale Kopie</title>');
|
||||
parts.push(' <base href="/">');
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Stylesheets -->');
|
||||
|
||||
stylesheets.forEach(sheet => {
|
||||
const href = useExternalUrls ? sheet.url : sheet.localUrl;
|
||||
parts.push(` <link rel="stylesheet" href="${href}">`);
|
||||
});
|
||||
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- PIN-Einfügung Script -->');
|
||||
parts.push(' <script>');
|
||||
parts.push(' window.addEventListener("message", function(event) {');
|
||||
parts.push(' if (event.data && event.data.action === "fillPin") {');
|
||||
parts.push(' console.log("PIN-Einfügung empfangen:", event.data.pin);');
|
||||
parts.push(' ');
|
||||
parts.push(' const pinSelectors = [');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"Vereins\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"Spiel-Pin\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"][placeholder*=\\"PIN\\"]",');
|
||||
parts.push(' "input[type=\\"password\\"]"');
|
||||
parts.push(' ];');
|
||||
parts.push(' ');
|
||||
parts.push(' let pinField = null;');
|
||||
parts.push(' for (const selector of pinSelectors) {');
|
||||
parts.push(' pinField = document.querySelector(selector);');
|
||||
parts.push(' if (pinField) break;');
|
||||
parts.push(' }');
|
||||
parts.push(' ');
|
||||
parts.push(' if (pinField) {');
|
||||
parts.push(' pinField.value = event.data.pin;');
|
||||
parts.push(' pinField.dispatchEvent(new Event("input", { bubbles: true }));');
|
||||
parts.push(' pinField.dispatchEvent(new Event("change", { bubbles: true }));');
|
||||
parts.push(' console.log("PIN erfolgreich eingefügt:", event.data.pin);');
|
||||
parts.push(' }');
|
||||
parts.push(' }');
|
||||
parts.push(' });');
|
||||
parts.push(' </script>');
|
||||
parts.push('</head>');
|
||||
|
||||
// Body
|
||||
parts.push('<body>');
|
||||
parts.push(' <app-root></app-root>');
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Scripts -->');
|
||||
|
||||
scripts.forEach(script => {
|
||||
const src = useExternalUrls ? script.url : script.localUrl;
|
||||
parts.push(` <script src="${src}"></script>`);
|
||||
});
|
||||
|
||||
parts.push(' ');
|
||||
parts.push(' <!-- Initialisierung -->');
|
||||
parts.push(' <script>');
|
||||
parts.push(' console.log("nuscore lokale Kopie gestartet");');
|
||||
parts.push(' console.log("PIN-Einfügung bereit");');
|
||||
parts.push(' ');
|
||||
parts.push(' const urlParams = new URLSearchParams(window.location.search);');
|
||||
parts.push(' const code = urlParams.get("code");');
|
||||
parts.push(' if (code) {');
|
||||
parts.push(' console.log("Code aus URL gefunden:", code);');
|
||||
parts.push(' }');
|
||||
parts.push(' ');
|
||||
parts.push(' // Angular Router Konfiguration für lokale Kopie');
|
||||
parts.push(' if (window.ng && window.ng.core) {');
|
||||
parts.push(' console.log("Angular gefunden, konfiguriere Router...");');
|
||||
parts.push(' }');
|
||||
parts.push(' </script>');
|
||||
parts.push('</body>');
|
||||
parts.push('</html>');
|
||||
|
||||
return parts.join('\n');
|
||||
}
|
||||
111
frontend/src/utils/simpleNuscoreHTMLGenerator.js
Normal file
111
frontend/src/utils/simpleNuscoreHTMLGenerator.js
Normal file
@@ -0,0 +1,111 @@
|
||||
// Einfacher HTML-Generator für nuscore lokale Kopie (iframe-basiert)
|
||||
export function generateSimpleNuscoreHTML(match) {
|
||||
const pin = match.homePin || match.guestPin || '';
|
||||
const code = match.code || '';
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>nuscore - Lokale Kopie</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
|
||||
.header { background: #f0f0f0; padding: 10px; border-bottom: 1px solid #ccc; }
|
||||
.controls { margin: 10px 0; }
|
||||
.controls button { margin-right: 10px; padding: 5px 10px; }
|
||||
.iframe-container { width: 100%; height: calc(100vh - 100px); }
|
||||
.iframe-container iframe { width: 100%; height: 100%; border: none; }
|
||||
.status { padding: 10px; background: #e8f5e8; border: 1px solid #4caf50; margin: 10px 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>nuscore - Lokale Kopie</h1>
|
||||
<div class="controls">
|
||||
<button onclick="insertPin()">📌 PIN einfügen</button>
|
||||
<button onclick="copyPin()">📋 PIN kopieren</button>
|
||||
<input type="password" id="pinInput" placeholder="PIN eingeben" style="margin-left: 10px;">
|
||||
</div>
|
||||
<div class="status" id="status">Bereit für PIN-Einfügung</div>
|
||||
</div>
|
||||
<div class="iframe-container">
|
||||
<iframe id="nuscoreFrame" src="https://ttde-apps.liga.nu/nuliga/nuscore-tt/meetings-list"></iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentPin = "";
|
||||
|
||||
// PIN aus Match-Objekt laden
|
||||
const pin = "${pin}";
|
||||
const code = "${code}";
|
||||
|
||||
if (pin) {
|
||||
currentPin = pin;
|
||||
document.getElementById("pinInput").value = pin;
|
||||
document.getElementById("status").textContent = "PIN aus Match geladen: " + pin;
|
||||
}
|
||||
|
||||
function insertPin() {
|
||||
const pin = document.getElementById("pinInput").value || currentPin;
|
||||
if (!pin) {
|
||||
document.getElementById("status").textContent = "Keine PIN eingegeben";
|
||||
return;
|
||||
}
|
||||
|
||||
const iframe = document.getElementById("nuscoreFrame");
|
||||
const message = {
|
||||
action: "fillPin",
|
||||
pin: pin,
|
||||
timestamp: Date.now(),
|
||||
source: "nuscore-local"
|
||||
};
|
||||
|
||||
try {
|
||||
iframe.contentWindow.postMessage(message, "https://ttde-apps.liga.nu");
|
||||
document.getElementById("status").textContent = "PIN gesendet: " + pin;
|
||||
} catch (error) {
|
||||
document.getElementById("status").textContent = "Fehler beim Senden der PIN: " + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
function copyPin() {
|
||||
const pin = document.getElementById("pinInput").value || currentPin;
|
||||
if (!pin) {
|
||||
document.getElementById("status").textContent = "Keine PIN zum Kopieren";
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(pin).then(() => {
|
||||
document.getElementById("status").textContent = "PIN kopiert: " + pin;
|
||||
}).catch(() => {
|
||||
// Fallback
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = pin;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
document.getElementById("status").textContent = "PIN kopiert (Fallback): " + pin;
|
||||
});
|
||||
}
|
||||
|
||||
// PostMessage Listener für Antworten vom iframe
|
||||
window.addEventListener("message", function(event) {
|
||||
if (event.origin !== "https://ttde-apps.liga.nu") return;
|
||||
|
||||
if (event.data && event.data.action === "pinInserted") {
|
||||
document.getElementById("status").textContent = "PIN erfolgreich eingefügt!";
|
||||
} else if (event.data && event.data.action === "pinError") {
|
||||
document.getElementById("status").textContent = "PIN-Einfügung fehlgeschlagen: " + event.data.error;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("nuscore lokale Kopie gestartet");
|
||||
if (code) console.log("Code aus Match:", code);
|
||||
if (pin) console.log("PIN aus Match:", pin);
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
@@ -93,13 +93,13 @@ import { mapGetters, mapActions } from 'vuex';
|
||||
import apiClient from '../apiClient.js';
|
||||
import PDFGenerator from '../components/PDFGenerator.js';
|
||||
import SeasonSelector from '../components/SeasonSelector.vue';
|
||||
import MatchReportDialog from '../components/MatchReportDialog.vue';
|
||||
import MatchReportApiDialog from '../components/MatchReportApiDialog.vue';
|
||||
|
||||
export default {
|
||||
name: 'ScheduleView',
|
||||
components: {
|
||||
SeasonSelector,
|
||||
MatchReportDialog
|
||||
MatchReportApiDialog
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'currentClubName']),
|
||||
@@ -377,15 +377,9 @@ export default {
|
||||
const title = `${match.homeTeam?.name || 'N/A'} vs ${match.guestTeam?.name || 'N/A'} - ${this.selectedLeague}`;
|
||||
this.openDialog({
|
||||
title,
|
||||
component: 'MatchReportDialog',
|
||||
component: 'MatchReportApiDialog',
|
||||
props: {
|
||||
match
|
||||
},
|
||||
headerActions: {
|
||||
component: 'MatchReportHeaderActions',
|
||||
props: {
|
||||
match
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
65
test-iframe.html
Normal file
65
test-iframe.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>nuscore iframe Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
iframe { width: 100%; height: 600px; border: 2px solid #007bff; }
|
||||
.status { padding: 10px; margin: 10px 0; background: #f8f9fa; border-radius: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>nuscore iframe Test</h1>
|
||||
|
||||
<div class="status">
|
||||
<strong>Test-URL:</strong> <span id="test-url"></span><br>
|
||||
<strong>Status:</strong> <span id="status">Lädt...</span>
|
||||
</div>
|
||||
|
||||
<iframe
|
||||
id="test-iframe"
|
||||
src="http://localhost:3000/api/proxy/nuscore?code=KXEA9JR5EUPW&pin=1555"
|
||||
onload="iframeLoaded()"
|
||||
onerror="iframeError()">
|
||||
</iframe>
|
||||
|
||||
<script>
|
||||
const iframe = document.getElementById('test-iframe');
|
||||
const statusEl = document.getElementById('status');
|
||||
const urlEl = document.getElementById('test-url');
|
||||
|
||||
urlEl.textContent = iframe.src;
|
||||
|
||||
function iframeLoaded() {
|
||||
statusEl.textContent = '✅ Iframe geladen';
|
||||
statusEl.style.color = 'green';
|
||||
|
||||
try {
|
||||
// Versuche auf iframe-Inhalt zuzugreifen
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
if (iframeDoc) {
|
||||
console.log('Iframe-Titel:', iframeDoc.title);
|
||||
console.log('Iframe-Body:', iframeDoc.body ? 'Vorhanden' : 'Nicht vorhanden');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Cross-Origin-Zugriff blockiert (erwartet):', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function iframeError() {
|
||||
statusEl.textContent = '❌ Iframe-Fehler';
|
||||
statusEl.style.color = 'red';
|
||||
}
|
||||
|
||||
// Timeout nach 10 Sekunden
|
||||
setTimeout(() => {
|
||||
if (statusEl.textContent === 'Lädt...') {
|
||||
statusEl.textContent = '⏰ Timeout';
|
||||
statusEl.style.color = 'orange';
|
||||
}
|
||||
}, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
95
test-nuscore-api.html
Normal file
95
test-nuscore-api.html
Normal file
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Nuscore API</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ccc; border-radius: 5px; }
|
||||
.success { color: green; }
|
||||
.error { color: red; }
|
||||
button { padding: 10px 15px; margin: 5px; cursor: pointer; }
|
||||
pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow-x: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Nuscore API</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>1. Health Check</h2>
|
||||
<button onclick="testHealth()">Health Check</button>
|
||||
<div id="health-status"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>2. Cookie Initialisierung</h2>
|
||||
<input type="text" id="test-code" value="KXEA9JR5EUPW" placeholder="Code eingeben">
|
||||
<button onclick="testInitCookies()">Cookies initialisieren</button>
|
||||
<div id="cookie-status"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>3. Meeting Info</h2>
|
||||
<button onclick="testMeetingInfo()">Meeting Info laden</button>
|
||||
<div id="meeting-status"></div>
|
||||
<pre id="meeting-data"></pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function testHealth() {
|
||||
const statusDiv = document.getElementById('health-status');
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/nuscore/health');
|
||||
const data = await response.json();
|
||||
statusDiv.innerHTML = `<div class="success">✅ Health Check OK: ${JSON.stringify(data)}</div>`;
|
||||
} catch (error) {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Health Check Fehler: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testInitCookies() {
|
||||
const code = document.getElementById('test-code').value;
|
||||
const statusDiv = document.getElementById('cookie-status');
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:3000/api/nuscore/init-cookies/${code}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
statusDiv.innerHTML = `<div class="success">✅ Cookies initialisiert: ${JSON.stringify(data)}</div>`;
|
||||
} else {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Cookie-Fehler: ${JSON.stringify(data)}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Cookie-Fehler: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testMeetingInfo() {
|
||||
const code = document.getElementById('test-code').value;
|
||||
const statusDiv = document.getElementById('meeting-status');
|
||||
const dataDiv = document.getElementById('meeting-data');
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:3000/api/nuscore/meetinginfo/${code}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
statusDiv.innerHTML = `<div class="success">✅ Meeting Info geladen</div>`;
|
||||
dataDiv.textContent = JSON.stringify(data, null, 2);
|
||||
} else {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Meeting Info Fehler: ${JSON.stringify(data)}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Meeting Info Fehler: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatisch Health Check beim Laden
|
||||
window.onload = testHealth;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
78
test-proxy.html
Normal file
78
test-proxy.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>nuscore Proxy Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.test-section { margin: 20px 0; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
|
||||
button { padding: 10px 20px; margin: 5px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
||||
button:hover { background: #0056b3; }
|
||||
iframe { width: 100%; height: 600px; border: 1px solid #ccc; }
|
||||
.status { padding: 10px; margin: 10px 0; border-radius: 4px; }
|
||||
.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
||||
.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>nuscore Proxy Test</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Health Check</h2>
|
||||
<button onclick="testHealth()">Health Check</button>
|
||||
<div id="health-status" class="status"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Proxy Test</h2>
|
||||
<input type="text" id="test-code" placeholder="Code eingeben" value="TEST123">
|
||||
<button onclick="testProxy()">Proxy Test</button>
|
||||
<div id="proxy-status" class="status"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Iframe Test</h2>
|
||||
<button onclick="loadIframe()">Iframe laden</button>
|
||||
<iframe id="test-iframe" style="display: none;"></iframe>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function testHealth() {
|
||||
const statusDiv = document.getElementById('health-status');
|
||||
try {
|
||||
const response = await fetch('http://localhost:3000/api/proxy/nuscore/health');
|
||||
const data = await response.json();
|
||||
statusDiv.innerHTML = `<div class="success">✅ Health Check OK: ${JSON.stringify(data)}</div>`;
|
||||
} catch (error) {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Health Check Fehler: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testProxy() {
|
||||
const code = document.getElementById('test-code').value;
|
||||
const statusDiv = document.getElementById('proxy-status');
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:3000/api/proxy/nuscore?code=${encodeURIComponent(code)}`);
|
||||
const text = await response.text();
|
||||
|
||||
if (response.ok) {
|
||||
statusDiv.innerHTML = `<div class="success">✅ Proxy OK: ${text.length} Zeichen empfangen</div>`;
|
||||
} else {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Proxy Fehler: ${text}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
statusDiv.innerHTML = `<div class="error">❌ Proxy Fehler: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function loadIframe() {
|
||||
const code = document.getElementById('test-code').value;
|
||||
const iframe = document.getElementById('test-iframe');
|
||||
iframe.src = `http://localhost:3000/api/proxy/nuscore?code=${encodeURIComponent(code)}`;
|
||||
iframe.style.display = 'block';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user