diff --git a/lobby-backend/Dockerfile.dev b/lobby-backend/Dockerfile.dev deleted file mode 100644 index 6994210..0000000 --- a/lobby-backend/Dockerfile.dev +++ /dev/null @@ -1,32 +0,0 @@ -# ============================================================================== -# Void-Genesis - Lobby Backend (Dev Environment) -# ============================================================================== - -# Wir nutzen eine leichtgewichtige Alpine-Version der aktuellen Node.js LTS (z.B. 22) -# Alpine-Images sind extrem klein und reduzieren die Angriffsfläche. -FROM node:22-alpine - -# Setze das Arbeitsverzeichnis innerhalb des Containers auf /app. -# Alle folgenden Befehle (RUN, CMD, COPY) werden in diesem Ordner ausgeführt. -WORKDIR /app - -# Wir kopieren zuerst NUR die package.json (und package-lock.json, falls vorhanden). -# Das ist ein Best-Practice für Docker: Docker speichert diesen Schritt im Cache. -# 'npm install' wird beim erneuten Bauen nur dann ausgeführt, wenn sich an den -# Abhängigkeiten in der package.json etwas geändert hat. Das spart enorm Zeit! -COPY package*.json ./ - -# Installiere alle Abhängigkeiten (inklusive Dev-Dependencies wie nodemon) -RUN npm install - -# HINWEIS: Den restlichen Quellcode (src/) kopieren wir hier im Dev-Dockerfile -# absichtlich nicht mit COPY. Der Code wird stattdessen über die docker-compose.yml -# als Volume gemountet. So greifen deine Änderungen am Root-Server sofort im Container. - -# Wir dokumentieren hier, dass der Container auf Port 3000 lauscht. -# Das Backend wird diesen Port später nutzen. -EXPOSE 3000 - -# Der Standardbefehl, wenn der Container startet. Wir rufen das Script "dev" auf, -# welches wir gleich im nächsten Schritt in der package.json definieren werden. -CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/lobby-backend/package-lock.json b/lobby-backend/package-lock.json deleted file mode 100644 index 6ca319b..0000000 --- a/lobby-backend/package-lock.json +++ /dev/null @@ -1,1618 +0,0 @@ -{ - "name": "void-genesis-lobby-backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "void-genesis-lobby-backend", - "version": "1.0.0", - "dependencies": { - "argon2": "^0.44.0", - "cors": "^2.8.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.3", - "pg": "^8.11.5" - }, - "devDependencies": { - "nodemon": "^3.1.0" - } - }, - "node_modules/@epic-web/invariant": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", - "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", - "license": "MIT" - }, - "node_modules/@phc/format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", - "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argon2": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz", - "integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@phc/format": "^1.0.0", - "cross-env": "^10.0.0", - "node-addon-api": "^8.5.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-env": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", - "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", - "license": "MIT", - "dependencies": { - "@epic-web/invariant": "^1.0.0", - "cross-spawn": "^7.0.6" - }, - "bin": { - "cross-env": "dist/bin/cross-env.js", - "cross-env-shell": "dist/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.15.1", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", - "license": "MIT", - "dependencies": { - "jws": "^4.0.1", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.8.0.tgz", - "integrity": "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nodemon": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", - "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^10.2.1", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/nodemon/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "license": "MIT" - }, - "node_modules/pg": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", - "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.13.0", - "pg-pool": "^3.14.0", - "pg-protocol": "^1.14.0", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.4.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", - "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", - "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", - "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", - "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/lobby-backend/package.json b/lobby-backend/package.json deleted file mode 100644 index 3da91d7..0000000 --- a/lobby-backend/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "void-genesis-lobby-backend", - "version": "1.0.0", - "description": "Backend für die Master-Account-Lobby", - "main": "src/server.js", - "type": "module", - "scripts": { - "start": "node src/server.js", - "dev": "nodemon src/server.js" - }, - "dependencies": { - "argon2": "^0.44.0", - "cors": "^2.8.5", - "express": "^4.19.2", - "jsonwebtoken": "^9.0.3", - "pg": "^8.11.5" - }, - "devDependencies": { - "nodemon": "^3.1.0" - } -} diff --git a/lobby-backend/src/controllers/auth.controller.js b/lobby-backend/src/controllers/auth.controller.js deleted file mode 100644 index f363e85..0000000 --- a/lobby-backend/src/controllers/auth.controller.js +++ /dev/null @@ -1,320 +0,0 @@ -import * as authService from '../services/auth.service.js'; - -// Ein Standard-Regulärer-Ausdruck (Regex) zur Prüfung eines gültigen E-Mail-Formats -const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - -/** - * Controller-Methode für die Registierung-Route. - */ -export const register = async (req, res) => { - try { - // Daten aus dem Body extrahieren - const { username, email, password } = req.body; - - // Basis-Validierung (leere Felder, Passwortlänge) - if (!username || !email || !password) { - return res.status(400).json({ - // ERR_MISSING_FIELDS: Bitte alle Felder ausfüllen. - error: 'ERR_MISSING_FIELDS' - }); - } - if (password.length < 8) { - return res.status(400).json({ - // ERR_PASSWORD_TOO_SHORT: Das Passwort muss mindestens 8 Zeichen lang sein. - error: 'ERR_PASSWORD_TOO_SHORT' - }); - } - if (username.length < 3) { - return res.status(400).json({ - // ERR_USERNAME_TOO_SHORT: Der Benutzername muss mindestens 3 Zeichen lang sein. - error: 'ERR_USERNAME_TOO_SHORT' - }); - } - if (!EMAIL_REGEX.test(email)) { - return res.status(400).json({ - /// ERR_INVALID_EMAIL: Das E-Mail-Format ist ungültig. - error: 'ERR_INVALID_EMAIL' - }); - } - - // Den Auth-Service aufrufen (die eigenliche Arbeit) - const result = await authService.registerUser(username, email, password); - - // Erfolgreiche Antwort an der Frontend senden - return res.status(201).json({ - success: true, - // MSG_REGISTER_SUCCESS: Registrierung erfolgreich. Bitte prüfe deine E-Mails. - message: 'MSG_REGISTER_SUCCESS', - accountId: result.accountId - }); - - } catch (error) { - // Fehlerbehandlung: Wenn es unser eigener Conflict-Fehler ist (zB Email existiert) - if (error.code === 'ERR_USERNAME_TAKEN') { - return res.status(409).json({ - // ERR_USERNAME_TAKEN: Dieser Benutzername ist bereits vergeben. - error: 'ERR_USERNAME_TAKEN' - }); - } - if (error.code === 'ERR_EMAIL_TAKEN') { - return res.status(409).json({ - // ERR_USERNAME_TAKEN: Diese E-Mail-Addresse ist bereits vergeben. - error: 'ERR_EMAIL_TAKEN' - }); - } - - // Bei unerwarteten Fehlern (z.B. Datenbank oder Brovo nicht erreichbar.) - console.error('[AuthController] Fehler bei der Registierung:', error); - return res.status(500).json({ - // ERR_INTERNAL_SERVER: Ein interner Serverfehler ist aufgetreten. - error: 'ERR_INTERNAL_SERVER' - }); - } -}; - -/** - * Controller-Methode für die E-Mail-Verifizierungs-Route - */ -export const verifyEmail = async (req, res) => { - try { - // Daten aus dem Request-Body extrahieren - const { email , otpCode } = req.body; - - // Vasis-Validierung - if (!email || !otpCode) { - return res.status(400).json({ - error: 'ERR_MISSING_FIELDS' - }); - } - - // Den Service aufrufen, um die eigenliche Arbeit zu machen - await authService.verifyEmail(email, otpCode); - - // Erfolgreiche Antwort ans Frontend senden - return res.status(200).json({ - success: true, - message: 'MSG_EMAIL_VERIFIED' - }); - - } catch (error) { - if (['ERR_USER_NOT_FOUND', 'ERR_ALREADY_VERIFIED', 'ERR_INVALID_OTP', 'ERR_OTP_EXPIRED'].includes(error.code)) { - - return res.status(400).json({ - error: error.code - }); - } - - console.error('[AuthController] Fehler bei E-Mail-Verifizierung:', error); - return res.status(500).json({ - error: 'ERR_INTERNAL_SERVER' - }); - } -}; - -/** - * Controller-Methode für die Login-Route - */ -export const login = async (req, res) => { - try { - const { identifier, password } = req.body; - - // Basis-Validierung - if (!identifier || !password) { - return res.status(400).json({ - error: 'ERR_MISSING_FIELDS' - }); - } - - // Service aufrufen - const result = await authService.loginUser(identifier, password); - - // Erfolgreiche Antwort mit den Tokens - return res.status(200).json({ - success: true, - // MSG_LOGIN_SUCCESS: Erfolgreich eingeloggt - message: 'MSG_LOGIN_SUCCESS', - accessToken: result.accessToken, - refreshToken: result.refreshToken, - user: result.user - }); - - } catch (error) { - if (error.code === 'ERR_INVALID_CREDENTIALS') { - return res.status(401).json({ - error: 'ERR_INVALID_CREDENTIALS' - }); - } - if (error.code === 'ERR_EMAIL_NOT_VERIFIED') { - return res.status(403).json({ - error: 'ERR_EMAIL_NOT_VERIFIED', - email: error.email - }); - } - - console.error('[AuthController] Fehler beim Login:', error); - return res.status(500).json({ - error: 'ERR_INTERNAL_SERVER' - }); - } -} - -/** - * Controller-Methode für die Token-Refresh-Route - */ -export const refresh = async (req, res) => { - try { - // Das Refresh-Token aus dem Request-Body holen - const { refreshToken } = req.body; - - // Validierung: Is überhaupt ein Token mitgesendet worden ? - if (!refreshToken) { - // ERR_MISSING_TOKEN: Es wurde kein Refresh-Token übergeben - return res.status(400).json({ - error: 'ERR_MISSING_TOKEN' - }); - } - - // Den Service aufrufen, um das Token zu prüfen und ein neues zu generieren - const result = await authService.refreshAccessToken(refreshToken); - - // Das neue Access-Token an das Frontend zurück geben - return res.status(200).json({ - success: true, - accessToken: result.accessToken, - user: result.user - }); - - } catch (error) { - // Ungültige, abgelaufene oder in der DB gelöschte Tokens abfangen - if (error.code === 'ERR_UNAUTHORIZED') { - return res.status(401).json({ - // ERR_UNAUTHORIZED: Zugriff verweigert. Die Sitzung ist ungültig. - error: 'ERR_UNAUTHORIZED' - }); - } - - // Bei unerwarteten Fehlern (z.B. Datenbank oder Brovo nicht erreichbar.) - console.error('[AuthController] Fehler beim Token-Refresh:', error); - return res.status(500).json({ - // ERR_INTERNAL_SERVER: Ein interner Serverfehler ist aufgetreten. - error: 'ERR_INTERNAL_SERVER' - }); - } -} - -/** - * Controller-Methode für die Resend-OTP-Route - * Wird aufgerufen, wenn der Nutzer im Frontend auf "Code erneut senden" klickt - */ -export const resendOTP = async (req, res) => { - try { - const { email } = req.body; - - if (!email) { - return res.status(400).json({ - error: 'ERR_MISSING_FIELDS' - }); - } - - // Aufruf unseres Service - await authService.resendOTP(email); - - return res.status(200).json({ - success: true, - // MSG_OTP_RESENT: Ein neuer Code wurde an deine E-Mail gesendet. - message: 'MSG_OTP_RESENT' - }); - - } catch (error) { - if (['ERR_USER_NOT_FOUND', 'ERR_ALREADY_VERIFIED', 'ERR_COOLDOWN_ACTIVE'].includes(error.code)) { - const statusCode = error.code === 'ERR_COOLDOWN_ACTIVE' ? 429 : 400; - return res.status(statusCode).json({ - error: error.code - }); - } - - console.error('[AuthController] Fehler beim erneuten Senden des OTP:', error); - return res.status(500).json({ - error: 'ERR_INTERNAL_SERVER' - }); - } -}; - -/** - * Controller-Methode für die Passwort vergessen anfrage - */ -export const requestPasswordReset = async (req, res) => { - try { - const { email } = req.body; - - if (!email) { - return res.status(400).json({ - error: 'ERR_MISSING_FIELDS' - }); - } - - await authService.requestPasswordReset(email); - - // Auch wenn die E-Mail nicht existiert, geben wir oft ein Success zurück, - // damit Angreifer nicht testen können, welche E-Mails registriert sind. - // In unserem Service werfen wir aber ERR_USER_NOT_FOUND. Wir fangen das unten ab. - return res.status(200).json({ - success: true, - message: 'MSG_RESET_EMAIL_SENT' // Muss später ins i18n - }); - - } catch (error) { - // Wenn der Nutzer nicht existiert, tun wir für das Frontend trotzdem so, als wäre - // die E-Mail rausgegangen (Sicherheitsbest-Practice). - if (error.code === 'ERR_USER_NOT_FOUND') { - return res.status(200).json({ - success: true, - message: 'MSG_RESET_EMAIL_SENT' - }); - } - - if (error.code === 'ERR_COOLDOWN_ACTIVE') { - return res.status(429).json({ - error: error.code - }); - } - - console.error('[AuthController] Fehler bei requestPasswordReset:', error); - return res.status(500).json({ error: 'ERR_INTERNAL_SERVER' }); - } -}; - -/** - * Controller-Methode für das finale Setzen des neuen Passworts. - */ -export const resetPassword = async (req, res) => { - try { - const { email, otpCode, newPassword } = req.body; - - // Basis-Validierung - if (!email || !otpCode || !newPassword) { - return res.status(400).json({ error: 'ERR_MISSING_FIELDS' }); - } - - // Passwort-Richtlinie prüfen (wie bei der Registrierung) - if (newPassword.length < 8) { - return res.status(400).json({ error: 'ERR_PASSWORD_TOO_SHORT' }); - } - - // Service aufrufen - await authService.resetPassword(email, otpCode, newPassword); - - return res.status(200).json({ - success: true, - message: 'MSG_PASSWORD_RESET_SUCCESS' // Muss später ins i18n - }); - - } catch (error) { - if (['ERR_USER_NOT_FOUND', 'ERR_INVALID_OTP', 'ERR_OTP_EXPIRED'].includes(error.code)) { - return res.status(400).json({ error: error.code }); - } - - console.error('[AuthController] Fehler bei resetPassword:', error); - return res.status(500).json({ error: 'ERR_INTERNAL_SERVER' }); - } -}; \ No newline at end of file diff --git a/lobby-backend/src/controllers/sector.controller.js b/lobby-backend/src/controllers/sector.controller.js deleted file mode 100644 index 1406299..0000000 --- a/lobby-backend/src/controllers/sector.controller.js +++ /dev/null @@ -1,69 +0,0 @@ -import pool from '../db.js'; -import * as sectorService from '../services/sector.service.js'; - -/** - * Liefert alle Sektoren für das Frontend. - * Übergibt die HTTP-Anfrage an den zuständigen Service und liefert das Ergebnis aus. - */ -export const getSectorsList = async (req, res) => { - const accountId = req.user.accountId; - - try { - const sectorData = await sectorService.getSectorsList(accountId); - - return res.status(200).json({ - success: true, - ...sectorData - }); - - } catch (error) { - console.error('[SectorController] Fehler in getSectorsList:', error); - - return res.status(500).json({ - error: 'ERR_INTERNAL_SERVER' - }); - } -}; - -/** - * Controller-Methode für den Sektor-Beitritt (Charaktererstellung). - * Nimmt die Request an, validiert grob und leitet an den Service weiter. - */ -export const joinSector = async (req, res) => { - const accountId = req.user.accountId; - const { sectorId, localUsername, gender } = req.body; - - if (!sectorId || !localUsername || !gender) { - return res.status(400).json({ - error: 'ERR_MISSING_FIELDS' - }); - } - - if (localUsername.length < 3) { - return res.status(400).json({ - error: 'ERR_USERNAME_TOO_SHORT' - }); - } - - try { - await sectorService.joinSector(accountId, sectorId, localUsername, gender); - - return res.status(201).json({ - success: true, - message: 'MSG_SECTOR_JOINED_SUCCESS' - }); - - } catch (error) { - if (error.code === 'ERR_ALREADY_IN_SECTOR' || error.code === 'ERR_LOCAL_USERNAME_TAKEN') { - return res.status(409).json({ - error: error.code - }); - } - - // Generischer Fallback für echte Systemabstürze - console.error('[SectorController] Fehler beim Sektor-Beitritt:', error); - return res.status(500).json({ - error: 'ERR_INTERNAL_SERVER' - }); - } -}; \ No newline at end of file diff --git a/lobby-backend/src/db.js b/lobby-backend/src/db.js deleted file mode 100644 index 5a15ac9..0000000 --- a/lobby-backend/src/db.js +++ /dev/null @@ -1,36 +0,0 @@ -// ============================================================================== -// Void-Genesis - Datenbank-Konfiguration -// ============================================================================== - -import pg from 'pg'; - -// Wir nutzen einen Connection Pool. Das ist deutlich effizienter als für jede -// Anfrage eine neue Verbindung zur Datenbank aufzubauen. Der Pool hält im -// Hintergrund mehrere Verbindungen offen und verteilt sie dynamisch. -const { Pool } = pg; - -const pool = new Pool({ - // Diese Variablen kommen direkt aus unserer docker-compose.yml - // (Bereich 'environment' des Backend-Containers) - host: process.env.DB_HOST, - port: process.env.DB_PORT, - user: process.env.DB_USER, - password: process.env.DB_PASS, - database: process.env.DB_NAME, -}); - -// Wir testen beim Start einmalig, ob die Verbindung klappt. -// So sehen wir im Docker-Log sofort, falls z.B. das Passwort falsch ist. -pool.connect((err, client, release) => { - if (err) { - console.error('❌ Fehler beim Verbinden mit der PostgreSQL-Datenbank:', err.stack); - } else { - console.log('✅ Erfolgreich mit der PostgreSQL-Datenbank (void_lobby) verbunden!'); - // WICHTIG: Den Client immer direkt wieder freigeben, wenn wir fertig sind - release(); - } -}); - -// Wir exportieren den Pool, damit wir ihn in anderen Dateien (wie unseren Routes) -// für Datenbankabfragen nutzen können. -export default pool; \ No newline at end of file diff --git a/lobby-backend/src/middlewares/auth.middleware.js b/lobby-backend/src/middlewares/auth.middleware.js deleted file mode 100644 index ca3ad26..0000000 --- a/lobby-backend/src/middlewares/auth.middleware.js +++ /dev/null @@ -1,30 +0,0 @@ -import jwt from 'jsonwebtoken'; - -/** - * Middleware zum Schützen privater API-Routen. - * Prüft, ob ein gültiges Access_Token im Aithorization-Headder mitgesendet wurde. - */ -export const requireAuth = (req, res, next) => { - // Der Standard für Tokens ist der Header: "Authorization: Bearer " - const authHeader = req.headers.authorization; - - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return res.status(401).json({ - error: 'ERR_UNAUTHORIZED_NO_TOKEN' - }); - } - - const token = authHeader.split(' ')[1]; - - try { - const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET); - - req.user = decoded; - - next(); - } catch (error) { - return res.status(401).json({ - error: 'ERR_UNAUTHORIZED_INVALID_TOKEN' - }); - } -}; \ No newline at end of file diff --git a/lobby-backend/src/routes/auth.routes.js b/lobby-backend/src/routes/auth.routes.js deleted file mode 100644 index d76be3c..0000000 --- a/lobby-backend/src/routes/auth.routes.js +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express'; -import * as authController from '../controllers/auth.controller.js'; - -const router = express.Router(); - -router.post('/register', authController.register); -router.post('/verify-email', authController.verifyEmail); -router.post('/login', authController.login); -router.post('/refresh', authController.refresh); -router.post('/resend-otp', authController.resendOTP); -router.post('/forgot-password', authController.requestPasswordReset); -router.post('/reset-password', authController.resetPassword); - -export default router; \ No newline at end of file diff --git a/lobby-backend/src/routes/sector.routes.js b/lobby-backend/src/routes/sector.routes.js deleted file mode 100644 index 690770f..0000000 --- a/lobby-backend/src/routes/sector.routes.js +++ /dev/null @@ -1,10 +0,0 @@ -import express from 'express'; -import * as sectorController from '../controllers/sector.controller.js'; -import { requireAuth } from '../middlewares/auth.middleware.js'; - -const router = express.Router(); - -router.get('/list', requireAuth, sectorController.getSectorsList); -router.post('/join', requireAuth, sectorController.joinSector); - -export default router; \ No newline at end of file diff --git a/lobby-backend/src/scripts/initDb.js b/lobby-backend/src/scripts/initDb.js deleted file mode 100644 index e75deca..0000000 --- a/lobby-backend/src/scripts/initDb.js +++ /dev/null @@ -1,186 +0,0 @@ -import pool from '../db.js'; - -// ============================================================================== -// Void-Genesis - Datenbank Initialisierungs-Skript -// Führt alle nötigen CREATE TABLE Befehle aus, falls die Tabellen nicht existieren. -// ============================================================================== - -const createTablesQuery = ` - --- ------------------------------------------------------------------------------ --- DROP TABLES --- WICHTIG: Die Reihenfolge beim Löschen ist entscheidend! --- Tabellen mit Fremdschlüsseln (Foreign Keys) müssen zuerst gelöscht werden. --- ------------------------------------------------------------------------------ -DROP TABLE IF EXISTS refresh_tokens; -DROP TABLE IF EXISTS auth_otps; -DROP TABLE IF EXISTS lobby_accounts_sectors; -- Zuerst die Brückentabelle löschen -DROP TABLE IF EXISTS sectors; -- Dann die Sektoren-Tabelle -DROP TABLE IF EXISTS lobby_accounts; -- Zuletzt die Haupt-Nutzer-Tabelle - --- ------------------------------------------------------------------------------ --- TABLE: lobby_accounts --- Basis-Nutzerdaten für das gesamte Void-Genesis Universum. --- ------------------------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS lobby_accounts ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - username VARCHAR(50) UNIQUE NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - password_hash VARCHAR(255) NOT NULL, - is_email_verified BOOLEAN DEFAULT false, - res_premium INT DEFAULT 0, - is_admin BOOLEAN DEFAULT false, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- ------------------------------------------------------------------------------ --- TABLE: sectors --- Beinhaltet die globalen Metadaten zu allen verfügbaren Spiel-Sektoren. --- Hier definieren wir, welche Server existieren und welche Eigenschaften sie haben. --- ------------------------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS sectors ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(50) UNIQUE NOT NULL, - status VARCHAR(20) DEFAULT 'offline', - speed_fleet INT DEFAULT 1, - speed_economy INT DEFAULT 1, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- ------------------------------------------------------------------------------ --- TABLE: lobby_accounts_sectors (Brückentabelle) --- Verknüpft einen Lobby-Account mit einem Sektor. --- Existiert hier ein Eintrag, bedeutet das: Der Nutzer hat in diesem Sektor einen Spielstand. --- ------------------------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS lobby_accounts_sectors ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - lobby_account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE, - sector_id UUID REFERENCES sectors(id) ON DELETE CASCADE, - local_username VARCHAR(50) NOT NULL, - gender VARCHAR(20) NOT NULL, - res_premium INT DEFAULT 0, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW(), - UNIQUE(lobby_account_id, sector_id), - UNIQUE(sector_id, local_username) -); - --- ------------------------------------------------------------------------------ --- TABLE: auth_otps --- Speichert temporäre 6-stellige Codes für Registrierung oder Passwort-Reset. --- ------------------------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS auth_otps ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE, - otp_code VARCHAR(6) NOT NULL, - type VARCHAR(20) NOT NULL, - expires_at TIMESTAMPTZ NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- ------------------------------------------------------------------------------ --- TABLE: refresh_tokens --- Speichert langlebige Tokens für die "Angemeldet bleiben"-Funktion. --- ------------------------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS refresh_tokens ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - account_id UUID REFERENCES lobby_accounts(id) ON DELETE CASCADE, - token VARCHAR(500) UNIQUE NOT NULL, - expires_at TIMESTAMPTZ NOT NULL, - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- ------------------------------------------------------------------------------ --- TRIGGER: Auto-update 'updated_at' timestamp --- Diese Funktion setzt das 'updated_at' Feld bei jeder Änderung auf die aktuelle Zeit. --- ------------------------------------------------------------------------------ -CREATE OR REPLACE FUNCTION update_modified_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = NOW(); - RETURN NEW; -END; -$$ language 'plpgsql'; - --- Wir wenden den Trigger nur an, wenn er noch nicht existiert. -DO $$ -BEGIN - -- Trigger für lobby_accounts - IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_lobby_accounts_modtime') THEN - CREATE TRIGGER update_lobby_accounts_modtime - BEFORE UPDATE ON lobby_accounts - FOR EACH ROW - EXECUTE FUNCTION update_modified_column(); - END IF; - - -- Trigger für sectors - IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_sectors_modtime') THEN - CREATE TRIGGER update_sectors_modtime - BEFORE UPDATE ON sectors - FOR EACH ROW - EXECUTE FUNCTION update_modified_column(); - END IF; - - -- Trigger für lobby_accounts_sectors (WICHTIG für unsere "Zuletzt gespielt" Funktion) - IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'update_lobby_accounts_sectors_modtime') THEN - CREATE TRIGGER update_lobby_accounts_sectors_modtime - BEFORE UPDATE ON lobby_accounts_sectors - FOR EACH ROW - EXECUTE FUNCTION update_modified_column(); - END IF; -END $$; -`; - -// ============================================================================== -// Ausführungs-Logik -// ============================================================================== -async function initDatabase() { - console.log('⏳ Starte Datenbank-Initialisierung...'); - - // Wir holen uns eine Verbindung aus dem Pool - const client = await pool.connect(); - - try { - // Wir senden den kompletten SQL-String an die Datenbank - await client.query(createTablesQuery); - console.log('✅ Tabellen wurden erfolgreich erstellt (oder existierten bereits).'); - -const sectorCheck = await client.query('SELECT count(*) FROM sectors'); - if (parseInt(sectorCheck.rows[0].count) === 0) { - console.log('🌱 Füge 24 Test-Sektoren ein...'); - - const greekAlphabet = [ - 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', - 'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu', - 'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma', - 'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega' - ]; - - // Wir generieren für jeden Namen ein dynamisches INSERT - const insertPromises = greekAlphabet.map((name, index) => { - // Wir variieren die Geschwindigkeiten leicht für optische Vielfalt - const speed = (index % 4) + 1; - return client.query(` - INSERT INTO sectors (name, status, speed_fleet, speed_economy) - VALUES ($1, 'online', $2, $2) - `, [name, speed]); - }); - - await Promise.all(insertPromises); - console.log('✅ 24 Test-Sektoren erfolgreich eingefügt.'); - } - - } catch (error) { - console.error('❌ Fehler beim Erstellen der Tabellen:', error); - } finally { - // Ganz wichtig: Verbindung wieder freigeben - client.release(); - // Den Node-Prozess beenden, damit das Skript nicht in der Konsole hängen bleibt - process.exit(); - } -} - -// Skript direkt aufrufen -initDatabase(); \ No newline at end of file diff --git a/lobby-backend/src/server.js b/lobby-backend/src/server.js deleted file mode 100644 index 87ce497..0000000 --- a/lobby-backend/src/server.js +++ /dev/null @@ -1,55 +0,0 @@ -// ============================================================================== -// Void-Genesis - Lobby Backend Server (Einstiegspunkt) -// ============================================================================== - -import express from 'express'; -import cors from 'cors'; -import pool from './db.js'; - -import authRoutes from './routes/auth.routes.js'; -import sectorRoutes from './routes/sector.routes.js'; - -const app = express(); -const PORT = process.env.PORT || 3000; - -// Middleware -app.use(cors()); -app.use(express.json()); - -app.use('/api/auth', authRoutes); -app.use('/api/sectors', sectorRoutes); - -// Unsere bestehende Health-Route -app.get('/api/health', (req, res) => { - res.json({ - status: 'ok', - message: 'Void-Genesis Lobby Backend läuft!', - timestamp: new Date().toISOString() - }); -}); - -// Eine Test-Route für die Datenbank -app.get('/api/db-test', async (req, res) => { - try { - const result = await pool.query('SELECT NOW()'); - - res.json({ - status: 'success', - message: 'Datenbankverbindung steht!', - db_time: result.rows[0].now - }); - } catch (err) { - console.error('Fehler bei der DB-Abfrage:', err); - res.status(500).json({ - status: 'error', - message: 'Datenbankfehler', - error: err.message - }); - } -}); - -// Server starten -app.listen(PORT, () => { - console.log(`🚀 Void-Genesis Lobby Backend gestartet auf Port ${PORT}`); - console.log(`Environment: ${process.env.NODE_ENV}`); -}); \ No newline at end of file diff --git a/lobby-backend/src/services/auth.service.js b/lobby-backend/src/services/auth.service.js deleted file mode 100644 index ca47011..0000000 --- a/lobby-backend/src/services/auth.service.js +++ /dev/null @@ -1,606 +0,0 @@ -import argon2 from 'argon2'; -import jwt from 'jsonwebtoken'; -import pool from '../db.js'; -import { sendEmail } from './email.service.js'; - -/** - * Generiert einen zufälligen 6-stelligen Code als String - */ -const generateOTP = () => { - return Math.floor(100000 + Math.random() * 900000).toString(); -}; - -/** - * Registriert einen neuen Nutzer, speichert das OTP und löst den E-Mail-Versand aus. - * - * @param {string} username - Der gewünschte Benutzername - * @param {string} email - Die E-Mail-Addresse - * @param {string} password - Das Klartext-Passwort - * @returns {object} - Die accountId des neuen Nutzers - */ -export const registerUser = async (username, email, password) => { - // Wir holen uns einen dedizierten Client aus dem pool für unsere Transaktion - const client = await pool.connect(); - - const lowerCaseEmail = email.toLowerCase(); - - try { - // Transaktion starten: Entweder alles klappt, oder nichts wird gespeichert - await client.query('BEGIN'); - - // Prüfen, ob der Nutzer oder die E-Mail schon existiert - const checkResult = await client.query(` - SELECT username, email FROM lobby_accounts - WHERE LOWER(username) = LOWER($1) OR email = $2`, - [username, lowerCaseEmail] - ); - - if (checkResult.rows.length > 0) { - const existingRecord = checkResult.rows[0]; - - if (existingRecord.email === lowerCaseEmail) { - const error = new Error('Email already exists'); - error.code = 'ERR_EMAIL_TAKEN'; - throw error; - } else { - const error = new Error('Username already exists'); - error.code = 'ERR_USERNAME_TAKEN'; - throw error; - } - } - - // Passwort sicher hashen - const passwordHash = await argon2.hash(password); - - // Nutzer in de Datenbank eintragen - const insertAccount = await client.query(` - INSERT INTO lobby_accounts (username, email, password_hash) - VALUES ($1, $2, $3) RETURNING id`, - [username, lowerCaseEmail, passwordHash] - ); - const accountId = insertAccount.rows[0].id; - - // OTP generieren uns in der auth_otps Tabelle speichern (15min Gültigkeit) - const otpCode = generateOTP(); - await client.query(` - INSERT INTO auth_otps (account_id, otp_code, type, expires_at) - VALUES ($1, $2, 'register', NOW() + INTERVAL '15 minutes')`, - [accountId, otpCode] - ); - - // Registierungs E-Mail über unseren E-Mail Service versenden - const mailHtml = ` -

Willkommen bei Void-Genesis, ${username}!

-

Dein Verifizierungscode lautet:

-

${otpCode}

-

Dieser Code ist 15 Minuten lang gültig.

- `; - await sendEmail(lowerCaseEmail, username, 'Void-Genesis - Aktiviere deinen Account', mailHtml); - - // Wenn wir hier ankommen, lief alles fehlerfrei, wir committen die Daten. - await client.query('COMMIT'); - - return { accountId }; - } catch (error) { - // bei JEDEM Fehler machen wir einen Rollback. Die Datenbank bleibt sauber. - await client.query('ROLLBACK'); - - if (error.code === '23505') { - if (error.constraint === 'lobby_accounts_username_key') { - const customErr = new Error('Username already exists'); - customErr.code = 'ERR_USERNAME_TAKEN'; - throw customErr; - } - if (error.constraint === 'lobby_accounts_email_key') { - const customErr = new Error('Email already exists'); - customErr.code = 'ERR_EMAIL_TAKEN'; - throw customErr; - } - } - - throw error; - } finally { - client.release(); - } -} - -/** - * Verifiziert die E-Mail-Adresse eines Nutzers - * - * @param {string} email - Die E-Mail-Addresse des Nutzers - * @param {string} otpCode - Der 6-stellige Code aus der E-Mail - * @returns {boolean} - true wenn erfolgreich - */ -export const verifyEmail = async (email, otpCode) => { - const client = await pool.connect(); - const lowerCaseEmail = email.toLowerCase(); - - try { - await client.query('BEGIN'); - - // Nutzer anhand der E-Mail suchen un die ID sowie Verifizierungsstatus abfragen - const accountResult = await client.query( - 'SELECT id, is_email_verified FROM lobby_accounts WHERE email = $1', - [lowerCaseEmail] - ); - - if (accountResult.rows.length === 0) { - // ERR_USER_NOT_FOUND: Es existiert kein Account mit dieser E-Mail - const error = new Error('User not found'); - error.code = 'ERR_USER_NOT_FOUND'; - throw error; - } - - const account = accountResult.rows[0]; - - // Prüfen, ob der Nutzer nicht ohnehin schon verifiziert ist - if (account.is_email_verified) { - // ERR_ALREADY_VERIFIED: Diese E-Mail-Addresse wurde bereits bestätigt - const error = new Error('Already verified'); - error.code = 'ERR_ALREADY_VERIFIED'; - throw error; - } - - // Den passenden OTP-Code in der Datenbank suchen - const otpResult = await client.query(` - SELECT id, expires_at FROM auth_otps - WHERE account_id = $1 AND otp_code = $2 AND type = 'register'`, - [account.id, otpCode] - ); - - if (otpResult.rows.length === 0) { - // ERR_INVALID_OTP: Der eingegebene Code ist falsch. - const error = new Error('Invalid OTP'); - error.code = 'ERR_INVALID_OTP'; - throw error; - } - - const otpRecord = otpResult.rows[0]; - - // Prüfen, ob der Cide bereits abgelaufen ist (älter als 15 Minuten) - if (new Date() > new Date(otpRecord.expires_at)) { - // ERR_OTP_EXPIRED: Der Code ist abgelaufen. Bitte fordere einen neuen an. - const error = new Error('OTP expired'); - error.code = 'ERR_OTP_EXPIRED'; - throw error; - } - - // Account als verifiziert markieren - await client.query( - 'UPDATE lobby_accounts SET is_email_verified = true WHERE id = $1', - [account.id] - ); - - // Den genutzten OTP-Code aus der Datenbank löschen (Sicherheit und Sauberkeit) - await client.query( - 'DELETE FROM auth_otps WHERE id = $1', - [otpRecord.id] - ); - - // Alles lief perfekt, Änderungen speichern - await client.query('COMMIT'); - return true; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } -}; - -/** - * Loggt einen Nutzer ein, verifiziert das Passwort uns erstellt JWT-Tokens - * - * @param {string} identifier - Kann der Benutzername oder die E-Mail-Address sein - * @param {string} password - Das eingegebene Klartext-Passwort - * @returns {object} - Enthält accessToken, refreshToken und grundlegende Nutzerdaten - */ -export const loginUser = async (identifier, password) => { - const client = await pool.connect(); - - try { - // Nutzer suchen - const accountResult = await client.query(` - SELECT id, username, email, password_hash, is_email_verified, is_admin, res_premium - FROM lobby_accounts - WHERE LOWER(username) = LOWER($1) OR email = LOWER($1)`, - [identifier] - ); - - // Wenn kein Nutzer gefunden wurde - if (accountResult.rows.length === 0) { - // ERR_INVALID_CREDENTIALS: Benutzername/E-Mail oder Passwort falsch - const error = new Error('Invalid credentials'); - error.code = 'ERR_INVALID_CREDENTIALS'; - throw error; - } - - const account = accountResult.rows[0]; - - // Prüfen, ob die Email bereits bestätigt wurde - if (!account.is_email_verified) { - // ERR_EMAIL_NOT_VERIFIED: Bitte bestätige zuerst deine E-Mail-Addresse - const error = new Error('Email not verified'); - error.code = 'ERR_EMAIL_NOT_VERIFIED'; - error.email = account.email; - throw error; - } - - // Passwort vergleichen (argon2) - const isPasswordValid = await argon2.verify(account.password_hash, password); - if (!isPasswordValid) { - const error = new Error('Invalied credentials'); - error.code = 'ERR_INVALID_CREDENTIALS'; - throw error; - } - - // JWT-Tokens geneieren - // Das Access-Token speichert die wichtigsten Infos für schnelle Abfragen - const tokenPayload = { - accountId: account.id, - username: account.username, - isAdmin: account.is_admin - }; - - const accessToken = jwt.sign( - tokenPayload, - process.env.JWT_ACCESS_SECRET, - { expiresIn: '15m' } - ); - - const refreshToken = jwt.sign( - { accountId: account.id }, - process.env.JWT_REFRESH_SECRET, - { expiresIn: '30d' } - ); - - // Refresh-Token in der Datenbank speichern (Vorbereitung für das Single-Session System) - await client.query(` - INSERT INTO refresh_tokens (account_id, token, expires_at) - VALUES ($1, $2, NOW() + INTERVAL '30 days')`, - [account.id, refreshToken] - ); - - // Erfolgreiche Rückgabe der Daten - return { - accessToken, - refreshToken, - user: { - id: account.id, - username: account.username, - email: account.email, - res_premium: account.res_premium - } - }; - } finally { - client.release(); - } -}; - -/** - * Prüft ein Refresh-Token und generiert ein neues, aktuelles Access-Token - * - * @param {string} refreshToken - Das langlebige Refresh-Token des Nutzers. - * @returns {object} - Enthält das neue accessToken - */ -export const refreshAccessToken = async (refreshToken) => { - const client = await pool.connect(); - - try { - // JWT-Signatur und Ablaufdatum des Refresh-Tokens kryptografisch verifizieren - // Wenn das Token manipuliert oder abgelaufen ist, wirft jwt.verify sofort einen Fehler. - const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET); - const { accountId } = payload; - - // Sicherheits-Check: Existiert diese Token noch in unserer Datenbank? - // Wenn sich ein zweites Gerät eingeloggt hat, wurden die altenm Tokens gelöscht. - // Dieser Check schlägt dasnn fehl und blockiert den unerlaubten Zugriff! - const tokenResult = await client.query( - 'SELECT id FROM refresh_tokens WHERE account_id = $1 AND token = $2', - [accountId, refreshToken] - ); - - if (tokenResult.rows.length === 0) { - const error = new Error('Refresh token was revoked or does not exist'); - error.code = 'ERR_UNAUTHORIZED'; - throw error; - } - - // Aktuelle Benutzerdaten frosch aus der Datenbank abfragen - // Das behebt das "Stale Data" Problem. Falls der Admin Status geändert wurde, - // steht er im neuen Token sofort korrekt drin, ohne die DB bei jedem API.Call zu belasten - const accountResult = await client.query( - 'SELECT id, username, email, is_admin, res_premium FROM lobby_accounts WHERE id = $1', - [accountId] - ); - - if (accountResult.rows.length === 0) { - const error = new Error('Account no longer exists'); - error.code = 'ERR_UNAUTHORIZED'; - throw error; - } - - const account = accountResult.rows[0]; - - // Neues kurzlebiges Access-Token erstellen (wieder für 15 min) - const tokenPayload = { - accountId: account.id, - username: account.username, - isAdmin: account.is_admin - }; - - const accessToken = jwt.sign( - tokenPayload, - process.env.JWT_ACCESS_SECRET, - { expiresIn: '15m' } - ); - - return { - accessToken, - user: { - id: account.id, - username: account.username, - email: account.email, - res_premium: account.res_premium - } - }; - } catch (error) { - // Abfangen von JWT-spezifischen Fehlern (z.B. Token abgelaufen oder Signatur falsch) - if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError' || error.name === 'SyntaxError') { - const customErr = new Error('Invalid, expired or malformed token'); - customErr.code = 'ERR_UNAUTHORIZED'; - throw customErr; - } - - // Alle anderen Fehhler (zB Datenbank Fehler) weiterwerfen - throw error; - } finally { - client.release(); - } -} - -/** - * Sendet einen neuen OTP-Code an den Nutzer, falls der alte abgelaufen ist oder nicht ankam. - * Vorherige, ungenutzte Codes werdeb zur Sicherheit invalidiert (gelöscht) - * - * @param {string} email - Die E-Mail Addresse des Nutzers - * @returns {boolean} - true, wenn die E-Mail erfolgreich versendet wurde. - */ -export const resendOTP = async (email) => { - const client = await pool.connect(); - const lowerCaseEmail = email.toLowerCase(); - - try { - await client.query('BEGIN'); - - // Prüfen, ob der Nutzer überhaupt existiert - const accountResult = await client.query( - 'SELECT id, username, is_email_verified FROM lobby_accounts WHERE email = $1', - [lowerCaseEmail] - ); - - if (accountResult.rows.length === 0) { - // Aus Sicherheitsgründen verraten wir dem Frontend nicht zwingend, - // dass die E-Mail nicht existieren, aber für unser internes Error-Handling - // werfen wir einen spezifischen Fehler. - const error = new Error('User nor found'); - error.code = 'ERR:USER_NOT_FOUND'; - throw error; - } - - const account = accountResult.rows[0]; - - // Prüfen, ob der Account nicht vielleicht schon verifiziert ist - // Wenn ja, macht ein neuer Code keinen Sinn. - if (account.is_email_verified) { - const error = new Error('Already verified'); - error.code = 'ERR_ALREADY_VERIFIED'; - throw error; - } - - const lastOtpResult = await client.query(` - SELECT created_at FROM auth_otps WHERE account_id = $1 AND type = 'register' ORDER BY created_at DESC LIMIT 1`, - [account.id] - ); - - if (lastOtpResult.rows.length > 0) { - const lastCreatedTime = new Date(lastOtpResult.rows[0].created_at).getTime(); - const currentTime = new Date().getTime(); - const timeDifference = currentTime - lastCreatedTime; - - if (timeDifference < 120000) { - const error = new Error('Cooldown active'); - error.code = 'ERR_COOLDOWN_ACTIVE'; - throw error; - } - } - - // Zur Sichherheit alle alten, noch aktiven Registierungs-OTPs dieses Nutzers löschen. - // So verhindern wir, dass alte Codes in Postfächern noch funktionieren und minimieren - // Spam-Risiken. - await client.query( - `DELETE FROM auth_otps WHERE account_id = $1 AND type = 'register'`, - [account.id] - ); - - // Einen frischen, 6-stelligen Cide generieren - const newOtpCode = generateOTP(); - - // Den neuen Code in die Datenbank eintragen - await client.query(` - INSERT INTO auth_otps (account_id, otp_code, type, expires_at) - VALUES ($1, $2, 'register', NOW() + INTERVAL '15 minutes')`, - [account.id, newOtpCode] - ); - - // 6. Die neue E-Mail zusammenbauen und versenden - const mailHtml = ` -

Hallo ${account.username}, hier ist dein neuer Code!

-

Du hast einen neuen Verifizierungscode für Void-Genesis angefordert:

-

${newOtpCode}

-

Dieser Code ist ab jetzt wieder 15 Minuten lang gültig.

- `; - - await sendEmail(lowerCaseEmail, account.username, 'Void-Genesis - Dein neue Code', mailHtml); - - await client.query('COMMIT'); - return true; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } -}; - -/** - * Startet den Prozess zum Zurücksetzten des Passworts. - * Generiert einen speziellen OTP-Code und sendet ihn per E-Mail. - * - * @param {string} email - Die Email-Adresse des Nutzers - * @returns {boolean} - true, wenn die E-Mail erfolgreich versendet wurde. - */ -export const requestPasswordReset = async (email) => { - const client = await pool.connect(); - const lowerCaseEmail = email.toLowerCase(); - - try { - await client.query('BEGIN'); - - const accountResult = await client.query( - 'SELECT id, username FROM lobby_accounts WHERE email = $1', - [lowerCaseEmail] - ); - - if (accountResult.rows.length === 0) { - const error = new Error('User not found'); - error.code = 'ERR_USER_NOT_FOUND'; - throw error; - } - - const lastOtpResult = await client.query(` - SELECT created_at FROM auth_otps WHERE account_id = $1 AND type = 'password_reset' ORDER BY created_at DESC LIMIT 1`, - [accountResult.id] - ); - - if (lastOtpResult.rows.length > 0) { - const lastCreatedTime = new Date(lastOtpResult.rows[0].created_at).getTime(); - const currentTime = new Date().getTime(); - const timeDifference = currentTime - lastCreatedTime; - - if (timeDifference < 120000) { - const error = new Error('Cooldown active'); - error.code = 'ERR_COOLDOWN_ACTIVE'; - throw error; - } - } - - const account = accountResult.rows[0]; - - await client.query( - `DELETE FROM auth_otps WHERE account_id = $1 AND type = 'password_reset'`, - [account.id] - ); - - const resetCode = generateOTP(); - - await client.query(` - INSERT INTO auth_otps (account_id, otp_code, type, expires_at) - VALUES ($1, $2, 'password_reset', NOW() + INTERVAL '15 minutes')`, - [account.id, resetCode] - ); - - const mailHtml = ` -

Hallo ${account.username},

-

Du hast eine Anfrage zum Zurücksetzen deines Passworts gestellt.

-

Dein Code lautet:

-

${resetCode}

-

Dieser Code ist 15 Minuten lang gültig. Falls du diese Anfrage nicht gestellt hast, kannst du diese E-Mail ignorieren.

- `; - - await sendEmail(lowerCaseEmail, account.username, 'Void-Genesis - Passwort zurücksetzen', mailHtml); - - await client.query('COMMIT'); - return true; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } -}; - - -/** - * Setzt das neue Passwort, soffern der OTP-Code korrekt und nicht abgelaufen ist - * - * @param {string} email - Die E-Mail Addresse des Nutzers - * @param {string} otpCode - Der 6-stellige Code aus der E-Mail - * @param {string} newPassword - das neue Klartext passwort - * @returns {boolean} - true wenn das Passwort erfolgreich geändert wurde - */ -export const resetPassword = async (email, otpCode, newPassword) => { - const client = await pool.connect(); - const lowerCaseEmail = email.toLowerCase(); - - try { - await client.query('BEGIN'); - - const accountResult = await client.query( - 'SELECT id FROM lobby_accounts WHERE email = $1', - [lowerCaseEmail] - ); - - if (accountResult.rows.length === 0) { - const error = new Error('User not found'); - error.code = 'ERR_USER_NOT_FOUND'; - throw error; - } - - const account = accountResult.rows[0]; - - const otpResult = await client.query(` - SELECT id, expires_at FROM auth_otps - WHERE account_id = $1 AND otp_code = $2 AND type = 'password_reset'`, - [account.id, otpCode] - ); - - if (otpResult.rows.length === 0) { - const error = new Error('Invalid OTP'); - error.code = 'ERR_INVALID_OTP'; - throw error; - } - - const otpRecord = otpResult.rows[0]; - - if (new Date() > new Date(otpRecord.expires_at)) { - const error = new Error('OTP expired'); - error.code = 'ERR_OTP_EXPIRED'; - throw error; - } - - const passwordHash = await argon2.hash(newPassword); - - await client.query( - 'UPDATE lobby_accounts SET password_hash = $1 WHERE id = $2', - [passwordHash, account.id] - ); - - await client.query( - 'DELETE FROM refresh_tokens WHERE account_id = $1', - [account.id] - ); - - await client.query('COMMIT'); - return true; - - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } -}; diff --git a/lobby-backend/src/services/email.service.js b/lobby-backend/src/services/email.service.js deleted file mode 100644 index fc56827..0000000 --- a/lobby-backend/src/services/email.service.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Sendet eine E-Mail über die Brevo REST API. - * - * @param {string} toEmail - Die Ziel E-Mail Adresse - * @param {string} toName - Der Benutzername des Empfängers - * @param {string} subject - Der Betreff der E-Mail - * @param {string} htmlContent - Der HTML-Inhalt der E-Mail - * @returns {boolean} - true wenn erfolgreich, wirft einen Fehler wenn nicht. - */ -export const sendEmail = async (toEmail, toName, subject, htmlContent) => { - const mailMode = process.env.MAIL_MODE || 'development'; - - // Im Development-Modus loggen wir nur, um das API-Limit nicht zu belasten - if (mailMode === 'development') { - console.log(`\n[DEV-MAIL-SIMULATION] An: ${toEmail}`); - console.log(`[DEV-MAIL-SIMULATION] Betreff: ${subject}`); - console.log(`[DEV-MAIL-SIMULATION] Inhalt:\n${htmlContent}\n`); - return true; - } - - // Echter Versand über Brevo - try { - const response = await fetch('https://api.brevo.com/v3/smtp/email', { - method: 'POST', - headers: { - 'accept': 'application/json', - 'api-key': process.env.BREVO_API_KEY, - 'content-type': 'application/json' - }, - body: JSON.stringify({ - sender: { - name: process.env.BREVO_SENDER_NAME, - email: process.env.BREVO_SENDER_EMAIL - }, - to: [{ - email: toEmail, - name: toName - }], - subject: subject, - htmlContent: htmlContent - }) - }); - - if (!response.ok) { - const errorData = await response.json(); - console.error('Brevo API Fehler-Details:', errorData); - throw new Error('Fehler bei der Kommunikation mit dem E-Mail-Provider.'); - } - - return true; - } catch (error) { - console.error('Fehler im email.service:', error); - throw error; // Den Fehler werfen wir weiter, damit der Aufrufer ihn behandeln kann. - } -} \ No newline at end of file diff --git a/lobby-backend/src/services/sector.service.js b/lobby-backend/src/services/sector.service.js deleted file mode 100644 index f792831..0000000 --- a/lobby-backend/src/services/sector.service.js +++ /dev/null @@ -1,107 +0,0 @@ -import pool from '../db.js'; - -/** - * Ruft die Sektorlisten für einen bestimmten Account ab. - * Unterteilt das Ergebnis in bereits beigetretene Sektoren ("Meine Sektoren") - * und noch verfügbare Sektoren ("Neue Sektoren"). - * - * @param {string} accountId - Die UUID des aktuell eingeloggten Spielers - * - * @returns {Promise} - Ein Objekt mit den Arrays 'mySectors' und 'newSectors' - */ -export const getSectorsList = async (accountId) => { - const client = await pool.connect(); - - try { - const mySectorsResult = await client.query(` - SELECT - s.id, s.name, s.status, s.speed_fleet, s.speed_economy, - las.local_username, las.gender, - las.created_at as joined_at, - las.updated_at as last_played - FROM sectors s - JOIN lobby_accounts_sectors las ON s.id = las.sector_id - WHERE las.lobby_account_id = $1 - ORDER BY las.updated_at DESC - `, [accountId]); - - const newSectorsResult = await client.query(` - SELECT - id, name, status, speed_fleet, speed_economy - FROM sectors - WHERE status != 'offline' - AND id NOT IN ( - SELECT sector_id - FROM lobby_accounts_sectors - WHERE lobby_account_id = $1 - ) - ORDER BY created_at ASC - `, [accountId]); - - const mapSectorData = (sector) => ({ - ...sector, - online_players: Math.floor(Math.random() * 150) + 10, - planets_count: (Math.floor(Math.random() * 150) + 10) * 12 - }); - - return { - mySectors: mySectorsResult.rows.map(mapSectorData), - newSectors: newSectorsResult.rows.map(mapSectorData) - }; - - } catch (error) { - console.error('[SectorService] Fehler beim Auslesen der Sektordaten:', error); - throw error; - - } finally { - client.release(); - } -}; - -/** - * Trägt einen Spieler in einen neuen Sektor ein. - * Führt die eigentliche Datenbankoperation durch und wirft spezifische - * Fehler-Objekte, falls Constraints (UNIQUE-Regeln) verletzt werden. - * - * @param {string} accountId - Die UUID des Nutzers - * @param {string} sectorId - Die UUID des Sektors - * @param {string} localUsername - Der Commander-Name für diesen Sektor - * @param {string} gender - Das gewählte Geschlecht ('male' oder 'female') - * - * @returns {boolean} - true bei Erfolg - */ -export const joinSector = async (accountId, sectorId, localUsername, gender) => { - const client = await pool.connect(); - - try { - // Wir versuchen, den Datensatz direkt einzufügen. - await client.query(` - INSERT INTO lobby_accounts_sectors (lobby_account_id, sector_id, local_username, gender) - VALUES ($1, $2, $3, $4) - `, [accountId, sectorId, localUsername, gender]); - - return true; - - } catch (error) { - // Fehlercode 23505 = Postgres "unique_violation" - if (error.code === '23505') { - - if (error.constraint === 'lobby_accounts_sectors_lobby_account_id_sector_id_key') { - const customErr = new Error('Already in sector'); - customErr.code = 'ERR_ALREADY_IN_SECTOR'; - throw customErr; - } - - if (error.constraint === 'lobby_accounts_sectors_sector_id_local_username_key') { - const customErr = new Error('Local username taken'); - customErr.code = 'ERR_LOCAL_USERNAME_TAKEN'; - throw customErr; - } - } - - throw error; - - } finally { - client.release(); - } -}; \ No newline at end of file diff --git a/lobby-frontend/Dockerfile.dev b/lobby-frontend/Dockerfile.dev deleted file mode 100644 index 7601541..0000000 --- a/lobby-frontend/Dockerfile.dev +++ /dev/null @@ -1,29 +0,0 @@ -# ============================================================================== -# Void-Genesis - Lobby Frontend (Dev Environment) -# ============================================================================== - -# Auch für das Frontend nutzen wir die kleine Alpine-Version von Node.js -FROM node:22-alpine - -# Arbeitsverzeichnis für das Frontend -WORKDIR /app - -# Auch hier: Erst die package-Dateien kopieren für optimales Docker-Caching -COPY package*.json ./ - -# Installiere die Frontend-Abhängigkeiten (Vite, PixiJS, etc.) -RUN npm install - -# Vite nutzt im Hintergrund 'Chokidar', um Dateiänderungen zu überwachen (Hot Reload). -# In Docker-Umgebungen klappt das manchmal nicht sofort. Durch USEPOLLING zwingen -# wir Chokidar, aktiv nach Änderungen zu suchen. Das macht das Hot-Reloading robuster. -ENV CHOKIDAR_USEPOLLING=true - -# Wir dokumentieren den Standard-Port von Vite -EXPOSE 5173 - -# Starte den Vite Dev-Server. -# WICHTIG: Das '--host' Flag ist entscheidend! Standardmäßig lauscht Vite nur auf -# localhost (127.0.0.1) im Container. Mit '--host' lauscht Vite auf 0.0.0.0 und -# erlaubt so, dass Anfragen von außerhalb des Containers (z.B. vom Browser) durchkommen. -CMD ["npm", "run", "dev", "--", "--host"] \ No newline at end of file diff --git a/lobby-frontend/agb.html b/lobby-frontend/agb.html deleted file mode 100644 index d139dc3..0000000 --- a/lobby-frontend/agb.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - Void-Genesis | Rechtliches - - - - - - - - - - \ No newline at end of file diff --git a/lobby-frontend/datenschutz.html b/lobby-frontend/datenschutz.html deleted file mode 100644 index 04bfbe6..0000000 --- a/lobby-frontend/datenschutz.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Void-Genesis | Rechtliches - - - - - - - - - - \ No newline at end of file diff --git a/lobby-frontend/impressum.html b/lobby-frontend/impressum.html deleted file mode 100644 index 25b8522..0000000 --- a/lobby-frontend/impressum.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - Void-Genesis | Rechtliches - - - - - - - - - - - \ No newline at end of file diff --git a/lobby-frontend/index.html b/lobby-frontend/index.html deleted file mode 100644 index 82c05b0..0000000 --- a/lobby-frontend/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - Void-Genesis | Login - - - - -
-
-
- -
- - DEV-BUILD: Das Spiel befindet sich in der Entwicklung. - Die Registrierung ist derzeit deaktiviert (E-Mail-System pausiert). -
- -
-
- - -
- -

- Bitte gib den 6-stelligen Code aus deiner E-Mail ein. -

- -
- - -
- -
- - -
- -
- - -
- - - -
- - -
- -
- - - - - - - - - -
-
- - - - - -
-
-
-
- -
- - -
-
-
-
- -
- - - - - \ No newline at end of file diff --git a/lobby-frontend/package-lock.json b/lobby-frontend/package-lock.json deleted file mode 100644 index 5679e0d..0000000 --- a/lobby-frontend/package-lock.json +++ /dev/null @@ -1,2152 +0,0 @@ -{ - "name": "void-genesis-lobby-frontend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "void-genesis-lobby-frontend", - "version": "1.0.0", - "dependencies": { - "pixi.js": "^8.0.4" - }, - "devDependencies": { - "free-tex-packer-core": "^0.3.5", - "vite": "^5.2.8" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@jvitela/mustache-wax": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.4.tgz", - "integrity": "sha512-cGugVYp0WO7w89KQlyyt2rQyCO5MbrKeqtIAkG8oBfOaiHoeGeKG7WYdBFHftvoZUuK4WFbWyn3hgFipdetC+Q==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "mustache": "^4.2.0" - } - }, - "node_modules/@pixi/colord": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", - "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", - "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", - "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", - "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", - "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", - "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", - "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", - "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", - "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", - "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", - "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", - "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", - "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", - "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", - "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", - "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", - "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", - "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", - "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", - "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", - "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", - "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", - "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", - "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", - "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", - "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/earcut": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-3.0.0.tgz", - "integrity": "sha512-k/9fOUGO39yd2sCjrbAJvGDEQvRwRnQIZlBz43roGwUZo5SHAmyVvSFyaVVZkicRVCaDXPKlbxrUcBuJoSWunQ==", - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webgpu/types": { - "version": "0.1.70", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.70.tgz", - "integrity": "sha512-LFiNHHKMvmAEvwVew3JLJmTdShhbdwRFSImUshGhE2mGE8ybQzIo63l5uRp+YKnNx+8Qno8Kf6gN+DKMreIJCA==", - "license": "BSD-3-Clause" - }, - "node_modules/@xmldom/xmldom": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", - "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true, - "license": "MIT" - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bignumber.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", - "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/bmp-js": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", - "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/centra": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", - "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", - "dev": true - }, - "node_modules/earcut": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", - "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", - "license": "ISC" - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/exif-parser": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/free-tex-packer-core": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/free-tex-packer-core/-/free-tex-packer-core-0.3.5.tgz", - "integrity": "sha512-bRVByTevxdNsMJNBcqn7AcI28Tcy7Ji1KaLgPfYKJVS1WUGefEb6DrO9GzxAN+BTbJAsVD/A0ofdOGSMK7x7hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jvitela/mustache-wax": "^1.0.1", - "jimp": "^0.2.28", - "maxrects-packer": "2.7.3", - "mustache": "^2.3.0", - "tinify": "^1.5.0" - } - }, - "node_modules/free-tex-packer-core/node_modules/mustache": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", - "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", - "dev": true, - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - }, - "engines": { - "npm": ">=1.4.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/gifuct-js": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/gifuct-js/-/gifuct-js-2.1.2.tgz", - "integrity": "sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==", - "license": "MIT", - "dependencies": { - "js-binary-schema-parser": "^2.0.3" - } - }, - "node_modules/global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ip-regex": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", - "integrity": "sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ismobilejs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", - "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==", - "license": "MIT" - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jimp": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", - "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bignumber.js": "^2.1.0", - "bmp-js": "0.0.3", - "es6-promise": "^3.0.2", - "exif-parser": "^0.1.9", - "file-type": "^3.1.0", - "jpeg-js": "^0.2.0", - "load-bmfont": "^1.2.3", - "mime": "^1.3.4", - "mkdirp": "0.5.1", - "pixelmatch": "^4.0.0", - "pngjs": "^3.0.0", - "read-chunk": "^1.0.1", - "request": "^2.65.0", - "stream-to-buffer": "^0.1.0", - "tinycolor2": "^1.1.2", - "url-regex": "^3.0.0" - } - }, - "node_modules/jpeg-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", - "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/js-binary-schema-parser": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz", - "integrity": "sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==", - "license": "MIT" - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/load-bmfont": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", - "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-equal": "0.0.1", - "mime": "^1.3.4", - "parse-bmfont-ascii": "^1.0.3", - "parse-bmfont-binary": "^1.0.5", - "parse-bmfont-xml": "^1.1.4", - "phin": "^3.7.1", - "xhr": "^2.0.1", - "xtend": "^4.0.0" - } - }, - "node_modules/maxrects-packer": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/maxrects-packer/-/maxrects-packer-2.7.3.tgz", - "integrity": "sha512-bG6qXujJ1QgttZVIH4WDanhoJtvbud/xP/XPyf6A69C9RdA61BM4TomFALCq2nrTa+tARRIBB4LuIFsnUQU2wA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/min-document": { - "version": "2.19.2", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", - "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dom-walk": "^0.1.0" - } - }, - "node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "dev": true, - "license": "MIT", - "peer": true, - "bin": { - "mustache": "bin/mustache" - } - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/parse-bmfont-ascii": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", - "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse-bmfont-binary": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", - "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse-bmfont-xml": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", - "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.5.0" - } - }, - "node_modules/parse-headers": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", - "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse-svg-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", - "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", - "license": "MIT" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "license": "MIT" - }, - "node_modules/phin": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", - "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "dependencies": { - "centra": "^2.7.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pixelmatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", - "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", - "dev": true, - "license": "ISC", - "dependencies": { - "pngjs": "^3.0.0" - }, - "bin": { - "pixelmatch": "bin/pixelmatch" - } - }, - "node_modules/pixi.js": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.18.1.tgz", - "integrity": "sha512-6LUPWYgulZhp/w4kam2XHXB0QedISZIqrJbRdHLLQ3csn5a38uzKxAp6B5j6s89QFYaIJbg95kvgTRcbgpO1ow==", - "license": "MIT", - "workspaces": [ - "examples", - "playground" - ], - "dependencies": { - "@pixi/colord": "^2.9.6", - "@types/earcut": "^3.0.0", - "@webgpu/types": "^0.1.69", - "@xmldom/xmldom": "^0.8.12", - "earcut": "^3.0.2", - "eventemitter3": "^5.0.1", - "gifuct-js": "^2.1.2", - "ismobilejs": "^1.1.1", - "parse-svg-path": "^0.1.2", - "tiny-lru": "^11.4.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/pixijs" - } - }, - "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/promise-nodeify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/promise-nodeify/-/promise-nodeify-3.0.1.tgz", - "integrity": "sha512-ghsSuzZXJX8iO7WVec2z7GI+Xk/EyiD+JZK7AZKhUqYfpLa/Zs4ylUD+CwwnKlG6G3HnkUPMAi6PO7zeqGKssg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6", - "npm": ">=1.3.7" - } - }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", - "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/read-chunk": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", - "integrity": "sha512-5NLTTdX45dKFtG8CX5pKmvS9V5u9wBE+gkklN7xhDuhq3pA2I4O7ALfKxosCMcLHOhkxj6GNacZhfXtp5nlCdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rollup": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", - "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.4", - "@rollup/rollup-android-arm64": "4.60.4", - "@rollup/rollup-darwin-arm64": "4.60.4", - "@rollup/rollup-darwin-x64": "4.60.4", - "@rollup/rollup-freebsd-arm64": "4.60.4", - "@rollup/rollup-freebsd-x64": "4.60.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", - "@rollup/rollup-linux-arm-musleabihf": "4.60.4", - "@rollup/rollup-linux-arm64-gnu": "4.60.4", - "@rollup/rollup-linux-arm64-musl": "4.60.4", - "@rollup/rollup-linux-loong64-gnu": "4.60.4", - "@rollup/rollup-linux-loong64-musl": "4.60.4", - "@rollup/rollup-linux-ppc64-gnu": "4.60.4", - "@rollup/rollup-linux-ppc64-musl": "4.60.4", - "@rollup/rollup-linux-riscv64-gnu": "4.60.4", - "@rollup/rollup-linux-riscv64-musl": "4.60.4", - "@rollup/rollup-linux-s390x-gnu": "4.60.4", - "@rollup/rollup-linux-x64-gnu": "4.60.4", - "@rollup/rollup-linux-x64-musl": "4.60.4", - "@rollup/rollup-openbsd-x64": "4.60.4", - "@rollup/rollup-openharmony-arm64": "4.60.4", - "@rollup/rollup-win32-arm64-msvc": "4.60.4", - "@rollup/rollup-win32-ia32-msvc": "4.60.4", - "@rollup/rollup-win32-x64-gnu": "4.60.4", - "@rollup/rollup-win32-x64-msvc": "4.60.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stream-to": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", - "integrity": "sha512-Kg1BSDTwgGiVMtTCJNlo7kk/xzL33ZuZveEBRt6rXw+f1WLK/8kmz2NVCT/Qnv0JkV85JOHcLhD82mnXsR3kPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/stream-to-buffer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", - "integrity": "sha512-Da4WoKaZyu3nf+bIdIifh7IPkFjARBnBK+pYqn0EUJqksjV9afojjaCCHUemH30Jmu7T2qcKvlZm2ykN38uzaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "stream-to": "~0.2.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/tinify": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/tinify/-/tinify-1.8.2.tgz", - "integrity": "sha512-wkXLOj3/D2MdgWLI7TOXSbeD9xRu3GVxqt89ZWfrOyr8c4Ajd6gdowHY5ak7ASqMUu54+icHO/0BIiBg8pFIvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "https-proxy-agent": "^7.0.6", - "promise-nodeify": ">= 0.1" - }, - "engines": { - "node": ">= 14.0" - } - }, - "node_modules/tiny-lru": { - "version": "11.4.7", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.7.tgz", - "integrity": "sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/tinycolor2": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-regex": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", - "integrity": "sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ip-regex": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/xml-parse-from-string": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", - "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", - "dev": true, - "license": "MIT" - }, - "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/lobby-frontend/package.json b/lobby-frontend/package.json deleted file mode 100644 index df64adc..0000000 --- a/lobby-frontend/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "void-genesis-lobby-frontend", - "version": "1.0.0", - "description": "Frontend für die Master-Account-Lobby", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "dependencies": { - "pixi.js": "^8.0.4" - }, - "devDependencies": { - "free-tex-packer-core": "^0.3.5", - "vite": "^5.2.8" - } -} diff --git a/lobby-frontend/regeln.html b/lobby-frontend/regeln.html deleted file mode 100644 index 8e41f6f..0000000 --- a/lobby-frontend/regeln.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - Void-Genesis | Rechtliches - - - - - - - - - - \ No newline at end of file diff --git a/lobby-frontend/scripts/generate-spritesheet.js b/lobby-frontend/scripts/generate-spritesheet.js deleted file mode 100644 index b101222..0000000 --- a/lobby-frontend/scripts/generate-spritesheet.js +++ /dev/null @@ -1,98 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import texturePacker from 'free-tex-packer-core'; - -// ---------------------------------------------------------------------- -// ES-Modul Workaround für __dirname -// Da Vite standardmäßig ES-Module verwendet (type: "module" in package.json), -// müssen wir __dirname manuell nachbauen, um absolute Pfade zu generieren. -// ---------------------------------------------------------------------- -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// ---------------------------------------------------------------------- -// Pfad-Konfiguration -// ---------------------------------------------------------------------- -// Hier definieren wir, woher die rohen Bilder kommen und wohin der -// fertige Textur-Atlas gespeichert werden soll. -const RAW_ASSETS_DIR = path.resolve(__dirname, '../src/raw-assets/ui'); -const OUTPUT_DIR = path.resolve(__dirname, '../src/assets'); -const ATLAS_NAME = 'ui-atlas'; // Der Basisname für unsere Ausgabe (ui-atlas.png / ui-atlas.json) - -// ---------------------------------------------------------------------- -// Hauptfunktion zum Generieren des Sprite-Sheets -// ---------------------------------------------------------------------- -async function buildSpriteSheet() { - console.log(`[Packer] Lese Bilder aus: ${RAW_ASSETS_DIR}`); - - // Wir prüfen zunächst, ob der Quellordner existiert. Falls nicht, - // legen wir ihn an und brechen ab, da noch keine Bilder da sind. - if (!fs.existsSync(RAW_ASSETS_DIR)) { - fs.mkdirSync(RAW_ASSETS_DIR, { recursive: true }); - console.log('[Packer] Quellordner existierte nicht und wurde erstellt. Bitte füge Bilder hinzu und starte neu.'); - return; - } - - // Wir lesen alle Dateien im Quellordner aus. - const files = fs.readdirSync(RAW_ASSETS_DIR); - - // Wir filtern nach Bilddateien (z. B. PNG) und bereiten sie für den Packer vor. - const images = []; - for (const file of files) { - if (file.endsWith('.png')) { - const filePath = path.join(RAW_ASSETS_DIR, file); - // Der Packer benötigt den Dateipfad als "path" und den Dateiinhalt als "contents" - images.push({ - path: file, - contents: fs.readFileSync(filePath) - }); - } - } - - if (images.length === 0) { - console.log('[Packer] Keine PNG-Bilder im Quellordner gefunden. Abbruch.'); - return; - } - - // ---------------------------------------------------------------------- - // Packer-Konfiguration - // ---------------------------------------------------------------------- - const options = { - textureName: ATLAS_NAME, - width: 4096, // Maximale Breite des Atlases - height: 4096, // Maximale Höhe des Atlases - padding: 2, // Pixel-Abstand zwischen den Bildern (verhindert Rand-Bluten) - allowRotation: false, // Für UI-Elemente oft besser deaktiviert, damit sie in PixiJS nicht gedreht geladen werden - exporter: 'Pixi', // Generiert direkt ein Format, das PixiJS nativ lesen kann - removeFileExtension: true // Entfernt das ".png" in den JSON-Keys (leichter in PixiJS aufzurufen) - }; - - console.log(`[Packer] Verpacke ${images.length} Bilder...`); - - // Wir rufen den Packer asynchron auf - texturePacker(images, options, (files, error) => { - if (error) { - console.error('[Packer] Fehler beim Erstellen des Sprite-Sheets:', error); - return; - } - - // Zielordner erstellen, falls er nicht existiert - if (!fs.existsSync(OUTPUT_DIR)) { - fs.mkdirSync(OUTPUT_DIR, { recursive: true }); - } - - // Der Packer gibt ein Array von Dateien zurück (Bild + JSON). - // Wir speichern diese direkt im Zielordner. - for (const item of files) { - const outPath = path.join(OUTPUT_DIR, item.name); - fs.writeFileSync(outPath, item.buffer); - console.log(`[Packer] Erfolgreich gespeichert: ${outPath}`); - } - - console.log('[Packer] Sprite-Sheet erfolgreich erstellt!'); - }); -} - -// Skript ausführen -buildSpriteSheet(); \ No newline at end of file diff --git a/lobby-frontend/src/api/auth.js b/lobby-frontend/src/api/auth.js deleted file mode 100644 index f4e1125..0000000 --- a/lobby-frontend/src/api/auth.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Die Basis-URL für alle Authentifizierungs-Anfragen. - * Zentralisiert, damit wir sie bei Server-Umzügen nur an einer Stelle ändern müssen. - */ -const API_BASE_URL = 'https://api-dev.void-genesis.de/api/auth'; - -/** - * Führt den Login-Request an das Backend aus. - * - * @param {string} identifier - Benutzername oder E-Mail-Addresse - * @param {string} password - Das eingegebene Klartext-Passwort - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function loginUser(identifier, password) { - try { - const response = await fetch(`${API_BASE_URL}/login`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ identifier, password }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler beim Login:", error); - throw error; - } -} - -/** - * Führt den Registrierungs-Request an das Backend aus. - * - * @param {string} username - Der gewünschete Benutzername - * @param {string} email - Die E-Mail-Addresse - * @param {string} password - Das Klartext-Passwort - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function registerUser(username, email, password) { - try { - const response = await fetch(`${API_BASE_URL}/register`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, email, password }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler bei der Registrierung:", error); - throw error; - } -} - -/** - * Verifiziert die E-Mail-Addresse mit dem 6-stelligen OTP Code. - * - * @param {string} email - Die E-Mail-Addresse des Nutzers - * @param {string} otpCode - Der eingegebende 6-stellige Code aus der E-Mail - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function verifyEmailUser(email, otpCode) { - try { - const response = await fetch(`${API_BASE_URL}/verify-email`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, otpCode }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler bei der Verifizierung:", error); - throw error; - } -} - -/** - * Fordert einen neuen OTP-Code an, falls der alte abgelaufen ist (Resend-Funktion) - * - * @param {string} email - Die E-Mail-Addresse des Nutzers - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function resendOTPCode(email) { - try { - console.log ("Email:", email); - const response = await fetch(`${API_BASE_URL}/resend-otp`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler beim erneutem Senden des OTP:", error); - throw error; - } -} - -/** - * Leitet den Prozess zum Zurücksetzen des Passworts ein (sendet die Email mit Code). - * - * @param {string} email - Die E-Mail-Addresse des Nutzers - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function requestPasswordResetUser(email) { - try { - const response = await fetch(`${API_BASE_URL}/forgot-password`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler bei, Passwort-Reset-Anfrage:", error); - throw error; - } -} - -/** - * Setzt das Passwort final zurück. - * - * @param {string} email - Die E-Mail-Addresse des Nutzers - * @param {string} otpCode - Der 6-stellige Code aus der Reset-E-Mail - * @param {string} newPassword - Das neu gewählte Passwort - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function resetPasswordUser(email, otpCode, newPassword) { - try { - const response = await fetch(`${API_BASE_URL}/reset-password`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, otpCode, newPassword }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler bei, Passwort-Reset:", error); - throw error; - } -} - -/** - * Nutzt das Refresh-Token, um ein neues Access-Token anzufordern (Auto-Login). - * - * @param {string} refreshToken - * - * @returns {Promise} - */ -export async function refreshAccessTokenUser(refreshToken) { - try { - const response = await fetch(`${API_BASE_URL}/refresh`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ refreshToken }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler beim Token-Refresh:", error); - throw error; - } -} diff --git a/lobby-frontend/src/api/sectors.js b/lobby-frontend/src/api/sectors.js deleted file mode 100644 index 0cfa6b9..0000000 --- a/lobby-frontend/src/api/sectors.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Die Basis-URL für alle Sektor-spezifischen Anfragen an das Backend. - * Auch hier zentralisiert, um zukünftige Server-Umzüge zu erleichtern. - */ -const API_BASE_URL = 'https://api-dev.void-genesis.de/api/sectors'; - -/** - * Ruft die Liste aller verfügbaren Sektoren vom Backend ab. - * Da diese Route geschützt ist, müssen wir das Access-Token mitsenden. - * - * @param {string} token - Das JWT Access Token des aktuell eingeloggten Nutzers - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function fetchSectorsList(token) { - try { - const response = await fetch(`${API_BASE_URL}/list`, { - method: 'GET', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler beim Sektor-Abruf:", error); - throw error; - } -} - -/** - * Sendet die Anfrage an das Backend, um einem neuen Sektor beizutreten. - * - * @param {string} token - Das JWT Access Token - * @param {string} sectorId - Die UUID des Sektors - * @param {string} localUsername - Der gewünschte Commander-Name - * @param {string} gender - Das gewählte Geschlecht ('male' oder 'female') - * - * @returns {Promise} - Ein Objekt mit 'ok' (boolean) und den 'data' (JSON) - */ -export async function joinSectorUser(token, sectorId, localUsername, gender) { - try { - const response = await fetch(`${API_BASE_URL}/join`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ sectorId, localUsername, gender }) - }); - - const data = await response.json(); - return { ok: response.ok, data }; - - } catch (error) { - console.error("Netzwerkfehler beim Sektor-Beitritt:", error); - throw error; - } -} \ No newline at end of file diff --git a/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf b/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf deleted file mode 100644 index e1721ae..0000000 Binary files a/lobby-frontend/src/assets/fonts/Bungee-Regular.ttf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf b/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf deleted file mode 100644 index 4bd82f9..0000000 Binary files a/lobby-frontend/src/assets/fonts/Exo2.0-Regular.otf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf b/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf deleted file mode 100644 index 9df61a5..0000000 Binary files a/lobby-frontend/src/assets/fonts/Exo2.0-SemiBold.otf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf b/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf deleted file mode 100644 index b500dbf..0000000 Binary files a/lobby-frontend/src/assets/fonts/Gugi-Regular.ttf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf b/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf deleted file mode 100644 index 4157958..0000000 Binary files a/lobby-frontend/src/assets/fonts/Quantico-Bold.ttf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf b/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf deleted file mode 100644 index ab7340b..0000000 Binary files a/lobby-frontend/src/assets/fonts/WDXLLubrifontSC-Regular.ttf and /dev/null differ diff --git a/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf b/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf deleted file mode 100644 index aa4654d..0000000 Binary files a/lobby-frontend/src/assets/fonts/ZenDots-Regular.ttf and /dev/null differ diff --git a/lobby-frontend/src/assets/ui-atlas.json b/lobby-frontend/src/assets/ui-atlas.json deleted file mode 100644 index cc7554d..0000000 --- a/lobby-frontend/src/assets/ui-atlas.json +++ /dev/null @@ -1,591 +0,0 @@ -{ - "frames": { - "SPR_SciFiMenus_Frame_Box_Large_11": { - "frame": { - "x": 2, - "y": 2, - "w": 998, - "h": 494 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 13, - "y": 11, - "w": 998, - "h": 494 - }, - "sourceSize": { - "w": 1024, - "h": 512 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Frame_Box_Large_02": { - "frame": { - "x": 1004, - "y": 2, - "w": 997, - "h": 984 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 14, - "y": 21, - "w": 997, - "h": 984 - }, - "sourceSize": { - "w": 1024, - "h": 1024 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Frame_Box_Large_12": { - "frame": { - "x": 2, - "y": 500, - "w": 984, - "h": 481 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 20, - "y": 17, - "w": 984, - "h": 481 - }, - "sourceSize": { - "w": 1024, - "h": 512 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Frame_Box_Large_11_Background": { - "frame": { - "x": 2, - "y": 985, - "w": 919, - "h": 427 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 52, - "y": 34, - "w": 919, - "h": 427 - }, - "sourceSize": { - "w": 1024, - "h": 512 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Frame_Box_Large_12_Background": { - "frame": { - "x": 2005, - "y": 2, - "w": 898, - "h": 393 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 64, - "y": 73, - "w": 898, - "h": 393 - }, - "sourceSize": { - "w": 1024, - "h": 512 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Ring_Medium_13": { - "frame": { - "x": 2907, - "y": 2, - "w": 826, - "h": 853 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 97, - "y": 85, - "w": 826, - "h": 853 - }, - "sourceSize": { - "w": 1024, - "h": 1024 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Frame_Box_Large_02_Background": { - "frame": { - "x": 2005, - "y": 399, - "w": 825, - "h": 895 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 98, - "y": 63, - "w": 825, - "h": 895 - }, - "sourceSize": { - "w": 1024, - "h": 1024 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Background_01": { - "frame": { - "x": 3737, - "y": 2, - "w": 256, - "h": 256 - }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 256, - "h": 256 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Menu_Item_Background_05": { - "frame": { - "x": 3737, - "y": 262, - "w": 256, - "h": 256 - }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 256, - "h": 256 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Menu_Item_Selected_20": { - "frame": { - "x": 3737, - "y": 522, - "w": 256, - "h": 256 - }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 256, - "h": 256 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Menu_Item_Selected_10": { - "frame": { - "x": 3737, - "y": 782, - "w": 228, - "h": 234 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 14, - "y": 10, - "w": 228, - "h": 234 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Solo_01_Clean": { - "frame": { - "x": 3969, - "y": 782, - "w": 125, - "h": 194 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 65, - "y": 31, - "w": 125, - "h": 194 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Background_Hexagons_01": { - "frame": { - "x": 2834, - "y": 859, - "w": 512, - "h": 512 - }, - "rotated": false, - "trimmed": false, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 512, - "h": 512 - }, - "sourceSize": { - "w": 512, - "h": 512 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Menu_Item_Selected_19": { - "frame": { - "x": 3350, - "y": 859, - "w": 228, - "h": 234 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 14, - "y": 10, - "w": 228, - "h": 234 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Lock_Closed_01_Clean": { - "frame": { - "x": 3582, - "y": 859, - "w": 132, - "h": 164 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 62, - "y": 45, - "w": 132, - "h": 164 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_Social_Discord": { - "frame": { - "x": 3718, - "y": 1020, - "w": 226, - "h": 175 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 14, - "y": 41, - "w": 226, - "h": 175 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFi_Map_Message": { - "frame": { - "x": 3582, - "y": 1027, - "w": 130, - "h": 88 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 64, - "y": 84, - "w": 130, - "h": 88 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "SPR_SciFiMenus_Menu_Item_Background_07": { - "frame": { - "x": 3350, - "y": 1097, - "w": 224, - "h": 230 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 16, - "y": 12, - "w": 224, - "h": 230 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Warning_02_Clean": { - "frame": { - "x": 3578, - "y": 1199, - "w": 222, - "h": 193 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 17, - "y": 31, - "w": 222, - "h": 193 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Multiplayer_01_Clean": { - "frame": { - "x": 3350, - "y": 1331, - "w": 218, - "h": 141 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 19, - "y": 57, - "w": 218, - "h": 141 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Planet_02_Clean": { - "frame": { - "x": 3804, - "y": 1199, - "w": 216, - "h": 149 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 20, - "y": 54, - "w": 216, - "h": 149 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Tick_01_Clean": { - "frame": { - "x": 3804, - "y": 1352, - "w": 191, - "h": 144 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 32, - "y": 56, - "w": 191, - "h": 144 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Settings_01_Clean": { - "frame": { - "x": 3572, - "y": 1396, - "w": 182, - "h": 199 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 37, - "y": 28, - "w": 182, - "h": 199 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - }, - "ICON_SciFiMenus_Menu_Message_01_Clean": { - "frame": { - "x": 3758, - "y": 1500, - "w": 177, - "h": 218 - }, - "rotated": false, - "trimmed": true, - "spriteSourceSize": { - "x": 39, - "y": 17, - "w": 177, - "h": 218 - }, - "sourceSize": { - "w": 256, - "h": 256 - }, - "pivot": { - "x": 0.5, - "y": 0.5 - } - } - }, - "meta": { - "app": "http://github.com/odrick/free-tex-packer-core", - "version": "0.3.5", - "image": "ui-atlas.png", - "format": "RGBA8888", - "size": { - "w": 4096, - "h": 1720 - }, - "scale": 1 - } -} \ No newline at end of file diff --git a/lobby-frontend/src/assets/ui-atlas.png b/lobby-frontend/src/assets/ui-atlas.png deleted file mode 100644 index e6cb62d..0000000 Binary files a/lobby-frontend/src/assets/ui-atlas.png and /dev/null differ diff --git a/lobby-frontend/src/core/pixi-app.js b/lobby-frontend/src/core/pixi-app.js deleted file mode 100644 index 8217048..0000000 --- a/lobby-frontend/src/core/pixi-app.js +++ /dev/null @@ -1,170 +0,0 @@ -import { Application } from "pixi.js"; - -export const app = new Application(); - -let manuelResizeCallback = null; - -/** - * Startet die PixiJS-Engine und hängt das Canvas in das HTML-Dokument ein. - * - * @param {string} containerId - Die HTML-ID des divs, in das PixiJS gerendert werden soll. - */ -export async function initPixi(containerId) { - await app.init({ - resizeTo: window, - backgroundAlpha: 0, - antialias: true, - resolution: window.devicePixelRatio || 1, - autoDensity: true - }); - document.getElementById(containerId).appendChild(app.canvas); -} - -/** - * Übernimmt die gesammte responsive Skalierungslogik für unsere PixiJS-Element. - * Wir übergeben hier alle Container, die flexible auf Fenstergrößen reagieren müssen. - * - * @param {Object} refs - Ein Paket mit allen UI-Containern. - */ -export function setupResize(refs) { - const { - background, discordContainer, logoContainer, uiContainer, - panelFrame, lobbyPixiContainer, footerBackground, footerPattern, - footerBackgroundContainer, onboardingPixiContainer - } = refs; - - function resize() { - const screenWidth = window.innerWidth; - const screenHeight = window.innerHeight; - - if (background && background.texture) { - const scaleX = screenWidth / background.texture.width; - const scaleY = screenHeight / background.texture.height; - const scale = Math.max(scaleX, scaleY); - background.scale.set(scale); - background.x = (screenWidth - background.width) / 2; - background.y = (screenHeight - background.height) / 2; - } - - let panelScale = 1.0; - let panelOffsetY = 0; - - if (screenHeight <= 500){ - panelScale = 0.4; - panelOffsetY = 0; - discordContainer.position.set(-420, 350); - logoContainer.position.set(-300, 150); - logoContainer.scale.set(1.3); - } else if (screenWidth <= 600) { - panelScale = 0.7; - panelOffsetY = 0; - discordContainer.position.set(125, 520); - logoContainer.position.set(250, -120); - logoContainer.scale.set(0.7); - } else { - panelScale = 1.0; - panelOffsetY = 0; - discordContainer.position.set(640, 380); - logoContainer.position.set(250, -120); - logoContainer.scale.set(1.0); - - if (screenHeight <= 1050) { - panelOffsetY = 80; - } - } - - uiContainer.scale.set(panelScale); - uiContainer.x = (screenWidth - (panelFrame.width * panelScale)) / 2; - uiContainer.y = ((screenHeight - (panelFrame.height * panelScale)) / 2) + panelOffsetY; - - const isMobilePortrait = screenWidth <= 800 && screenHeight > screenWidth; - const isLandscapeMobile = screenHeight <= 500; - - const targetPanelWidth = isMobilePortrait ? 650 : 1240; - - const htmlFooter = document.getElementById('game-footer'); - const footerHeight = (htmlFooter && htmlFooter.style.display !== 'none') ? htmlFooter.offsetHeight : 0; - const actualFooterHeight = Math.max(footerHeight, isLandscapeMobile ? 30 : 40); - - const visualWidth = screenWidth - (isMobilePortrait ? 20 : 60); - const visualHeight = screenHeight - actualFooterHeight - 80; - - const lobbyScale = Math.min(1.0, visualWidth / targetPanelWidth); - - let targetPanelHeight = visualHeight / lobbyScale; - targetPanelHeight = Math.max(350, targetPanelHeight); - - if (!isMobilePortrait && !isLandscapeMobile) { - targetPanelHeight = Math.min(800, targetPanelHeight); - } - - let targetMaskHeight = targetPanelHeight - 110; - - let lobbyOffsetY = 0; - if (isMobilePortrait) { - lobbyOffsetY = -25; - } - - if (lobbyPixiContainer.bgSprite && lobbyPixiContainer.scrollMask) { - lobbyPixiContainer.bgSprite.width = targetPanelWidth; - lobbyPixiContainer.bgSprite.height = targetPanelHeight; - lobbyPixiContainer.scrollMask.clear(); - lobbyPixiContainer.scrollMask.rect(30, 80, targetPanelWidth - 60, targetMaskHeight); - lobbyPixiContainer.scrollMask.fill(0xffffff); - } - - lobbyPixiContainer.scale.set(lobbyScale); - lobbyPixiContainer.x = (screenWidth - (targetPanelWidth * lobbyScale)) / 2; - lobbyPixiContainer.y = ((screenHeight - (targetPanelHeight * lobbyScale)) / 2) + lobbyOffsetY; - - const sectorHtmlLayer = document.getElementById('sector-browser-layer'); - if (sectorHtmlLayer) { - sectorHtmlLayer.style.width = `${targetPanelWidth}px`; - sectorHtmlLayer.style.height = `${targetPanelHeight}px`; - - sectorHtmlLayer.style.left = `${lobbyPixiContainer.x}px`; - sectorHtmlLayer.style.top = `${lobbyPixiContainer.y}px`; - sectorHtmlLayer.style.transformOrigin = '0 0'; - sectorHtmlLayer.style.transform = `scale(${lobbyScale})`; - } - - const sectorListContainer = document.getElementById('sector-list-container'); - if (sectorListContainer) { - sectorListContainer.style.height = `${targetMaskHeight}px`; - } - - if (onboardingPixiContainer) { - onboardingPixiContainer.x = (screenWidth - 450) / 2; - onboardingPixiContainer.y = (screenHeight - 550) / 2; - } - - footerBackground.height = actualFooterHeight; - footerPattern.height = actualFooterHeight; - footerBackground.width = screenWidth; - footerPattern.width = screenWidth; - footerBackgroundContainer.y = screenHeight - footerHeight; - } - - app.renderer.on('resize', resize); - manuelResizeCallback = resize; - resize(); -} - -/** - * Löst eine manuelle Neuberechnung des Layout aus (wichtig nach dem Wechsel zur Lobby) - */ -export function triggerResize() { - if (manuelResizeCallback) { - manuelResizeCallback(); - } -} - -/** - * Wenn der Nutzer das Gerät dreht, laden wir die Seite nach einer kurzen Vertögerung neu - * um sicherzustellen dass HTML und Canvas perfekr synchronisieren. - */ -window.addEventListener('orientationchange', () => { - setTimeout(() => { - window.location.reload(); - }, 300); // 300ms warten, bis der Browser die neuen Maße gerendert hat -}); \ No newline at end of file diff --git a/lobby-frontend/src/css/auth.css b/lobby-frontend/src/css/auth.css deleted file mode 100644 index 9ef1651..0000000 --- a/lobby-frontend/src/css/auth.css +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Hier liegen alle Styles für den Anmelde- und Registrierungsprozess. - * Das umfasst das Formular, die eingabefelder, buttons, Checkboxen und die Hilfslinks - */ - -#register-form { - pointer-events: auto; - width: 500px; - height: 500px; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); -} - -.input-group label { - display: none; -} - -.input-group input { - position: absolute; - width: 300px; - height: 50px; - left: 115px; - box-sizing: border-box; - padding: 10px 15px; - background: transparent; - border: none; - outline: none; - color: var(--color-text-main); - font-size: 16px; - font-family: inherit; - transition: text-shadow 0.2s ease; -} - -#username { top: 135px; } -#email { top: 200px; } -#password { top: 265px; } - -.input-group input::placeholder { - color: var(--color-text-placeholder); -} - -.input-group input:focus { - text-shadow: 0 0 8px var(--color-cyan-dark); - background: transparent; - box-shadow: none; -} - -#btn-register { - position: absolute; - width: 370px; - height: 55px; - left: 65px; - top: 390px; - background: transparent; - border: none; - color: var(--color-text-main); - font-size: 18px; - font-weight: bold; - font-family: inherit; - letter-spacing: 2px; - cursor: pointer; - transition: all 0.2s ease; -} - -#btn-register:hover { - background: transparent; - color: var(--color-text-main); - text-shadow: 0 0 15px var(--color-text-main), 0 0 30px var(--color-green-bright); -} - -.tab-btn { - position: absolute; - height: 40px; - width: 180px; - top: 60px; - background: transparent; - border: none; - color: var(--color-text-inactive); - font-size: 14px; - font-family: inherit; - text-transform: uppercase; - cursor: pointer; - outline: none; - transition: color 0.2s ease; -} - -#tab-login { left: 65px; } -#tab-register { left: 255px; } - -.tab-btn.active { - color: var(--color-text-main); - text-shadow: 0 0 8px var(--color-cyan-bright); -} - -.tab-btn:hover:not(.active) { - color: var(--color-text-main); -} - -.checkbox-group { - position: absolute; - left: 115px; - display: flex; - align-items: center; - gap: 10px; -} - -#group-remember { top: 300px; } -#group-agb { top: 325px; } - -.checkbox-group label { - color: var(--color-text-main); - font-size: 14px; - cursor: pointer; -} - -.checkbox-group a { - color: var(--color-cyan-bright); - text-decoration: none; /* Unterstrich entfernen für einen cleaneren Look */ - transition: text-shadow 0.2s ease, color 0.2s ease; -} - -.checkbox-group a:hover { - color: var(--color-text-main); - text-shadow: 0 0 8px var(--color-cyan-bright); -} - -.checkbox-group input[type="checkbox"] { - appearance: none; - width: 18px; - height: 18px; - border: 1px solid var(--color-cyan-dark); - background: var(--color-checkbox-bg); - cursor: pointer; - position: relative; -} - -.checkbox-group input[type="checkbox"]:checked::after { - content: '✔'; - position: absolute; - color: var(--color-cyan-bright); - font-size: 14px; - top: -2px; - left: 2px; -} - -#otp-instruction { - display: none; - position: absolute; - top: 120px; - left: 65px; - width: 370px; - text-align: center; - color: var(--color-text-main); - font-size: 15px; - margin: 0; -} - -#msg-box { - position: absolute; - top: 350px; - left: 65px; - width: 370px; - height: 30px; - color: var(--color-error); - font-size: 14px; - text-align: center; - font-weight: bold; - display: flex; - align-items: center; - justify-content: center; -} - -#resend-otp-link { - position: absolute; - top: 250px; - left: 65px; - width: 370px; - text-align: center; - color: var(--color-text-inactive); - font-size: 14px; - text-decoration: underline; - cursor: pointer; - pointer-events: auto; - transition: color 0.2s ease, text-shadow 0.2s ease; -} - -#resend-otp-link:hover, #forgot-password-link:hover, #back-link:hover { - color: var(--color-cyan-bright); - text-shadow: 0 0 8px var(--color-cyan-bright); -} - -#forgot-password-link { - position: absolute; - top: 255px; - left: 120px; - color: var(--color-text-inactive); - font-size: 14px; - text-decoration: underline; - cursor: pointer; - pointer-events: auto; - transition: color 0.2s ease, text-shadow 0.2s ease; -} - -#discord-link { - position: absolute; - left: 640px; - top: 380px; - width: 280px; - height: 85px; - background: transparent; - text-decoration: none; - cursor: pointer; - pointer-events: auto; -} - -#back-link { - position: absolute; - top: 70px; - left: 65px; - color: var(--color-text-inactive); - font-size: 15px; - text-decoration: none; - cursor: pointer; - pointer-events: auto; - transition: color 0.2s ease, text-shadow 0.2s ease; -} - - - -#dev-warning-banner { - position: absolute; - top: 35px; /* Leichter Abstand zum oberen Bildschirmrand */ - left: 50%; - transform: translateX(-50%); /* Zentriert das Banner horizontal */ - - /* Optik: Roter, halbtransparenter Hintergrund mit rotem Rahmen */ - background: rgba(255, 68, 68, 0.3); - border: 1px solid var(--color-error); - box-shadow: 0 0 15px rgba(255, 68, 68, 0.8); /* Sanfter roter Glow */ - - color: var(--color-text-main); - padding: 12px 25px; - font-size: 14px; - font-family: var(--font-main); - text-align: center; - letter-spacing: 1px; - - /* Z-Index hoch ansetzen, damit es garantiert über allem anderen liegt */ - z-index: 100; - pointer-events: auto; /* Erlaubt das Markieren des Textes mit der Maus */ -} - -/* ---------------------------------------------------------------------- - * Hervorhebung des Warn-Icons innerhalb des Banners - * ---------------------------------------------------------------------- */ -#dev-warning-banner .warning-icon { - color: var(--color-error); - font-size: 16px; - margin-right: 5px; - text-shadow: 0 0 8px var(--color-error); -} - -#dev-warning-banner strong { - color: var(--color-error); -} \ No newline at end of file diff --git a/lobby-frontend/src/css/layout.css b/lobby-frontend/src/css/layout.css deleted file mode 100644 index 6c37cc5..0000000 --- a/lobby-frontend/src/css/layout.css +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Beinhaltet das absolute Grundgerüst der Seite. - * Reset-Regeln uns die Positionierung der Haupt-Container (Z-Index Ebenen). - */ - -body, html { - margin: 0; - padding: 0; - width: 100%; - height: 100%; - overflow: hidden; - background-color: var(--color-bg-page); - font-family: var(--font-main); - -webkit-text-size-adjust: 100%; - text-size-adjust: 100%; - position: fixed; - overscroll-behavior: none;} - -#game-wrapper { - position: relative; - width: 100%; - height: 100%; -} - -#pixi-container { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; -} - -#ui-layer { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10; - pointer-events: none; -} diff --git a/lobby-frontend/src/css/lobby.css b/lobby-frontend/src/css/lobby.css deleted file mode 100644 index 8c929e7..0000000 --- a/lobby-frontend/src/css/lobby.css +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Hier liegen alle Styles für die Lobb-Ansicht (nach dem Login) - * Das umfasst die Top-Bar, Dropdowns, der Footer und die Sektor-Karten - */ - - #lobby-ui { - position: absolute; - top: 0; left: 0; - width: 100%; height: 100%; - pointer-events: none; - z-index: 15; -} - -#top-bar { - width: 100%; height: 50px; - background: transparent; - border-bottom: none; - box-shadow: none; - display: flex; justify-content: space-between; align-items: center; - padding: 0 20px; box-sizing: border-box; - pointer-events: auto; -} - -.top-bar-right { - display: flex; align-items: center; gap: 20px; -} - -.custom-dropdown { - position: relative; - font-family: var(--font-main); - color: var(--color-text-main); -} - -.dropdown-toggle { - display: flex; align-items: center; gap: 8px; - cursor: pointer; - padding: 5px 10px; - border: 1px solid transparent; - transition: all 0.2s ease; - user-select: none; -} - -.dropdown-toggle:hover { - border: 1px solid var(--color-cyan-dark); - background: rgba(0, 240, 255, 0.05); -} - -.dropdown-arrow { - font-size: 10px; - color: var(--color-cyan-dark); -} - -.flag-icon { font-size: 16px; } - -.dropdown-menu { - position: absolute; - top: 100%; - right: 0; - margin-top: 10px; - background: rgba(11, 25, 44, 0.95); - border: 1px solid var(--color-cyan-dark); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); - list-style: none; - padding: 5px 0; - min-width: 150px; - opacity: 0; - pointer-events: none; - transform: translateY(-10px); - transition: all 0.2s ease; -} - -.dropdown-menu.show { - opacity: 1; - pointer-events: auto; - transform: translateY(0); -} - -.user-menu { min-width: 220px; } - -.dropdown-item { - padding: 10px 15px; - cursor: pointer; - display: flex; - align-items: center; - gap: 10px; - font-size: 14px; - transition: background 0.2s ease, color 0.2s ease; -} - -.dropdown-item:hover:not(.disabled) { - background: rgba(0, 240, 255, 0.1); - color: var(--color-cyan-bright); -} - -.dropdown-item.disabled, .dropdown-header.disabled { - color: var(--color-text-inactive); - cursor: not-allowed; -} - -.dropdown-header { - padding: 10px 15px; - font-size: 14px; - line-height: 1.4; -} - -.premium-text { - color: var(--color-cyan-bright); - font-weight: bold; -} - -.dropdown-divider { - height: 1px; - background: var(--color-cyan-dark); - margin: 5px 0; - opacity: 0.5; -} - -.logout-item { - color: var(--color-error); - font-weight: bold; -} - -.logout-item:hover { - background: rgba(255, 68, 68, 0.1) !important; - color: var(--color-error) !important; -} - -#game-footer { - position: absolute; bottom: 0; left: 0; width: 100%; - height: 40px; - z-index: 20; - pointer-events: none; - display: flex; justify-content: space-between; align-items: center; - padding: 0 40px; box-sizing: border-box; - color: var(--color-text-inactive); - font-size: 12px; - font-family: inherit; - border-top: 1px solid var(--color-text-inactive); - background: transparent; -} - -.footer-left { flex: 1; text-align: left; } -.footer-center { flex: 1; text-align: center; } -.footer-right { flex: 1; text-align: right; } - -#game-footer a { - color: var(--color-text-inactive); text-decoration: none; - transition: color 0.2s ease, text-shadow 0.2s ease; - pointer-events: auto; -} - -#game-footer a:hover { - color: var(--color-cyan-bright); - text-shadow: 0 0 8px var(--color-cyan-bright); -} - -#sector-browser-layer { - position: absolute; - width: 1240px; - height: 650px; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - pointer-events: auto; - transition: opacity 0.3s ease; -} -#sector-tab-container { - position: absolute; - top: 20px; - width: 100%; - height: 40px; - display: flex; - justify-content: center; - gap: 20px; -} - -#sector-tab-container .tab-btn { - position: relative; - left: 0 !important; - top: 0 !important; -} - -#tab-my-sectors { left: 45px; } -#tab-new-sectors { left: 235px; } - -#sector-list-container { - position: absolute; - top: 80px; - width: 100%; - height: 540px; - overflow-y: auto; - overflow-x: hidden; - padding: 20px 40px; /* Zurück zu den perfekten 40px */ - box-sizing: border-box; - - /* Zurück zum Grid für eine saubere Matrix-Optik */ - display: grid; - grid-template-columns: repeat(4, 270px); - grid-auto-rows: 90px; - row-gap: 20px; - column-gap: 25px; - align-content: start; - - /* Zentriert den gesamten Grid-Block (inkl. Lücken) mittig im Container */ - justify-content: center; -} - -.sector-card-link { - display: block; - width: 270px; - height: 90px; - text-decoration: none; - color: var(--color-text-main); - position: relative; - cursor: pointer; - box-sizing: border-box; -} - -.sector-card-content { - position: relative; - width: 100%; - height: 100%; - padding: 15px 25px; - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: center; -} - -.sector-row-top { - display: flex; - align-items: center; - gap: 15px; - font-size: 18px; - font-weight: bold; - letter-spacing: 1px; -} - -.sector-details-icon { - font-size: 14px; - color: var(--color-text-inactive); - transition: transform 0.2s ease, color 0.2s ease; -} - -.sector-card-link:hover .sector-details-icon { - color: var(--color-cyan-bright); - transform: scale(1.2) rotate(10deg); -} - -.sector-status { - margin-left: auto; - font-size: 14px; - text-transform: uppercase; - letter-spacing: 1px; -} - -.status-online { - color: var(--color-green-bright); - text-shadow: 0 0 8px rgba(0, 255, 0, 0.6); -} - -.status-maintenance { - color: var(--color-error); - text-shadow: 0 0 8px rgba(255, 68, 68, 0.6); -} - -.sector-row-mid { - display: flex; - gap: 30px; - font-size: 13px; - color: var(--color-text-placeholder); - margin-top: 5px; -} - -.sector-row-bottom { - font-size: 12px; - color: var(--color-cyan-bright); - margin-top: 4px; - font-style: italic; -} - -#sector-list-container::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -#sector-list-container::-webkit-scrollbar-track { - background: rgba(11, 25, 44, 0.3); -} - -#sector-list-container::-webkit-scrollbar-thumb { - background: var(--color-cyan-dark); - border-radius: 3px; -} \ No newline at end of file diff --git a/lobby-frontend/src/css/onboarding.css b/lobby-frontend/src/css/onboarding.css deleted file mode 100644 index 6b56c08..0000000 --- a/lobby-frontend/src/css/onboarding.css +++ /dev/null @@ -1,108 +0,0 @@ -/* Haupt-Ebene abdunklung entfernen, das macht jetzt PixiJS */ -#sector-onboarding-layer { - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - z-index: 50; - display: flex; - justify-content: center; - align-items: center; - pointer-events: none; -} - -#onboarding-form { - position: relative; - width: 450px; - height: 550px; - background: transparent; - pointer-events: auto; -} - -#local-username { - position: absolute; - left: 75px; - top: 250px; - width: 300px; - height: 50px; - background: transparent; - border: none; - color: var(--color-text-main); - padding: 0 15px; - font-size: 16px; - font-family: inherit; - outline: none; - box-sizing: border-box; -} - -#local-username::placeholder { - color: var(--color-text-placeholder); -} -#local-username:focus { - text-shadow: 0 0 8px var(--color-cyan-dark); -} - -#btn-gender-male { - position: absolute; - left: 75px; - top: 320px; - width: 140px; - height: 50px; -} - -#btn-gender-female { - position: absolute; - left: 235px; - top: 320px; - width: 140px; - height: 50px; -} - -#btn-cancel-onboarding { - position: absolute; - left: 75px; - top: 460px; - width: 140px; - height: 50px; -} - -#btn-submit-onboarding { - position: absolute; - left: 235px; - top: 460px; - width: 140px; - height: 50px; -} - -#onboarding-form button { - background: transparent; - border: none; - color: var(--color-text-inactive); - font-size: 14px; - font-family: inherit; - text-transform: uppercase; - cursor: pointer; - outline: none; - transition: color 0.2s ease, text-shadow 0.2s ease; -} - -#onboarding-form button:hover, -#onboarding-form button.active { - color: var(--color-text-main); - text-shadow: 0 0 8px var(--color-cyan-bright); -} - -#onboarding-msg-box { - position: absolute; - top: 400px; /* Zwischen Geschlecht und Buttons */ - width: 100%; - text-align: center; - color: var(--color-error); - font-size: 14px; - font-weight: bold; - display: flex; - justify-content: center; - align-items: center; - height: 30px; -} \ No newline at end of file diff --git a/lobby-frontend/src/css/variables.css b/lobby-frontend/src/css/variables.css deleted file mode 100644 index cf70052..0000000 --- a/lobby-frontend/src/css/variables.css +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Hier definieren wir auschließlich globale Variable und laden Schriftarten. - */ - -@font-face { - font-family: 'UI Font'; - /* .otf Dateien bekommen das Format 'opentype', .ttf Dateien 'truetype' */ - src: url('../assets/fonts/WDXLLubrifontSC-Regular.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'Logo Font'; - src: url('../assets/fonts/Quantico-Bold.ttf') format('truetype'); - font-weight: normal; - font-style: normal; -} - -:root { - --color-bg-page: #000000; - --color-text-main: #ffffff; - --color-text-placeholder: rgba(255, 255, 255, 0.4); - --color-text-inactive: rgba(255, 255, 255, 0.5); - - --color-cyan-bright: #00f0ff; - --color-cyan-dark: #00a0aa; - --color-green-bright: #00ff00; - --color-error: #ff4444; - --color-checkbox-bg: rgba(11, 25, 44, 0.8); - - --font-main: 'UI Font', sans-serif; -} \ No newline at end of file diff --git a/lobby-frontend/src/i18n.js b/lobby-frontend/src/i18n.js deleted file mode 100644 index 410d8b5..0000000 --- a/lobby-frontend/src/i18n.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Dieses Objekt enthält alle Textbausteine für das Frontend. - * Wenn wir später weitere Sprachen hinzufügen (z.B. 'es' für Spanisch), - * müssen wir nur dieses Objekt erweitern, ohne den restlichen Code anzufassen. - */ -export const translations = { - de: { - // --- UI Elemente --- - tab_login: "Login", - tab_register: "Registrieren", - btn_login: "Login", - btn_register: "Jetzt kostenlos registrieren", - link_forgot_password: "Passwort vergessen?", - btn_reset_request: "Code anfordern", - btn_reset_password: "Passwort ändern", - discord_text: "Offizieller Discord Server", - link_back: "« Zurück", - btn_male: "Männlich", - btn_female: "Weiblich", - btn_cancel: "Abbrechen", - btn_join: "Beitreten", - - // --- Platzhalter (Placeholders) --- - ph_username_login: "Benutzername / E-Mail", - ph_username_reg: "Benutzername eingeben", - ph_email: "E-Mail eingeben", - ph_password: "Passwort eingeben", - ph_otp: "6-stelliger Code", - - // --- Labels --- - lbl_agb: "Ich akzeptiere die AGB, Spielregeln & den Datenschutz", - lbl_remember: "Angemeldet bleiben", - - // --- OTP Modus --- - txt_otp_instruction: "Bitte gib den 6-stelligen Code aus deiner E-Mail ein.", - txt_reset_instruction_1: "Um dein Passwort zurückzusetzen, gib bitte deine E-Mail-Adresse ein.", - txt_reset_instruction_2: "Bitte gib den Code aus der E-Mail sowie dein neues Passwort ein.", - btn_verify: "BESTÄTIGEN", - msg_verify_success: "Verifizierung erfolgreich! Du kannst dich nun einloggen.", - link_resend_otp: "Code erneut senden", - - // --- Sektor-Onboarding --- - ERR_ALREADY_IN_SECTOR: "Du bist diesem Sektor bereits beigetreten.", - ERR_LOCAL_USERNAME_TAKEN: "Dieser Commander-Name ist in diesem Sektor leider schon vergeben.", - MSG_SECTOR_JOINED_SUCCESS: "Sektor erfolgreich beigetreten!", - - // --- Footer --- - link_agb: "AGB", - link_rules: "Spielregeln", - link_imprint: "Impressum", - link_privacy: "Datenschutz", - - // --- Backend Fehlermeldungen (Mapping) --- - ERR_MISSING_FIELDS: "Bitte alle Felder ausfüllen.", - ERR_PASSWORD_TOO_SHORT: "Das Passwort muss mindestens 8 Zeichen lang sein.", - ERR_USERNAME_TOO_SHORT: "Der Benutzername muss mindestens 3 Zeichen lang sein.", - ERR_INVALID_EMAIL: "Das E-Mail-Format ist ungültig.", - ERR_USERNAME_TAKEN: "Dieser Benutzername ist bereits vergeben.", - ERR_EMAIL_TAKEN: "Diese E-Mail-Adresse ist bereits vergeben.", - ERR_INVALID_CREDENTIALS: "Benutzername/E-Mail oder Passwort falsch.", - ERR_EMAIL_NOT_VERIFIED: "Bitte bestätige zuerst deine E-Mail-Adresse.", - ERR_INTERNAL_SERVER: "Ein interner Serverfehler ist aufgetreten.", - ERR_INVALID_OTP: "Der eingegebene Code ist falsch. Bitte überprüfe deine Eingabe.", - ERR_OTP_EXPIRED: "Dieser Code ist abgelaufen. Bitte fordere einen neuen Code an.", - ERR_COOLDOWN_ACTIVE: "Bitte warte 2 Minuten, bevor du einen neuen Code anforderst.", - - // --- Erfolgsmeldungen --- - MSG_REGISTER_SUCCESS: "Registrierung erfolgreich. Bitte prüfe deine E-Mails.", - MSG_LOGIN_SUCCESS: "Erfolgreich eingeloggt!", - MSG_OTP_RESENT: "Ein neuer Code wurde an deine E-Mail gesendet.", - MSG_RESET_EMAIL_SENT: "Code gesendet. Bitte prüfe deine E-Mails.", - MSG_PASSWORD_RESET_SUCCESS: "Passwort geändert! Du kannst dich nun einloggen.", - }, - en: { - // --- UI Elements --- - tab_login: "Login", - tab_register: "Register", - btn_login: "LOGIN", - btn_register: "REGISTER", - discord_text: "Official Discord Server", - link_forgot_password: "Forgot Password?", - btn_reset_request: "REQUEST CODE", - btn_reset_password: "CHANGE PASSWORD", - link_back: "« back", - btn_male: "Male", - btn_female: "Femail", - btn_cancel: "Cancel", - btn_join: "Join Sector", - - // --- Placeholders --- - ph_username_login: "Username / E-Mail", - ph_username_reg: "Enter Username", - ph_email: "Enter E-Mail", - ph_password: "Enter Password", - ph_otp: "6-digit code", - - // --- Labels --- - lbl_agb: "I accept the Terms of Service, Rules & Privacy Policy", lbl_remember: "Keep me logged in", - - // --- OTP Modus --- - txt_otp_instruction: "Please enter the 6-digit code from your e-mail.", - txt_reset_instruction_1: "To reset your password, please enter your e-mail address.", - txt_reset_instruction_2: "Please enter the code from the e-mail and your new password.", - btn_verify: "VERIFY", - msg_verify_success: "Verification successful! You can now log in.", - link_resend_otp: "Resend Code", - - // --- Sektor-Onboarding --- - ERR_ALREADY_IN_SECTOR: "You have already joined this sector.", - ERR_LOCAL_USERNAME_TAKEN: "This Commander name is already taken in this sector.", - MSG_SECTOR_JOINED_SUCCESS: "Successfully joined the sector!", - - // --- Footer --- - link_agb: "Terms of Service", - link_rules: "Game Rules", - link_imprint: "Imprint", - link_privacy: "Privacy Policy", - - // --- Backend Error Messages (Mapping) --- - ERR_MISSING_FIELDS: "Please fill in all fields.", - ERR_PASSWORD_TOO_SHORT: "Password must be at least 8 characters long.", - ERR_USERNAME_TOO_SHORT: "Username must be at least 3 characters long.", - ERR_INVALID_EMAIL: "Invalid e-mail format.", - ERR_USERNAME_TAKEN: "This username is already taken.", - ERR_EMAIL_TAKEN: "This e-mail address is already taken.", - ERR_INVALID_CREDENTIALS: "Invalid username/e-mail or password.", - ERR_EMAIL_NOT_VERIFIED: "Please verify your e-mail address first.", - ERR_INTERNAL_SERVER: "An internal server error occurred.", - ERR_INVALID_OTP: "The entered code is incorrect. Please check your input.", - ERR_OTP_EXPIRED: "This code has expired. Please request a new code.", - ERR_COOLDOWN_ACTIVE: "Please wait 2 minutes before requesting a new code.", - - // --- Success Messages --- - MSG_REGISTER_SUCCESS: "Registration successful. Please check your e-mails.", - MSG_LOGIN_SUCCESS: "Successfully logged in!", - MSG_OTP_RESENT: "A new code has been sent to your e-mail.", - MSG_RESET_EMAIL_SENT: "Code sent. Please check your e-mails.", - MSG_PASSWORD_RESET_SUCCESS: "Password changed! You can now log in.", - } -}; - -/** - * Die aktuell gewählte Sprache. - * Später können wir das auch im LocalStorage des Browsers speichern, - * damit sich das Spiel die Sprache des Nutzers merkt. - */ -export let currentLang = localStorage.getItem('void_language') || 'de'; - -/** - * Hilfsfunktion, um die Sprache zu wechseln. - */ -export function setLanguage(lang) { - if (translations[lang]) { - currentLang = lang; - localStorage.setItem('void_language', lang); - } -} - -/** - * Holt den passenden Text für den übergebenen Schlüssel (Key). - * Fällt auf den Key selbst zurück, falls keine Übersetzung gefunden wird - * (hilft beim Debuggen, um fehlende Übersetzungen zu erkennen). - */ -export function t(key) { - return translations[currentLang][key] || key; -} \ No newline at end of file diff --git a/lobby-frontend/src/main.js b/lobby-frontend/src/main.js deleted file mode 100644 index 8c5610d..0000000 --- a/lobby-frontend/src/main.js +++ /dev/null @@ -1,834 +0,0 @@ -import { t, setLanguage, currentLang } from './i18n.js'; -import { Assets, Sprite, NineSliceSprite, Container, TilingSprite, Text, wordWrap, fragmentGPUTemplate, Graphics } from 'pixi.js'; -import { loginUser, registerUser, verifyEmailUser, resendOTPCode, requestPasswordResetUser, resetPasswordUser, refreshAccessTokenUser } from './api/auth.js'; -import { initDropdowns, updateTranslations } from './ui/dropdowns.js'; -import { initForms, formState, setOtpMode, setResetPasswordMode, startVisualCountdown, setRegisterMode } from './ui/forms.js'; -import { fetchSectorsList, joinSectorUser } from './api/sectors.js'; -import { app, initPixi, setupResize, triggerResize } from './core/pixi-app.js'; -import { buildLoginScene, LOGIN_COLORS } from './scenes/login-scene.js'; -import { buildLobbyBase, createSectorTabPixi, createSectorCardPixi, buildOnboardingModalPixi } from './scenes/lobby-scene.js'; - -let globalSheet = null; -let background = null; -let uiContainer = null; -let lobbyPixiContainer = null; -let sectorPixiContainer = null; -let sectorTabsPixiContainer = null; -let footerBackground = null; -let footerPattern = null; -let footerBackgroundContainer = null; -let onboardingPixiContainer = null; -let onboardingPixiRefs = null; -let selectedSectorId = null; -let selectedOnboardingGender = 'male'; - -async function initGame() { - await initPixi('pixi-container'); - - try { - const assetsToLoad = { - background: 'src/raw-assets/backgrounds/Background_A.png', - uiAtlas: 'src/assets/ui-atlas.json' - }; - - const backgroundTexture = await Assets.load(assetsToLoad.background); - const uiSpritesheet = await Assets.load(assetsToLoad.uiAtlas); - - await document.fonts.load('16px "UI Font"'); - await document.fonts.load('16px "Logo Font"'); - - updateTranslations(); - - buildScene(backgroundTexture, uiSpritesheet); - - document.getElementById('username').placeholder = t('ph_username_reg'); - document.getElementById('email').placeholder = t('ph_email'); - document.getElementById('password').placeholder = t('ph_password'); - document.getElementById('btn-register').textContent = t('btn_register'); - - await checkAuthOnStartup(); - - } catch (error) { - console.error("Fehler beim Laden der Assets:", error); - } -} - -function buildScene(bgTexture, sheet) { - globalSheet = sheet; - - // --- Hintergrund --- - background = new Sprite(bgTexture); - app.stage.addChild(background); - - // --- Haupt-UI-Container --- - uiContainer = new Container(); - app.stage.addChild(uiContainer); - - lobbyPixiContainer = new Container(); - lobbyPixiContainer.visible = false; - app.stage.addChild(lobbyPixiContainer); - - sectorPixiContainer = buildLobbyBase(lobbyPixiContainer, sheet); - - const htmlSectorList = document.getElementById('sector-list-container'); - if (htmlSectorList) { - htmlSectorList.addEventListener('scroll', (e) => { - sectorPixiContainer.y = -e.target.scrollTop; - }); - } - - // --- Footer Hintergund --- - footerBackgroundContainer = new Container(); - - footerBackground = new TilingSprite({ - texture: sheet.textures['SPR_SciFiMenus_Background_01'], - height: 40, - }); - footerBackground.tileScale.set(1.0); - footerBackground.tint = 0x000000; - footerBackground.alpha = 1.0; - footerBackgroundContainer.addChild(footerBackground); - - footerPattern = new TilingSprite({ - texture: sheet.textures['SPR_SciFiMenus_Background_Hexagons_01'], - height: 40, - }); - footerPattern.tileScale.set(0.3); - footerPattern.tint = LOGIN_COLORS.PATTERN; - footerPattern.alpha = 0.01; - footerBackgroundContainer.addChild(footerPattern); - - app.stage.addChild(footerBackgroundContainer); - - onboardingPixiRefs = buildOnboardingModalPixi(app.stage, sheet); - onboardingPixiContainer = onboardingPixiRefs.container; - - const loginRefs = buildLoginScene(uiContainer, sheet); - - initForms({ - tabLogin: loginRefs.tabLogin, - tabRegister: loginRefs.tabRegister, - inputUser: loginRefs.inputUser, - inputEmail: loginRefs.inputEmail, - inputLock: loginRefs.inputLock, - btnRegister: loginRefs.btnRegister, - discordIcon: loginRefs.discordIcon, - discordText: loginRefs.discordText, - COLOR_TAB_ACTIVE: LOGIN_COLORS.TAB_ACTIVE, - COLOR_TAB_INACTIVE: LOGIN_COLORS.TAB_INACTIVE, - COLOR_TAB_HOVER: LOGIN_COLORS.TAB_HOVER, - COLOR_BTN_HOVER: LOGIN_COLORS.BTN_HOVER, - COLOR_BTN_DEFAULT: LOGIN_COLORS.BTN_DEFAULT - }); - - setupResize({ - background, - discordContainer: loginRefs.discordContainer, - logoContainer: loginRefs.logoContainer, - uiContainer, - panelFrame: loginRefs.panelFrame, - lobbyPixiContainer, - footerBackground, - footerPattern, - footerBackgroundContainer, - onboardingPixiContainer - }); - - formState.isLoginMode = true; - setRegisterMode(); -} - -const form = document.getElementById('register-form'); -const msgBox = document.getElementById('msg-box'); - -form.addEventListener('submit', async (e) => { - e.preventDefault(); - - const usernameVal = document.getElementById('username').value; - const emailVal = document.getElementById('email').value; - const passwordVal = document.getElementById('password').value; - - msgBox.textContent = '...'; - msgBox.style.color = 'var(--color-text-main)'; - - if (formState.isForgotPasswordMode) { - try { - const result = await requestPasswordResetUser(emailVal); - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - - setTimeout(() => { - setResetPasswordMode(emailVal); - }, 1500); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - return; - } - - if (formState.isResetPasswordMode) { - try { - const result = await resetPasswordUser(formState.resetEmail, emailVal, passwordVal); - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - - setTimeout(() => { - window.location.reload(); - }, 1500); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - return; - } - - if (formState.isOtpMode) { - try { - const result = await verifyEmailUser(formState.pendingVerificationEmail, passwordVal); - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t('msg_verify_success'); - - setTimeout(() => { - window.location.reload(); - }, 1500); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - return; - } - - if (formState.isLoginMode) { - try { - const result = await loginUser(usernameVal, passwordVal); - - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - - if (result.data.error === 'ERR_EMAIL_NOT_VERIFIED') { - setTimeout(() => { - setOtpMode(result.data.email); - }, 1500); - } - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - - const rememberMe = document.getElementById('remember').checked; - sessionStorage.setItem('void_access_token', result.data.accessToken); - sessionStorage.setItem('void_user', JSON.stringify(result.data.user)); - - if (rememberMe) { - localStorage.setItem('void_refresh_token', result.data.refreshToken); - sessionStorage.removeItem('void_refresh_token'); - } else { - sessionStorage.setItem('void_refresh_token', result.data.refreshToken); - localStorage.removeItem('void_refresh_token'); - } - transitionToLobby(); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - } else { // Register Mode - try { - const result = await registerUser(usernameVal, emailVal, passwordVal); - - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error)|| t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - - setTimeout(() => { - setOtpMode(emailVal); - }, 1500); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - } -}); - -const resendOtpLink = document.getElementById('resend-otp-link'); -if (resendOtpLink) { - resendOtpLink.addEventListener('click', async (e) => { - e.preventDefault(); - - if (!pendingVerificationEmail) return; - - msgBox.textContent = '...'; - msgBox.style.color = 'var(--color-text-main)'; - - try { - const result = await resendOTPCode(pendingVerificationEmail); - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - startVisualCountdown(resendOtpLink, 'link_resend_otp', 120); - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - }); -} - -/** - * Diese Funktion wird aufgerufenm wenn der Login erfolgreich war und die Tokens - * sicher gespeichert sind, Sie baut das HTML-Formulat ab und bereitet PixiJS - * auf die Lobby-Ansicht vor - */ -async function transitionToLobby() { - const formElement = document.getElementById('register-form'); - if (formElement) { - formElement.style.display = 'none'; - } - - uiContainer.visible = false; - lobbyPixiContainer.visible = true; - - try { - const lobbyBGPath = 'src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png'; - const lobbyBGTexture = await Assets.load(lobbyBGPath); - background.texture = lobbyBGTexture; - triggerResize(); - buildLobbyUI(); - } catch (error) { - console.error("Fehler beim Laden des Lobby-Hintergrunds:", error); - } -} - -/** - * Blendet die Top-Bar ein, setzt den Spilernamen und aktiviert den Logout - */ -async function buildLobbyUI() { - const userDropdown = document.getElementById('user-dropdown'); - if (userDropdown) { - userDropdown.style.display = 'block'; - } - - const userDataStr = sessionStorage.getItem('void_user'); - if (userDataStr) { - try { - const userData = JSON.parse(userDataStr); - const usernameEl = document.getElementById('lobby-username'); - if (usernameEl && userData.username) { - usernameEl.textContent = userData.username; - } - - const emailEl = document.getElementById('lobby-email'); - if (emailEl && userData.email) { - emailEl.textContent = userData.email; - } - - const premiumEl = document.getElementById('lobby-premium'); - if (premiumEl && userData.res_premium !== undefined) { - premiumEl.textContent = userData.res_premium; - } - } catch (e) { - console.error("Fehler beim Auslesen der User-Daten:", e); - } - } - - await loadAndRenderSectorBrowser(); -} - -/** - * Läd die Sektordaten von der API und steuert die Tab-Sichtbarkeiten - */ -async function loadAndRenderSectorBrowser() { - const token = sessionStorage.getItem('void_access_token') || localStorage.getItem('void_access_token'); - if (!token) return; - - try { - const result = await fetchSectorsList(token); - if (!result.ok || !result.data.success) { - console.error("Fehler beim Laden der Sektoren:", result.data.error); - return; - } - - const data = result.data; - const layer = document.getElementById('sector-browser-layer'); - layer.style.display = 'block'; - - const tabLastSector = document.getElementById('tab-last-sector'); - const tabMySectors = document.getElementById('tab-my-sectors'); - const tabNewSectors = document.getElementById('tab-new-sectors'); - - if (sectorTabsPixiContainer) { - lobbyPixiContainer.removeChild(sectorTabsPixiContainer); - sectorTabsPixiContainer.destroy({ children: true }); - } - sectorTabsPixiContainer = new Container(); - lobbyPixiContainer.addChild(sectorTabsPixiContainer); - - const isMobilePortrait = window.innerWidth <= 800 && window.innerHeight > window.innerWidth; - const centerPos = isMobilePortrait ? 325 : 620; - - if (data.mySectors.length === 0) { - // Szenario A: Keine eigenen Sektoren - tabLastSector.style.display = 'none'; - tabMySectors.style.display = 'none'; - tabNewSectors.classList.add('active'); - - const singleTabX = centerPos - 90; - const tabNewBg = createSectorTabPixi(globalSheet, singleTabX, 20, LOGIN_COLORS.TAB_ACTIVE, 0.6); - // ACHTUNG: Hier jetzt .container verwenden! - sectorTabsPixiContainer.addChild(tabNewBg.container); - renderSectorList(data.newSectors, 'new'); - - } else { - // Szenario B: Eigene Sektoren vorhanden - tabLastSector.style.display = 'inline-block'; - tabMySectors.style.display = 'inline-block'; - tabNewSectors.style.display = 'inline-block'; - - // --- NEU: Daten für die Tabs filtern und aufteilen --- - - // 1. "Zuletzt gespielt" - // Da das Backend bereits nach Datum absteigend sortiert, ist das Element - // an Index 0 garantiert der Sektor, in dem der Spieler zuletzt aktiv war. - // Wir packen ihn in ein neues Array, da unsere renderSectorList-Funktion ein Array erwartet. - const lastPlayedSectorArray = [data.mySectors[0]]; - - // 2. "Meine Sektoren" (Der Rest) - // Wir nutzen die JavaScript-Funktion 'slice(1)', um alle Sektoren ab dem zweiten Element (Index 1) - // bis zum Ende des Arrays zu kopieren. - // Hinweis: Wenn der Spieler insgesamt nur 1 Sektor besitzt, gibt slice(1) automatisch - // ein leeres Array [] zurück, was perfekt ist! - const otherMySectorsArray = data.mySectors.slice(1); - - // --- PixiJS-Hintergründe berechnen (unverändert) --- - const leftTabX = centerPos - 290; - const midTabX = centerPos - 90; - const rightTabX = centerPos + 110; - - const tabLastBg = createSectorTabPixi(globalSheet, leftTabX, 20, LOGIN_COLORS.TAB_ACTIVE, 0.6); - const tabMyBg = createSectorTabPixi(globalSheet, midTabX, 20, LOGIN_COLORS.TAB_INACTIVE, 0.4); - const tabNewBg = createSectorTabPixi(globalSheet, rightTabX, 20, LOGIN_COLORS.TAB_INACTIVE, 0.4); - - sectorTabsPixiContainer.addChild(tabLastBg.container, tabMyBg.container, tabNewBg.container); - - const updateTabVisuals = (activeHtmlId) => { - tabLastSector.classList.remove('active'); - tabMySectors.classList.remove('active'); - tabNewSectors.classList.remove('active'); - - document.getElementById(activeHtmlId).classList.add('active'); - - tabLastBg.fill.tint = LOGIN_COLORS.TAB_INACTIVE; tabLastBg.fill.alpha = 0.4; - tabMyBg.fill.tint = LOGIN_COLORS.TAB_INACTIVE; tabMyBg.fill.alpha = 0.4; - tabNewBg.fill.tint = LOGIN_COLORS.TAB_INACTIVE; tabNewBg.fill.alpha = 0.4; - - if (activeHtmlId === 'tab-last-sector') { tabLastBg.fill.tint = LOGIN_COLORS.TAB_ACTIVE; tabLastBg.fill.alpha = 0.6; } - if (activeHtmlId === 'tab-my-sectors') { tabMyBg.fill.tint = LOGIN_COLORS.TAB_ACTIVE; tabMyBg.fill.alpha = 0.6; } - if (activeHtmlId === 'tab-new-sectors') { tabNewBg.fill.tint = LOGIN_COLORS.TAB_ACTIVE; tabNewBg.fill.alpha = 0.6; } - }; - - // --- ÄNDERUNG: Klick-Events mit den gefilterten Arrays versorgen --- - tabLastSector.onclick = () => { - updateTabVisuals('tab-last-sector'); - // Wir übergeben hier ausschließlich das Array mit dem EINEN, letzten Sektor - renderSectorList(lastPlayedSectorArray, 'my'); - }; - - tabMySectors.onclick = () => { - updateTabVisuals('tab-my-sectors'); - // Wir übergeben hier alle Sektoren AUSSER dem letzten - renderSectorList(otherMySectorsArray, 'my'); - }; - - tabNewSectors.onclick = () => { - updateTabVisuals('tab-new-sectors'); - // Neue Sektoren bleiben natürlich unangetastet - renderSectorList(data.newSectors, 'new'); - }; - - const setupTabHover = (htmlEl, pixiRef) => { - htmlEl.onmouseenter = () => { - if (!htmlEl.classList.contains('active')) { - pixiRef.fill.tint = LOGIN_COLORS.TAB_HOVER; - pixiRef.fill.alpha = 0.8; - } - }; - htmlEl.onmouseleave = () => { - if (!htmlEl.classList.contains('active')) { - pixiRef.fill.tint = LOGIN_COLORS.TAB_INACTIVE; - pixiRef.fill.alpha = 0.4; - } - }; - }; - - setupTabHover(tabLastSector, tabLastBg); - setupTabHover(tabMySectors, tabMyBg); - setupTabHover(tabNewSectors, tabNewBg); - - // --- ÄNDERUNG: Initialer Aufruf --- - // Wenn die Lobby lädt, starten wir standardmäßig im "Zuletzt gespielt"-Tab, - // also übergeben wir auch hier das gefilterte 1-Element-Array. - renderSectorList(lastPlayedSectorArray, 'my'); - } - - } catch (error) { - console.error("Netzwerkfehler beim Sektor-Abruf:", error); - } -} - -/** - * Rendert die HTML-Karte und die dazugehöhrigen PixiJS-Hintergrundboxen - */ -function renderSectorList(sectors, type) { - const listContainer = document.getElementById('sector-list-container'); - listContainer.innerHTML = ''; - - if (sectorPixiContainer) { - sectorPixiContainer.removeChildren(); - sectorPixiContainer.y = 0; - } - - const htmlSectorList = document.getElementById('sector-list-container'); - if (htmlSectorList) htmlSectorList.scrollTop = 0; - - if (sectors.length === 0) { - listContainer.innerHTML = '

Keine Sektoren in dieser Kategorie verfügbar.

'; - return; - } - - sectors.forEach((sector, index) => { - const cardLink = document.createElement('a'); - cardLink.className = 'sector-card-link'; - cardLink.href = `#`; - - // --- Temporäre Platzhalter für das neue UI (ersetzen wir später durch echte DB-Werte) --- - const ageDays = sector.age_days || Math.floor(Math.random() * 30) + 1; - const totalPlayers = sector.total_players || Math.floor(Math.random() * 500) + 150; - - let bottomRowHtml = ''; - if (type === 'my' && sector.local_username) { - bottomRowHtml = `
Spieler: ${sector.local_username}
`; - } - - cardLink.innerHTML = ` -
- - - ${sector.name} - - ${ageDays} Tage - - ${sector.status === 'online' ? 'online' : 'wartung'} - - Online: ${sector.online_players}/${totalPlayers} - Planeten: ${sector.planets_count} - ${bottomRowHtml} -
- `; - - cardLink.onclick = (e) => { - e.preventDefault(); - console.log(`Starte Onboarding für Sektor: ${sector.name} (ID: ${sector.id})`); - - selectedSectorId = sector.id; - - const sectorBrowserLayer = document.getElementById('sector-browser-layer'); - if (sectorBrowserLayer) { - sectorBrowserLayer.style.opacity = 0.05; - sectorBrowserLayer.style.pointerEvents = 'none'; - } - - const onboardingHtmlLayer = document.getElementById('sector-onboarding-layer'); - if (onboardingHtmlLayer) onboardingHtmlLayer.style.display = 'flex'; - - if (onboardingPixiContainer) onboardingPixiContainer.visible = true; - - const localUsernameInput = document.getElementById('local-username'); - if (localUsernameInput) { - // Den Nutzer-String aus dem SessionStorage abrufen, den wir beim Login gespeichert haben - const userDataStr = sessionStorage.getItem('void_user') || localStorage.getItem('void_user'); - if (userDataStr) { - try { - // Den JSON-String wieder in ein nutzbares JavaScript-Objekt umwandeln - const userData = JSON.parse(userDataStr); - if (userData.username) { - // Wir tragen den Namen als echten Wert ein, damit das 'required' Attribut erfüllt ist - localUsernameInput.value = userData.username; - } - } catch (err) { - console.error("Fehler beim Auslesen der User-Daten für das Onboarding:", err); - } - } - } - }; - - listContainer.appendChild(cardLink); - - const isMobilePortrait = window.innerWidth <= 800 && window.innerHeight > window.innerWidth; - // Im Hochformat haben wir 2 Spalten (durch unsere Media Query), sonst 4 - const columns = isMobilePortrait ? 2 : 4; - const cardWidth = 270; - const cardHeight = 90; - const gapX = 25; - const gapY = 20; - - // Wir lesen die echte Breite des HTML-Containers ab (das zieht die Scrollbar automatisch ab!) - const listContainerDOM = document.getElementById('sector-list-container'); - const containerClientWidth = listContainerDOM ? listContainerDOM.clientWidth : (isMobilePortrait ? 650 : 1240); - - // Wir berechnen die Breite des GESAMTEN Grid-Blocks. - // Bei 4 Spalten: (4 * 270) + (3 * 25) = 1155px Gesamtbreite. - const gridBlockWidth = (columns * cardWidth) + ((columns - 1) * gapX); - - // Der Start-Punkt X ist exakt die verbleibende Restbreite geteilt durch 2. - // Das entspricht mathematisch 1:1 dem 'justify-content: center' aus dem CSS. - const startX = (containerClientWidth - gridBlockWidth) / 2; - const startY = 100; // 80px Top-Bar-Offset + 20px Padding oben - - const col = index % columns; - const row = Math.floor(index / columns); - - const posX = startX + col * (cardWidth + gapX); - const posY = startY + row * (cardHeight + gapY); - - // PixiJS-Hintergründe & Icons generieren lassen - const cardRefs = createSectorCardPixi(globalSheet, sectorPixiContainer, posX, posY); - - cardLink.addEventListener('mouseenter', () => { - cardRefs.frameSprite.alpha = 1.0; - cardRefs.bgSprite.alpha = 0.8; - cardRefs.iconDetails.tint = 0x00f0ff; - cardRefs.iconDetails.scale.set(0.09); - cardRefs.iconDetails.rotation = 0.2; - cardRefs.iconPlayers.tint = 0xffffff; - cardRefs.iconPlanets.tint = 0xffffff; - }); - - cardLink.addEventListener('mouseleave', () => { - cardRefs.frameSprite.alpha = 0.8; - cardRefs.bgSprite.alpha = 0.6; - cardRefs.iconDetails.tint = 0xaaaaaa; - cardRefs.iconDetails.scale.set(0.08); - cardRefs.iconDetails.rotation = 0; - cardRefs.iconPlayers.tint = 0xaaaaaa; - cardRefs.iconPlanets.tint = 0xaaaaaa; - }); - }); -} - -async function checkAuthOnStartup() { - const refreshToken = localStorage.getItem('void_refresh_token') || sessionStorage.getItem('void_refresh_token'); - if (!refreshToken) { return } - - try { - const result = await refreshAccessTokenUser(refreshToken); - - if (result.ok && result.data.accessToken) { - sessionStorage.setItem('void_access_token', result.data.accessToken); - if (result.data.user) sessionStorage.setItem('void_user', JSON.stringify(result.data.user)); - transitionToLobby(); - } else { - sessionStorage.removeItem('void_access_token'); - sessionStorage.removeItem('void_refresh_token'); - localStorage.removeItem('void_refresh_token'); - sessionStorage.removeItem('void_user'); - } - } catch (error) { - console.error("Netzwerkfehler beim Auto-Login:", error); - } -} - -initDropdowns(() => { - const userField = document.getElementById('username'); - if (userField) userField.placeholder = formState.isLoginMode ? t('ph_username_login') : t('ph_username_reg'); - - const emailField = document.getElementById('email'); - if (emailField) emailField.placeholder = t('ph_email'); - - const passField = document.getElementById('password'); - if (passField) passField.placeholder = formState.isOtpMode || formState.isResetPasswordMode ? t('ph_otp') : t('ph_password'); -}); - -const btnGenderMale = document.getElementById('btn-gender-male'); -const btnGenderFemale = document.getElementById('btn-gender-female'); -const btnSubmitOnboarding = document.getElementById('btn-submit-onboarding'); -const onboardingForm = document.getElementById('onboarding-form'); -const btnCancelOnboarding = document.getElementById('btn-cancel-onboarding'); - -if (btnCancelOnboarding) { - btnCancelOnboarding.addEventListener('click', (e) => { - e.preventDefault(); - - // 1. Sektor-Browser HTML wieder voll sichtbar machen - const sectorBrowserLayer = document.getElementById('sector-browser-layer'); - if (sectorBrowserLayer) { - sectorBrowserLayer.style.opacity = '1.0'; - sectorBrowserLayer.style.pointerEvents = 'auto'; // Klicks wieder erlauben - } - - // 2. HTML-Layer des Modals ausblenden - const onboardingHtmlLayer = document.getElementById('sector-onboarding-layer'); - if (onboardingHtmlLayer) onboardingHtmlLayer.style.display = 'none'; - - // 3. PixiJS-Layer ausblenden - if (onboardingPixiContainer) onboardingPixiContainer.visible = false; - - // 4. Formular zurücksetzen (damit alte Eingaben verschwinden) - const onboardingForm = document.getElementById('onboarding-form'); - if (onboardingForm) onboardingForm.reset(); - - // Optional: Hier auch noch die visuelle 'active' Klasse von deinen - // Geschlechter-Buttons entfernen, falls nötig. - }); -} - -// --- Hilfsfunktion: Hover-Effekte synchronisieren --- -// Genau wie beim Login übertragen wir den Mauszeiger vom unsichtbaren HTML auf PixiJS -function syncHover(htmlElement, pixiRef, defaultTint, hoverTint = LOGIN_COLORS.TAB_HOVER) { - if (!htmlElement || !pixiRef) return; - - htmlElement.addEventListener('mouseenter', () => { - // Wenn der Button gerade "aktiv" ist (z.B. gewähltes Geschlecht), ignorieren wir den Hover - if (!htmlElement.classList.contains('active')) { - pixiRef.fill.tint = hoverTint; - pixiRef.fill.alpha = 0.8; - } - }); - - htmlElement.addEventListener('mouseleave', () => { - if (!htmlElement.classList.contains('active')) { - pixiRef.fill.tint = defaultTint; - pixiRef.fill.alpha = 0.6; // Zurück zur Standard-Transparenz - } - }); -} - -// Hover-Effekte auf die Buttons anwenden (falls onboardingPixiRefs schon existiert) -// Wir prüfen hier sicherheitshalber ab, da die Refs asynchron generiert werden -const applyHoverEffects = setInterval(() => { - if (onboardingPixiRefs) { - clearInterval(applyHoverEffects); // Sobald geladen, Interval stoppen - - // Geschlechter-Buttons (Standard-Farbe ist Inactive, Hover ist Weiß) - syncHover(btnGenderMale, onboardingPixiRefs.btnMale, LOGIN_COLORS.TAB_INACTIVE); - syncHover(btnGenderFemale, onboardingPixiRefs.btnFemale, LOGIN_COLORS.TAB_INACTIVE); - - // Aktions-Buttons (Standard-Farbe ist Default-Grün) - syncHover(btnCancelOnboarding, onboardingPixiRefs.btnCancel, LOGIN_COLORS.BTN_DEFAULT, LOGIN_COLORS.BTN_HOVER); - syncHover(btnSubmitOnboarding, onboardingPixiRefs.btnJoin, LOGIN_COLORS.BTN_DEFAULT, LOGIN_COLORS.BTN_HOVER); - } -}, 100); - -// --- Geschlecht Umschalten (Toggle-Logik) --- -if (btnGenderMale && btnGenderFemale) { - btnGenderMale.addEventListener('click', (e) => { - e.preventDefault(); - selectedOnboardingGender = 'male'; - - // HTML Klassen updaten (für Text-Farbe) - btnGenderMale.classList.add('active'); - btnGenderFemale.classList.remove('active'); - - // PixiJS Farben updaten - if (onboardingPixiRefs) { - onboardingPixiRefs.btnMale.fill.tint = LOGIN_COLORS.TAB_ACTIVE; - onboardingPixiRefs.btnMale.fill.alpha = 0.6; - - onboardingPixiRefs.btnFemale.fill.tint = LOGIN_COLORS.TAB_INACTIVE; - onboardingPixiRefs.btnFemale.fill.alpha = 0.4; - } - }); - - btnGenderFemale.addEventListener('click', (e) => { - e.preventDefault(); - selectedOnboardingGender = 'female'; - - // HTML Klassen updaten - btnGenderFemale.classList.add('active'); - btnGenderMale.classList.remove('active'); - - // PixiJS Farben updaten - if (onboardingPixiRefs) { - onboardingPixiRefs.btnFemale.fill.tint = LOGIN_COLORS.TAB_ACTIVE; - onboardingPixiRefs.btnFemale.fill.alpha = 0.6; - - onboardingPixiRefs.btnMale.fill.tint = LOGIN_COLORS.TAB_INACTIVE; - onboardingPixiRefs.btnMale.fill.alpha = 0.4; - } - }); -} - -// --- Formular Absenden (Beitreten) --- -if (onboardingForm) { - onboardingForm.addEventListener('submit', async (e) => { - e.preventDefault(); // Verhindert das Neuladen der Seite - - if (!selectedSectorId) return; - - const localUsername = document.getElementById('local-username').value; - const msgBox = document.getElementById('onboarding-msg-box'); - - msgBox.textContent = '...'; - msgBox.style.color = 'var(--color-text-main)'; - - const token = sessionStorage.getItem('void_access_token') || localStorage.getItem('void_access_token'); - - try { - - const result = await joinSectorUser(token, selectedSectorId, localUsername, selectedOnboardingGender); - - if (!result.ok) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t(result.data.error) || t('ERR_INTERNAL_SERVER'); - return; - } - - msgBox.style.color = 'var(--color-green-bright)'; - msgBox.textContent = t(result.data.message); - - setTimeout(() => { - if (btnCancelOnboarding) btnCancelOnboarding.click(); - - loadAndRenderSectorBrowser(); - }, 1500); - - } catch (error) { - msgBox.style.color = 'var(--color-error)'; - msgBox.textContent = t('ERR_INTERNAL_SERVER'); - } - }); -} -initGame(); \ No newline at end of file diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_A.png b/lobby-frontend/src/raw-assets/backgrounds/Background_A.png deleted file mode 100644 index 88be743..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_A.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_B.png b/lobby-frontend/src/raw-assets/backgrounds/Background_B.png deleted file mode 100644 index 79b0165..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_B.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_C.png b/lobby-frontend/src/raw-assets/backgrounds/Background_C.png deleted file mode 100644 index 5eb8599..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_C.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_D.png b/lobby-frontend/src/raw-assets/backgrounds/Background_D.png deleted file mode 100644 index cfdd3b5..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_D.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_E.png b/lobby-frontend/src/raw-assets/backgrounds/Background_E.png deleted file mode 100644 index 90d3308..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_E.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_F.png b/lobby-frontend/src/raw-assets/backgrounds/Background_F.png deleted file mode 100644 index 165ca31..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_F.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/Background_G.png b/lobby-frontend/src/raw-assets/backgrounds/Background_G.png deleted file mode 100644 index 1f9e008..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/Background_G.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png b/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png deleted file mode 100644 index be33e46..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpaceStation_01.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png b/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png deleted file mode 100644 index 514caa6..0000000 Binary files a/lobby-frontend/src/raw-assets/backgrounds/SPR_SciFiMenus_Example_Image_SpookyComputer_01.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png deleted file mode 100644 index 8d2fabe..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Lock_Closed_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Message_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Message_01_Clean.png deleted file mode 100644 index 5a29168..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Message_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Multiplayer_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Multiplayer_01_Clean.png deleted file mode 100644 index e57513b..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Multiplayer_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Planet_02_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Planet_02_Clean.png deleted file mode 100644 index 52423a6..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Planet_02_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Settings_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Settings_01_Clean.png deleted file mode 100644 index 6c9bc41..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Settings_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png deleted file mode 100644 index 05899a9..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Solo_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png deleted file mode 100644 index d37a03f..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Tick_01_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png deleted file mode 100644 index 0087b6d..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFiMenus_Menu_Warning_02_Clean.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png b/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png deleted file mode 100644 index bfbab02..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_SciFi_Map_Message.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png b/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png deleted file mode 100644 index 546bfec..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/ICON_Social_Discord.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png deleted file mode 100644 index 89d0139..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_01.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png deleted file mode 100644 index ab03f8c..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Background_Hexagons_01.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png deleted file mode 100644 index ef9c298..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png deleted file mode 100644 index a5e4ed1..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_02_Background.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11.png deleted file mode 100644 index e95fd2d..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11_Background.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11_Background.png deleted file mode 100644 index f45cbc5..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_11_Background.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png deleted file mode 100644 index ce8c5b1..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png deleted file mode 100644 index 1cf76b0..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Frame_Box_Large_12_Background.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_05.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_05.png deleted file mode 100644 index 929a679..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_05.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png deleted file mode 100644 index 065ba2f..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Background_07.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png deleted file mode 100644 index 37d4e66..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_10.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png deleted file mode 100644 index 1b84ba9..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_19.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png deleted file mode 100644 index 016000a..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Menu_Item_Selected_20.png and /dev/null differ diff --git a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png b/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png deleted file mode 100644 index 87f929a..0000000 Binary files a/lobby-frontend/src/raw-assets/ui/SPR_SciFiMenus_Ring_Medium_13.png and /dev/null differ diff --git a/lobby-frontend/src/scenes/lobby-scene.js b/lobby-frontend/src/scenes/lobby-scene.js deleted file mode 100644 index 38c96bb..0000000 --- a/lobby-frontend/src/scenes/lobby-scene.js +++ /dev/null @@ -1,253 +0,0 @@ -import { Container, NineSliceSprite, Graphics, Sprite } from "pixi.js"; -import { LOGIN_COLORS } from "./login-scene"; - -/** - * Baut das statische Grundgerüst des Sektor-Browsers (Hintergrund & Scroll-Maske) - */ -export function buildLobbyBase(lobbyPixiContainer, sheet) { - const sectorBrowserBg = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Frame_Box_Large_11_Background'], - leftWidth: 100, - topHeight: 100, - rightWidth: 100, - bottomHeight: 100 - }); - sectorBrowserBg.width = 1240, - sectorBrowserBg.height = 650; - sectorBrowserBg.alpha = 0.8; - sectorBrowserBg.tint = LOGIN_COLORS.PANEL_BG; - lobbyPixiContainer.addChild(sectorBrowserBg); - - const sectorPixiContainer = new Container(); - lobbyPixiContainer.addChild(sectorPixiContainer); - - const scrollMask = new Graphics(); - scrollMask.rect(30, 80, 1180, 540); - scrollMask.fill(0xffffff); - lobbyPixiContainer.addChild(scrollMask); - sectorPixiContainer.mask = scrollMask; - - lobbyPixiContainer.bgSprite = sectorBrowserBg; - lobbyPixiContainer.scrollMask = scrollMask; - - return sectorPixiContainer; -} - -/** - * Generiert einen Sektor-Tab (Meine Sektoren / Neue Sektoren) - */ -export function createSectorTabPixi(sheet, x, y, tint, alpha) { - const itemContainer = new Container(); - itemContainer.position.set(x, y); - - const fill = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Background_07'], - leftWidth: 120, - topHeight: 120, - rightWidth: 120, - bottomHeight: 120 - }); - fill.width = 180; - fill.height = 40; - fill.tint = tint; - fill.alpha = alpha; - itemContainer.addChild(fill); - - const frame = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Selected_10'], - leftWidth: 100, - topHeight: 120, - rightWidth: 140, - bottomHeight: 120 - }); - frame.width = 180; - frame.height = 40; - frame.alpha = 0.8; - itemContainer.addChild(frame); - - return { container: itemContainer, fill, frame }; -} - -/** - * Generiert die PixiJS-Hintergründe und Icons für EINE Sektor-Karte. - * Gibt Referenzen zurück, damit wir später Hover-Effekte im HTML-Link auslösen können. - */ -export function createSectorCardPixi(sheet, sectorPixiContainer, posX, posY) { - const cardWidth = 270; - const cardHeight = 90; - - const bgSprite = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Frame_Box_Large_11_Background'], - leftWidth: 200, - topHeight: 200, - rightWidth: 200, - bottomHeight: 200 - }); - bgSprite.width = cardWidth; - bgSprite.height = cardHeight; - bgSprite.position.set(posX, posY); - bgSprite.tint = 0x0b192c; - bgSprite.alpha = 0.6; - sectorPixiContainer.addChild(bgSprite); - - const frameSprite = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Frame_Box_Large_11'], - leftWidth: 200, - topHeight: 200, - rightWidth: 200, - bottomHeight: 200 - }); - frameSprite.width = cardWidth; - frameSprite.height = cardHeight; - frameSprite.position.set(posX, posY); - frameSprite.alpha = 0.8; - sectorPixiContainer.addChild(frameSprite); - - const iconDetails = new Sprite(sheet.textures['ICON_SciFiMenus_Menu_Message_01_Clean']); - iconDetails.anchor.set(0.5); - iconDetails.position.set(posX + 25, posY + 27); - iconDetails.scale.set(0.08); iconDetails.tint = 0xaaaaaa; - sectorPixiContainer.addChild(iconDetails); - - const iconAge = new Sprite(sheet.textures['ICON_SciFiMenus_Menu_Settings_01_Clean']); - iconAge.anchor.set(0.5); - iconAge.position.set(posX + 130, posY + 26); - iconAge.scale.set(0.08); iconAge.tint = 0xaaaaaa; - sectorPixiContainer.addChild(iconAge); - - const iconPlayers = new Sprite(sheet.textures['ICON_SciFiMenus_Menu_Multiplayer_01_Clean']); - iconPlayers.anchor.set(0.5); - iconPlayers.position.set(posX + 25, posY + 62); - iconPlayers.scale.set(0.08); iconPlayers.tint = 0xaaaaaa; - sectorPixiContainer.addChild(iconPlayers); - - const iconPlanets = new Sprite(sheet.textures['ICON_SciFiMenus_Menu_Planet_02_Clean']); - iconPlanets.anchor.set(0.5); - iconPlanets.position.set(posX + 155, posY + 62); - iconPlanets.scale.set(0.08); iconPlanets.tint = 0xaaaaaa; - sectorPixiContainer.addChild(iconPlanets); - - return { bgSprite, frameSprite, iconDetails, iconPlayers, iconPlanets, iconAge }; -} - -/** - * Baut die PixiJS-Elemente für das Sektor-Onboarding-Modal (Charaktererstellung) - * - * @param {Container} parentContainer - Die Hauptebene (app.stage, auf die das Modal gelegt wird - * @param {Object} sheet - Das globale Sprite-Sheet für die Texturen - * - * @returns {Container} - Die Referenz auf den fertigen Pixi Container - */ -export function buildOnboardingModalPixi(parentContainer, sheet) { - const modalContainer = new Container(); - modalContainer.visible = false; - - const darkOverlay = new Graphics(); - darkOverlay.rect(-3000, -3000, 6000, 6000); - darkOverlay.fill(0x000000); - darkOverlay.alpha = 0.85; - darkOverlay.eventMode = 'static'; - modalContainer.addChild(darkOverlay); - - function createPixiUIItem(x, y, width, height, fillTint, fillAlpha, isBtn) { - const itemContainer = new Container(); - itemContainer.position.set(x, y); - - const fill = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Background_07'], - leftWidth: 120, - topHeight: 120, - rightWidth: 120, - bottomHeight: 120 - }); - fill.width = width; - fill.height = height; - fill.tint = fillTint; - fill.alpha = fillAlpha; - itemContainer.addChild(fill); - - let frame = null; - if (isBtn) { - frame = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Selected_10'], - leftWidth: 100, - topHeight: 120, - rightWidth: 140, - bottomHeight: 120 - }); - } else { - frame = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Selected_19'], - leftWidth: 100, - topHeight: 120, - rightWidth: 140, - bottomHeight: 120 - }); - } - - frame.width = width; - frame.height = height; - frame.alpha = 0.8; - itemContainer.addChild(frame); - - modalContainer.addChild(itemContainer); - return { container: itemContainer, fill, frame }; - } - - const modalBG = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Frame_Box_Large_12_Background'], - leftWidth: 200, - topHeight: 200, - rightWidth: 200, - bottomHeight: 200 - }); - modalBG.width = 450; - modalBG.height = 550; - modalBG.tint = LOGIN_COLORS.PANEL_BG; - modalBG.alpha = 1.0; - modalContainer.addChild(modalBG); - - const modalFrame = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Frame_Box_Large_12'], - leftWidth: 512, - topHeight: 256, - rightWidth: 512, - bottomHeight: 256 - }); - modalFrame.width = 450; - modalFrame.height = 550; - modalContainer.addChild(modalFrame); - - const avatarBox = new NineSliceSprite({ - texture: sheet.textures['SPR_SciFiMenus_Menu_Item_Background_05'], - leftWidth: 50, - topHeight: 50, - rightWidth: 50, - bottomHeight: 50 - }); - avatarBox.width = 150; - avatarBox.height = 150; - avatarBox.position.set(150, 60); - avatarBox.tint = 0x00f0ff; - avatarBox.alpha = 0.2; - modalContainer.addChild(avatarBox); - - const iconAvatar = new Sprite(sheet.textures['ICON_SciFiMenus_Menu_Solo_01_Clean']); - iconAvatar.anchor.set(0.5); - iconAvatar.position.set(225, 132.5); - iconAvatar.scale.set(0.7); - iconAvatar.tint = 0xaaaaaa; - modalContainer.addChild(iconAvatar); - - const inputName = createPixiUIItem(75, 250, 300, 50, LOGIN_COLORS.PANEL_BG, 0.8, false); - const btnMale = createPixiUIItem(75, 320, 140, 50, LOGIN_COLORS.TAB_ACTIVE, 0.6, true); - const btnFemale = createPixiUIItem(235, 320, 140, 50, LOGIN_COLORS.TAB_INACTIVE, 0.4, true); - const btnCancel = createPixiUIItem(75, 460, 140, 50, LOGIN_COLORS.BTN_DEFAULT, 0.6, true); - const btnJoin = createPixiUIItem(235, 460, 140, 50, LOGIN_COLORS.BTN_DEFAULT, 0.6, true); - - parentContainer.addChild(modalContainer); - return { - container: modalContainer, - inputName, btnMale, btnFemale, btnCancel, btnJoin - }; -} \ No newline at end of file diff --git a/lobby-frontend/src/scenes/login-scene.js b/lobby-frontend/src/scenes/login-scene.js deleted file mode 100644 index be60829..0000000 --- a/lobby-frontend/src/scenes/login-scene.js +++ /dev/null @@ -1,204 +0,0 @@ -import { Container, Sprite, NineSliceSprite, TilingSprite, Text } from "pixi.js"; -import { t } from "../i18n.js"; - -export const LOGIN_COLORS = { - PANEL_BG: 0x0b192c, - PATTERN: 0x00f0ff, - TAB_ACTIVE: 0x00f0ff, - TAB_INACTIVE: 0xaaaaaa, - TAB_HOVER: 0xffffff, - BTN_DEFAULT: 0x00aa00, - BTN_HOVER: 0x00ff00, - LOGO: 0x00f0ff -}; - -/** - * Baut alle visuellen PixiJS-Elemente für den Login-Screen. - * - * @param {Container} uiContainer - Der Haupt-Container für die Login-Ebene - * @param {Object} sheet - Das geladene Sprite-Sheet - * - * @returns {Object} - Ein Paket mit allen wichtigen Element-Referenzen für die main.js - */ -export function buildLoginScene(uiContainer, sheet) { - const frameInput = 'SPR_SciFiMenus_Menu_Item_Selected_19'; - const frameBtn = 'SPR_SciFiMenus_Menu_Item_Selected_10'; - const menuBgName = 'SPR_SciFiMenus_Menu_Item_Background_07'; - const panelBgName = 'SPR_SciFiMenus_Frame_Box_Large_02_Background'; - const patternBgName = 'SPR_SciFiMenus_Background_Hexagons_01'; - const panelFmName = 'SPR_SciFiMenus_Frame_Box_Large_02'; - - const iconUserName = 'ICON_SciFiMenus_Menu_Solo_01_Clean'; - const iconEmailName = 'ICON_SciFi_Map_Message'; - const iconLockName = 'ICON_SciFiMenus_Menu_Lock_Closed_01_Clean'; - const logoRingName = 'SPR_SciFiMenus_Ring_Medium_13'; - - const discordFrameName = 'SPR_SciFiMenus_Frame_Box_Large_12'; - const discordBgName = 'SPR_SciFiMenus_Frame_Box_Large_12_Background'; - const discordIconName = 'ICON_Social_Discord'; - - // --- Hilfsfunktion zum Erstellen der Input/Button-Rahmen --- - function createUIItem(x, y, width, height, frameTextureName, iconTextureName, fillTint, fillAlpha) { - const itemContainer = new Container(); - itemContainer.position.set(x, y); - - const fill = new NineSliceSprite({ - texture: sheet.textures[menuBgName], - leftWidth: 120, - topHeight: 120, - rightWidth: 120, - bottomHeight: 120 - }); - - fill.width = width; - fill.height = height; - fill.tint = fillTint; - fill.alpha = fillAlpha; - itemContainer.addChild(fill); - - const frame = new NineSliceSprite({ - texture: sheet.textures[frameTextureName], - leftWidth: 100, - topHeight: 120, - rightWidth: 140, - bottomHeight: 120 - }); - - frame.width = width; - frame.height = height; - frame.alpha = 0.8; - itemContainer.addChild(frame); - - if (iconTextureName) { - const icon = new Sprite(sheet.textures[iconTextureName]); - icon.position.set(-45, (height - (icon.height * 0.15)) / 2 - 5); - icon.scale.set(0.15); - itemContainer.addChild(icon); - } - - uiContainer.addChild(itemContainer); - return { container: itemContainer, fill, frame }; - } - - const panelBackground = new Sprite(sheet.textures[panelBgName]); - panelBackground.tint = LOGIN_COLORS.PANEL_BG; - panelBackground.width = 500; - panelBackground.height = 520; - panelBackground.position.set(0, -10); - uiContainer.addChild(panelBackground); - - const pattern = new TilingSprite({ - texture: sheet.textures[patternBgName], - width: 430, - height: 520, - }); - pattern.tileScale.set(0.3); - pattern.position.set(25, -10); - pattern.alpha = 0.05; - pattern.tint = LOGIN_COLORS.PATTERN; - uiContainer.addChild(pattern); - - const panelFrame = new NineSliceSprite({ - texture: sheet.textures[panelFmName], - leftWidth: 510, - topHeight: 510, - rightWidth: 510, - bottomHeight: 510 - }); - panelFrame.width = 500; - panelFrame.height = 500; - uiContainer.addChild(panelFrame); - - // --- Logo --- - const logoContainer = new Container(); - logoContainer.position.set(250, -120); - - const logoRing = new Sprite(sheet.textures[logoRingName]); - logoRing.anchor.set(0.5); - logoRing.tint = LOGIN_COLORS.LOGO; - logoRing.alpha = 1.0; - logoRing.scale.set(0.2); - logoContainer.addChild(logoRing); - - const logoText = new Text({ - text: 'Void-Genesis', - style: { - fontFamily: 'Logo Font', - fontSize: 50, - fill: LOGIN_COLORS.LOGO, - letterSpacing: 8, - } - }); - logoText.anchor.set(0.5); - logoContainer.addChild(logoText); - uiContainer.addChild(logoContainer); - - const tabLogin = createUIItem(65, 60, 180, 40, frameBtn, null, LOGIN_COLORS.INACTIVE, 0.6); - const tabRegister = createUIItem(255, 60, 180, 40, frameBtn, null, LOGIN_COLORS.ACTIVE, 0.4); - const inputUser = createUIItem(115, 135, 300, 50, frameInput, iconUserName, LOGIN_COLORS.PANEL_BG, 0.8); - const inputEmail = createUIItem(115, 200, 300, 50, frameInput, iconEmailName, LOGIN_COLORS.PANEL_BG, 0.8); - const inputLock = createUIItem(115, 265, 300, 50, frameInput, iconLockName, LOGIN_COLORS.PANEL_BG, 0.8); - const btnRegister = createUIItem(65, 390, 370, 55, frameBtn, null, LOGIN_COLORS.BTN_DEFAULT, 0.6); - - const discordContainer = new Container(); - discordContainer.position.set(640, 380); - - const discordBackground = new NineSliceSprite({ - texture: sheet.textures[discordBgName], - leftWidth: 0, - topHeight: 0, - rightWidth: 0, - bottomHeight: 0 - }); - discordBackground.tint = LOGIN_COLORS.PANEL_BG; - discordBackground.width = 255; - discordBackground.height = 75; - discordContainer.addChild(discordBackground); - - const discordPattern = new TilingSprite({ - texture: sheet.textures[patternBgName], - width: 255, - height: 75, - }); - discordPattern.tileScale.set(0.3); - discordPattern.tint = LOGIN_COLORS.PATTERN; - discordPattern.alpha = 0.05; - discordContainer.addChild(discordPattern); - - const discordFrame = new NineSliceSprite({ - texture: sheet.textures[discordFrameName], - leftWidth: 0, - topHeight: 0, - rightWidth: 0, - bottomHeight: 0 - }); - discordFrame.width = 280; - discordFrame.height = 85; - discordFrame.position.set(-15, -10); - discordContainer.addChild(discordFrame); - - const discordIcon = new Sprite(sheet.textures[discordIconName]); - discordIcon.position.set(10, 15); - discordIcon.scale.set(0.15); - discordIcon.tint = LOGIN_COLORS.TAB_INACTIVE; - discordContainer.addChild(discordIcon); - - const discordText = new Text({ - text: t('discord_text'), - style: { - fontFamily: 'UI Font', - fontSize: 20, - fill: LOGIN_COLORS.TAB_INACTIVE, - wordWrap: false, - wordWrapWidth: 120 - } - }); - discordText.position.set(60, 20); - discordContainer.addChild(discordText); - uiContainer.addChild(discordContainer); - - return { - tabLogin, tabRegister, inputUser, inputEmail, inputLock, btnRegister, - discordIcon, discordText, logoContainer, discordContainer, panelFrame - }; -} \ No newline at end of file diff --git a/lobby-frontend/src/ui/dropdowns.js b/lobby-frontend/src/ui/dropdowns.js deleted file mode 100644 index 317ff42..0000000 --- a/lobby-frontend/src/ui/dropdowns.js +++ /dev/null @@ -1,98 +0,0 @@ -import { setLanguage, currentLang, t } from "../i18n.js"; - -/** - * Sucht alle HTML-Elemente mir dem Attribut 'data-i18n' - * und aktualisiert ihren Textinhalt basierend auf der aktuellen Sprache. - */ -export function updateTranslations() { - const elements = document.querySelectorAll('[data-i18n]'); - elements.forEach(el => { - const key = el.getAttribute('data-i18n'); - el.innerHTML = t(key); - }); -} - -/** - * Initialisiert alle Dropdowns und die Sprachauswahl. - * - * @param {Function} updatePlaceholdersCallback - Wird aufgrufen, wenn - * die Sprache gewechselt wurde, - * um Platzhalter dynamisch anzupassen. - */ -export function initDropdowns(updatePlaceholdersCallback) { - const langToggle = document.getElementById('lang-toggle'); - const langMenu = document.getElementById('lang-menu'); - const userToggle = document.getElementById('user-toggle'); - const userMenu = document.getElementById('user-menu'); - - const closeAllDropdowns = () => { - if (langMenu) langMenu.classList.remove('show'); - if (userMenu) userMenu.classList.remove('show'); - }; - - if (langToggle && langMenu) { - langToggle.onclick = (e) => { - e.stopPropagation(); - const isShowing = langMenu.classList.contains('show'); - closeAllDropdowns(); - if (!isShowing) langMenu.classList.add('show'); - }; - langMenu.onclick = (e) => e.stopPropagation(); - } - - if (userToggle && userMenu) { - userToggle.onclick = (e) => { - e.stopPropagation(); - const isShowing = userMenu.classList.contains('show'); - closeAllDropdowns(); - if (!isShowing) userMenu.classList.add('show'); - }; - userMenu.onclick = (e) => e.stopPropagation(); - } - - document.addEventListener('click', closeAllDropdowns); - - if (langMenu) { - const langItems = langMenu.querySelectorAll('.dropdown-item'); - langItems.forEach(item => { - item.onclick = () => { - const selectedLang = item.getAttribute('data-lang'); - - document.getElementById('current-lang-text').textContent = selectedLang.toUpperCase(); - document.getElementById('current-flag').textContent = item.querySelector('.flag-icon').textContent; - - setLanguage(selectedLang); - updateTranslations(); - - if (updatePlaceholdersCallback) { - updatePlaceholdersCallback(); - } - - closeAllDropdowns(); - }; - }); - } - - const btnLogout = document.getElementById('btn-logout'); - if (btnLogout) { - btnLogout.onclick = () => { - sessionStorage.removeItem('void_access_token'); - sessionStorage.removeItem('void_refresh_token'); - localStorage.removeItem('void_refresh_token'); - sessionStorage.removeItem('void_user'); - window.location.reload(); - }; - } - - if (langMenu) { - const activeLangItem = langMenu.querySelector(`.dropdown-item[data-lang="${currentLang}"]`); - if (activeLangItem) { - const flag = activeLangItem.querySelector('.flag-icon').textContent; - const langText = document.getElementById('current-lang-text'); - const flagIcon = document.getElementById('current-flag'); - - if (langText) langText.textContent = currentLang.toUpperCase(); - if (flagIcon) flagIcon.textContent = flag; - } - } -} diff --git a/lobby-frontend/src/ui/forms.js b/lobby-frontend/src/ui/forms.js deleted file mode 100644 index 80d9007..0000000 --- a/lobby-frontend/src/ui/forms.js +++ /dev/null @@ -1,361 +0,0 @@ -import { t } from "../i18n.js"; - -// Speichert den Zustand zentral, damit die man.js darauf zugreifen kann -export const formState = { - isLoginMode: false, - isForgetPasswordMode: false, - isResetPasswordMode: false, - isOtpMode: false, - pendingVerificationEmail: '', - resetEmail: '', - cooldownTimer: null -}; - -// Hier wird die PixiJS-Referenz gespeichertm die wir beim Initialisieren übergeben bekommen. -let p = {}; - -/** - * Initialisiert die Formular-Steuerunf und regeistiert alle Event-Listener. - * - * @param {Object} pixiRefs - Ein Objekt mit allen zu steuernden PixiJS-Elementen und Farben. - */ -export function initForms(pixiRefs) { - p = pixiRefs; - - const htmlTabLogin = document.getElementById('tab-login'); - const htmlTabRegistert = document.getElementById('tab-register'); - const htmlButton = document.getElementById('btn-register'); - const htmlDiscord = document.getElementById('discord-link'); - - // --- Hover Effekte synchronisieren (HTML -> PixiJS) --- - htmlTabLogin.addEventListener('mouseenter', () => { - if (!formState.isLoginMode) { - p.tabLogin.fill.tint = p.COLOR_TAB_HOVER; - p.tabLogin.fill.alpha = 0.8; - } - }); - htmlTabLogin.addEventListener('mouseleave', () => { - if (!formState.isLoginMode) { - p.tabLogin.fill.tint = p.COLOR_TAB_INACTIVE; - p.tabLogin.fill.alpha = 0.4; - } - }); - - htmlTabRegistert.addEventListener('mouseenter', () => { - if (formState.isLoginMode) { - p.tabRegister.fill.tint = p.COLOR_TAB_HOVER; - p.tabRegister.fill.alpha = 0.8; - } - }); - htmlTabRegistert.addEventListener('mouseleave', () => { - if (formState.isLoginMode) { - p.tabRegister.fill.tint = p.COLOR_TAB_INACTIVE; - p.tabRegister.fill.alpha = 0.4; - } - }); - - htmlButton.addEventListener('mouseenter', () => { - p.btnRegister.fill.tint = p.COLOR_BTN_HOVER; - p.btnRegister.fill.alpha = 1.0; - }); - htmlButton.addEventListener('mouseleave', () => { - p.btnRegister.fill.tint = p.COLOR_BTN_DEFAULT; - p.btnRegister.fill.alpha = 0.6; - }); - - if (htmlDiscord) { - htmlDiscord.addEventListener('mouseenter', () => { - p.discordIcon.tint = p.COLOR_TAB_HOVER; - p.discordText.style.fill = p.COLOR_TAB_HOVER; - }); - htmlDiscord.addEventListener('mouseleave', () => { - p.discordIcon.tint = p.COLOR_TAB_INACTIVE; - p.discordText.style.fill = p.COLOR_TAB_INACTIVE; - }); - } - - htmlTabLogin.addEventListener('click', setLoginMode); - htmlTabRegistert.addEventListener('click', setRegisterMode); - - const forgotPasswordLink = document.getElementById('forgot-password-link'); - if (forgotPasswordLink) { - forgotPasswordLink.addEventListener('click', (e) => { - e.preventDefault(); - setForgotPasswordMode(); - }); - } - - const backLink = document.getElementById('back-link'); - if (backLink) { - backLink.addEventListener('click', (e) => { - e.preventDefault(); - formState.pendingVerificationEmail = ''; - formState.resetEmail = ''; - formState.isOtpMode = false; - formState.isForgotPasswordMode = false; - formState.isResetPasswordMode = false; - formState.isLoginMode = false; - setLoginMode(); - }); - } -} - -/** - * Leert die Formulardaten bei einem TabWechsel - */ -export function clearForm() { - document.getElementById('username').value = ''; - document.getElementById('email').value = ''; - document.getElementById('password').value = ''; - document.getElementById('remember').checked = false; - document.getElementById('agb').checked = false; - - document.getElementById('tab-container').style.display = 'block'; - p.tabLogin.container.visible = true; - p.tabRegister.container.visible = true; - - document.getElementById('username').style.display = 'block'; - p.inputUser.container.visible = true; - - const pwField = document.getElementById('password'); - pwField.style.display = 'block'; - pwField.type = 'password'; - p.inputLock.container.visible = true; - - document.getElementById('otp-instruction').style.display = 'none'; - document.getElementById('resend-otp-link').style.display = 'none'; - document.getElementById('forgot-password-link').style.display = 'none'; - document.getElementById('back-link').style.display = 'none'; - - if (formState.cooldownTimer) { - clearInterval(formState.cooldownTimer); - formState.cooldownTimer = null; - } - const resendLink = document.getElementById('resend-otp-link'); - if(resendLink) { - resendLink.style.pointerEvents = 'auto'; - resendLink.style.opacity = '1.0'; - resendLink.textContent = t('link_resend_otp'); - } - - const msgBox = document.getElementById('msg-box'); - msgBox.textContent = ''; - msgBox.style.color = 'var(--color-text-main)'; -} - -export function setLoginMode() { - if (formState.isLoginMode) return; - formState.isLoginMode = true; - clearForm(); - - p.tabLogin.fill.tint = p.COLOR_TAB_ACTIVE; - p.tabLogin.fill.alpha = 0.6; - p.tabRegister.fill.tint = p.COLOR_TAB_INACTIVE; - p.tabRegister.fill.alpha = 0.4; - - document.getElementById('tab-login').classList.add('active'); - document.getElementById('tab-register').classList.remove('active'); - - p.inputEmail.container.visible = false; - document.getElementById('group-email').style.display = 'none'; - - p.inputLock.container.y = 200; - document.getElementById('password').style.top = '200px'; - document.getElementById('forgot-password-link').style.display = 'block'; - - document.getElementById('email').required = false; - - document.getElementById('group-remember').style.display = 'flex'; - document.getElementById('group-agb').style.display = 'none'; - document.getElementById('agb').required = false; - - document.getElementById('btn-register').textContent = t('btn_login'); - document.getElementById('username').placeholder = t('ph_username_login'); - document.getElementById('password').placeholder = t('ph_password'); - document.getElementById('msg-box').textContent = ''; -} - -export function setRegisterMode() { - if (!formState.isLoginMode) return; - formState.isLoginMode = false; - clearForm(); - - p.tabRegister.fill.tint = p.COLOR_TAB_ACTIVE; - p.tabRegister.fill.alpha = 0.6; - p.tabLogin.fill.tint = p.COLOR_TAB_INACTIVE; - p.tabLogin.fill.alpha = 0.4; - - document.getElementById('tab-register').classList.add('active'); - document.getElementById('tab-login').classList.remove('active'); - - p.inputEmail.container.visible = true; - document.getElementById('group-email').style.display = 'block'; - - p.inputLock.container.y = 265; - document.getElementById('password').style.top = '265px'; - - document.getElementById('email').required = true; - - document.getElementById('group-remember').style.display = 'none'; - document.getElementById('group-agb').style.display = 'flex'; - document.getElementById('agb').required = true; - - document.getElementById('btn-register').textContent = t('btn_register'); - document.getElementById('username').placeholder = t('ph_username_reg'); - document.getElementById('email').placeholder = t('ph_email'); - document.getElementById('password').placeholder = t('ph_password'); - document.getElementById('msg-box').textContent = ''; -} - -export function setOtpMode(email) { - formState.isOtpMode = true; - formState.pendingVerificationEmail = email; - clearForm(); - - document.getElementById('tab-container').style.display = 'none'; - p.tabLogin.container.visible = false; - p.tabRegister.container.visible = false; - - document.getElementById('username').style.display = 'none'; - document.getElementById('username').required = false; - p.inputUser.container.visible = false; - - document.getElementById('group-email').style.display = 'none'; - document.getElementById('email').required = false; - p.inputEmail.container.visible = false; - const instructionEl = document.getElementById('otp-instruction'); - instructionEl.style.display = 'block'; - instructionEl.textContent = t('txt_otp_instruction'); - - document.getElementById('resend-otp-link').style.display = 'block'; - - p.inputLock.container.y = 200; - document.getElementById('password').style.top = '200px'; - document.getElementById('password').type = 'text'; - document.getElementById('password').placeholder = t('ph_otp'); - document.getElementById('password').value = ''; - - document.getElementById('group-agb').style.display = 'none'; - document.getElementById('agb').required = false; - document.getElementById('group-remember').style.display = 'none'; - - document.getElementById('msg-box').textContent = ''; - document.getElementById('btn-register').textContent = t('btn_verify'); - document.getElementById('back-link').style.display = 'block'; -} - -export function setForgotPasswordMode() { - formState.isLoginMode = false; - formState.isOtpMode = false; - formState.isForgotPasswordMode = true; - formState.isResetPasswordMode = false; - clearForm(); - - document.getElementById('tab-container').style.display = 'none'; - p.tabLogin.container.visible = false; - p.tabRegister.container.visible = false; - - document.getElementById('username').style.display = 'none'; - document.getElementById('username').required = false; - p.inputUser.container.visible = false; - - document.getElementById('password').style.display = 'none'; - document.getElementById('password').required = false; - p.inputLock.container.visible = false; - - document.getElementById('group-email').style.display = 'block'; - document.getElementById('email').type = 'email'; - document.getElementById('email').required = true; - document.getElementById('email').style.top = '200px'; - p.inputEmail.container.visible = true; - p.inputEmail.container.y = 200; - - document.getElementById('group-remember').style.display = 'none'; - document.getElementById('group-agb').style.display = 'none'; - document.getElementById('forgot-password-link').style.display = 'none'; - - const instructionEl = document.getElementById('otp-instruction'); - instructionEl.style.display = 'block'; - instructionEl.textContent = t('txt_reset_instruction_1'); - - document.getElementById('btn-register').textContent = t('btn_reset_request'); - document.getElementById('back-link').style.display = 'block'; - document.getElementById('msg-box').textContent = ''; -} - -export function setResetPasswordMode(email) { - formState.isForgotPasswordMode = false; - formState.isResetPasswordMode = true; - formState.resetEmail = email; - clearForm(); - - document.getElementById('tab-container').style.display = 'none'; - p.tabLogin.container.visible = false; - p.tabRegister.container.visible = false; - - document.getElementById('username').style.display = 'none'; - document.getElementById('username').required = false; - p.inputUser.container.visible = false; - - document.getElementById('group-email').style.display = 'block'; - document.getElementById('email').type = 'text'; - document.getElementById('email').placeholder = t('ph_otp'); - document.getElementById('email').required = true; - document.getElementById('email').style.top = '200px'; - p.inputEmail.container.y = 200; - - document.getElementById('password').style.display = 'block'; - document.getElementById('password').required = true; - document.getElementById('password').placeholder = t('ph_password'); - document.getElementById('password').style.top = '265px'; - p.inputLock.container.visible = true; - p.inputLock.container.y = 265; - - const instructionEl = document.getElementById('otp-instruction'); - instructionEl.style.display = 'block'; - instructionEl.style.top = '150px'; - instructionEl.textContent = t('txt_reset_instruction_2'); - - document.getElementById('back-link').style.display = 'block'; - document.getElementById('btn-register').textContent = t('btn_reset_password'); - document.getElementById('msg-box').textContent = ''; -} - -/** - * Startet einen visuellen Countdown auf einem übergebenen Link-Element. - * Sperrt währenddessen die Klickbarkeit - * - * @param {HTMLElement} linkElement - Das HTML A-Tag (zB resendOtpLink) - * @param {string} orginalTextKey - Der i18n Key für den ursprünglichen Text - * @param {number} durationSconds - Die Dauer in Sekunden (hier 120 für 2Minuten) - */ -export function startVisualCountdown(linkElement, orginalTextKey, durationSconds) { - if (formState.cooldownTimer) clearInterval(formState.cooldownTimer); - let remaining = durationSconds; - - linkElement.style.pointerEvents = 'none'; - linkElement.style.opacity = '0.5'; - - const updateText = () => { - const minutes = Math.floor(remaining / 60); - const seconds = remaining % 60; - const timeString = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}` - linkElement.textContent = `${t(orginalTextKey)} (${timeString})`; - }; - - updateText(); - - formState.cooldownTimer = setInterval(() => { - remaining--; - - if (remaining <= 0) { - clearInterval(formState.cooldownTimer); - formState.cooldownTimer = null; - linkElement.style.pointerEvents = 'auto'; - linkElement.style.opacity = '1.0'; - linkElement.textContent = t(orginalTextKey); - } else { - updateText(); - } - }, 1000); -} diff --git a/lobby-frontend/style.css b/lobby-frontend/style.css deleted file mode 100644 index 8796fc6..0000000 --- a/lobby-frontend/style.css +++ /dev/null @@ -1,104 +0,0 @@ -@import url('./src/css/variables.css'); -@import url('./src/css/layout.css'); -@import url('./src/css/auth.css'); -@import url('./src/css/lobby.css'); -@import url('./src/css/onboarding.css'); - -@media (max-width: 600px) { - /* --- 1. Login/Registrierungs-Formular skalieren --- */ - #register-form { - transform: translate(-50%, -50%) scale(0.7); - } - - /* --- 2. Top-Bar in der Lobby optimieren --- */ - #top-bar { - padding: 0 10px; - } - - .top-bar-left .lobby-logo { - font-size: 16px; - } - - #user-info { - gap: 10px; - } - - #lobby-username { - font-size: 14px; - max-width: 80px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - #btn-logout { - padding: 5px 10px; - font-size: 12px; - } - - /* --- 3. Footer aufräumen --- */ - #game-footer { - flex-direction: column; - height: auto; - padding: 10px 0; - gap: 8px; - background: rgba(0, 0, 0, 0.7); - } - - .footer-left, .footer-center, .footer-right { - text-align: center; - width: 100%; - } - - #discord-link { - left: 125px; - top: 520px; - } -} - -@media (max-height: 1050px) and (min-width: 601px) { - #register-form { - /* Wir schieben das HTML-Formular aus der exakten Mitte um 80px nach unten. - So hat das Logo (das in PixiJS darüber liegt) genug Luft zum Atmen. */ - top: calc(50% + 80px); - } -} - -@media (max-height: 500px) { - /* --- Formular noch kleiner machen, damit es vertikal passt --- */ - #register-form { - /* scale(0.5) macht das Formular klein genug für Handys im Querformat */ - transform: translate(-50%, -50%) scale(0.4); - top: 50%; - } - - /* --- Discord-Button wie im normalen Handy-Modus nach unten klappen --- */ - #discord-link { - left: -420px; - top: 350px; - } - - /* --- Footer extrem flach machen --- */ - /* Im Querformat haben wir in der Breite Platz, aber in der Höhe nicht. - Daher ordnen wir den Text wieder als Reihe (row) an. */ - #game-footer { - flex-direction: row; - height: 30px; - padding: 0 20px; - background: rgba(0, 0, 0, 0.7); - } - - .footer-left, .footer-center, .footer-right { - text-align: center; - width: 33%; - font-size: 10px; /* Schrift minimal verkleinern */ - } -} - -@media (max-width: 800px) and (orientation: portrait) { - #sector-list-container { - /* Erzwingt 2 Spalten auf dem Handy */ - grid-template-columns: repeat(2, 270px) !important; - justify-content: center !important; - } -} \ No newline at end of file diff --git a/lobby-frontend/vite.config.js b/lobby-frontend/vite.config.js deleted file mode 100644 index c057712..0000000 --- a/lobby-frontend/vite.config.js +++ /dev/null @@ -1,44 +0,0 @@ -// ============================================================================== -// Void-Genesis - Vite Konfiguration -// ============================================================================== - -import { defineConfig } from 'vite'; - -export default defineConfig({ - server: { - // Erlaubt Vite, Verbindungen über diese spezifische Domain anzunehmen. - // Verhindert den "Blocked request" Fehler hinter dem Nginx Proxy Manager. - allowedHosts: [ - 'dev.void-genesis.de' - ], - // 'host: true' stellt sicher, dass Vite auf 0.0.0.0 lauscht - // (haben wir zwar im Dockerfile schon als Flag, ist hier aber sicherer) - host: true, - }, - build: { - // ---------------------------------------------------------------------- - // EULA-Konformität: Base64-Zwang für alle Assets - // ---------------------------------------------------------------------- - // Wir setzen das Limit für das Inlining von Assets auf einen extrem hohen - // Wert (hier ca. 100 MB). Standardmäßig liegt dieser Wert bei 4096 (4 KB). - // Alles, was größer ist, wird von Vite normalerweise als separate Datei - // (z. B. .png oder .jpg) im Ausgabeordner gespeichert. - // - // Durch diesen hohen Wert zwingen wir Vite dazu, sämtliche importierten - // Assets zwingend als Base64-Strings direkt in die generierten JavaScript- - // oder CSS-Dateien einzubetten. Dadurch liegen nach dem Build absolut - // keine rohen, extrahierbaren Bilddateien mehr auf unserem Server. - // ---------------------------------------------------------------------- - assetsInlineLimit: 100000000, - - // Wir deaktivieren das Generieren von Source Maps für den produktiven Build. - // Das ist wichtig, da Source Maps extrem groß werden können, wenn sie - // riesige Base64-Strings enthalten. Außerdem erschwert es das Auslesen - // unserer Struktur von außen. - sourcemap: false, - - // Optional: Falls wir den Output noch weiter minimieren wollen, - // können wir hier auch die CSS-Code-Splitting-Optionen anpassen, - // aber für den Base64-Zwang reicht das assetsInlineLimit völlig aus. - } -}); \ No newline at end of file