diff --git a/package-lock.json b/package-lock.json index d2d1a585c4..eee7b97a73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -72,7 +72,7 @@ "@types/chai-as-promised": "8.0.1", "@types/eslint__js": "8.42.3", "@types/expect": "24.3.0", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "@types/fs-extra": "11.0.4", "@types/he": "1.2.3", "@types/jest": "29.5.13", @@ -87,7 +87,7 @@ "@types/safe-regex": "1.1.6", "@types/sinon": "17.0.3", "@types/sinon-chai": "3.2.12", - "@types/supertest": "2.0.16", + "@types/supertest": "6.0.2", "@types/uuid": "10.0.0", "chai": "4.5.0", "chai-as-promised": "8.0.0", @@ -1253,10 +1253,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", + "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -1264,27 +1266,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.7", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", + "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1304,10 +1307,12 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.25.7", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", + "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.0", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -1338,11 +1343,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -1411,24 +1417,25 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1448,8 +1455,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "engines": { "node": ">=6.9.0" } @@ -1507,22 +1515,25 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } @@ -1540,35 +1551,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1831,10 +1830,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1930,10 +1930,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -3091,26 +3092,28 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -3119,13 +3122,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -21298,6 +21300,19 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/types/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "license": "MIT", @@ -22350,13 +22365,13 @@ } }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } @@ -22371,6 +22386,18 @@ "@types/send": "*" } }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, "node_modules/@types/find-cache-dir": { "version": "3.2.1", "dev": true, @@ -22865,11 +22892,14 @@ } }, "node_modules/@types/supertest": { - "version": "2.0.16", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", + "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", "dev": true, "license": "MIT", "dependencies": { - "@types/superagent": "*" + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" } }, "node_modules/@types/tedious": { @@ -24079,27 +24109,6 @@ "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "1.9.3", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ansi-styles/node_modules/color-name": { - "version": "1.1.3", - "license": "MIT" - }, "node_modules/anymatch": { "version": "3.1.3", "license": "ISC", @@ -25464,25 +25473,6 @@ "node": ">= 16" } }, - "node_modules/chalk": { - "version": "2.4.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/char-regex": { "version": "1.0.2", "license": "MIT", @@ -30380,6 +30370,19 @@ "version": "1.0.0", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -38670,6 +38673,20 @@ "node": ">=18" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/polished": { "version": "4.3.1", "license": "MIT", @@ -43492,13 +43509,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "license": "MIT", @@ -43842,8 +43852,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.19.1", - "license": "MIT", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", "optional": true, "peer": true, "dependencies": { @@ -45363,6 +45374,18 @@ } } }, + "node_modules/webpack-dev-server/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, "node_modules/webpack-dev-server/node_modules/glob": { "version": "7.2.3", "license": "ISC", @@ -46972,6 +46995,19 @@ "url": "https://opencollective.com/storybook" } }, + "packages/lib-components/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, "packages/lib-components/node_modules/@types/jest": { "version": "28.1.8", "dev": true, @@ -47434,7 +47470,7 @@ "devDependencies": { "@types/cookie-parser": "1.4.7", "@types/cors": "2.8.17", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "supertest": "6.3.4" } }, diff --git a/package.json b/package.json index 28088651a1..6a951e3875 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@types/chai-as-promised": "8.0.1", "@types/eslint__js": "8.42.3", "@types/expect": "24.3.0", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "@types/fs-extra": "11.0.4", "@types/he": "1.2.3", "@types/jest": "29.5.13", @@ -63,7 +63,7 @@ "@types/safe-regex": "1.1.6", "@types/sinon": "17.0.3", "@types/sinon-chai": "3.2.12", - "@types/supertest": "2.0.16", + "@types/supertest": "6.0.2", "@types/uuid": "10.0.0", "chai": "4.5.0", "chai-as-promised": "8.0.0", diff --git a/packages/app-api/src/controllers/BanController.ts b/packages/app-api/src/controllers/BanController.ts index 56667ce2eb..65c0a588a7 100644 --- a/packages/app-api/src/controllers/BanController.ts +++ b/packages/app-api/src/controllers/BanController.ts @@ -3,7 +3,7 @@ import { ITakaroQuery } from '@takaro/db'; import { APIOutput, apiResponse } from '@takaro/http'; import { BanCreateDTO, BanOutputDTO, BanUpdateDTO } from '../service/Ban/dto.js'; import { AuthenticatedRequest, AuthService } from '../service/AuthService.js'; -import { Body, Get, Post, JsonController, UseBefore, Req, Params, Res, Put } from 'routing-controllers'; +import { Body, Get, Post, JsonController, UseBefore, Req, Params, Res, Put, Delete } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { ParamId } from '../lib/validators.js'; import { PERMISSIONS } from '@takaro/auth'; @@ -62,11 +62,16 @@ export class BanSearchInputDTO extends ITakaroQuery { @OpenAPI({ security: [{ domainAuth: [] }], + tags: ['Player'], }) -@JsonController() +@JsonController('/player') export class BanController { @UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS])) @ResponseSchema(BanOutputArrayDTOAPI) + @OpenAPI({ + description: 'Search for bans', + summary: 'Search for bans', + }) @Post('/ban/search') async search(@Req() req: AuthenticatedRequest, @Res() res: Response, @Body() query: BanSearchInputDTO) { const service = new BanService(req.domainId); @@ -84,6 +89,10 @@ export class BanController { @UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS])) @ResponseSchema(BanOutputDTOAPI) + @OpenAPI({ + description: 'Get a single ban', + summary: 'Get a single ban', + }) @Get('/ban/:id') async getOne(@Req() req: AuthenticatedRequest, @Params() params: ParamId) { const service = new BanService(req.domainId); @@ -92,6 +101,10 @@ export class BanController { @UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS])) @ResponseSchema(BanOutputDTOAPI) + @OpenAPI({ + description: 'Create a new ban, creating a ban via the API will always make it takaro managed.', + summary: 'Ban player', + }) @Post('/ban') async create(@Req() req: AuthenticatedRequest, @Body() data: BanCreateDTO) { const service = new BanService(req.domainId); @@ -102,6 +115,10 @@ export class BanController { @UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS])) @ResponseSchema(BanOutputDTOAPI) + @OpenAPI({ + description: 'Update an existing ban, updating a ban via the API will always make it takaro managed.', + summary: 'Update ban', + }) @Put('/ban/:id') async update(@Req() req: AuthenticatedRequest, @Params() params: ParamId, @Body() data: BanUpdateDTO) { const service = new BanService(req.domainId); @@ -112,7 +129,11 @@ export class BanController { @UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS])) @ResponseSchema(APIOutput) - @Post('/ban/:id/delete') + @OpenAPI({ + description: 'Unban player. This will remove the ban from Takaro and the gameserver(s)', + summary: 'Unban player', + }) + @Delete('/ban/:id') async delete(@Req() req: AuthenticatedRequest, @Params() params: ParamId) { const service = new BanService(req.domainId); await service.delete(params.id); diff --git a/packages/app-api/src/controllers/GameServerController.ts b/packages/app-api/src/controllers/GameServerController.ts index 1a6aec458e..fbc96152df 100644 --- a/packages/app-api/src/controllers/GameServerController.ts +++ b/packages/app-api/src/controllers/GameServerController.ts @@ -461,6 +461,7 @@ export class GameServerController { @ResponseSchema(APIOutput) @OpenAPI({ description: 'Ban a player from a gameserver. Requires gameserver to be online and reachable.', + deprecated: true, }) @Post('/gameserver/:gameServerId/player/:playerId/ban') async banPlayer(@Req() req: AuthenticatedRequest, @Params() params: PogParam, @Body() data: BanPlayerInputDTO) { @@ -473,6 +474,7 @@ export class GameServerController { @ResponseSchema(APIOutput) @OpenAPI({ description: 'Unban a player from a gameserver. Requires gameserver to be online and reachable.', + deprecated: true, }) @Post('/gameserver/:gameServerId/player/:playerId/unban') async unbanPlayer(@Req() req: AuthenticatedRequest, @Params() params: PogParam) { @@ -485,6 +487,7 @@ export class GameServerController { @ResponseSchema(BanPlayerOutputDTO) @OpenAPI({ description: 'List bans for a gameserver. Requires gameserver to be online and reachable.', + deprecated: true, }) @Get('/gameserver/:id/bans') async listBans(@Req() req: AuthenticatedRequest, @Params() params: ParamId) { diff --git a/packages/app-api/src/controllers/UserController.ts b/packages/app-api/src/controllers/UserController.ts index 7b9611e273..c09369f135 100644 --- a/packages/app-api/src/controllers/UserController.ts +++ b/packages/app-api/src/controllers/UserController.ts @@ -151,7 +151,7 @@ export class UserController { } @Get('/me') - @UseBefore(AuthService.getAuthMiddleware([])) + @UseBefore(AuthService.getAuthMiddleware([], false)) @ResponseSchema(MeOutoutDTOAPI) @OpenAPI({ summary: 'Get the current logged in user', diff --git a/packages/app-api/src/controllers/__tests__/CSMMImport.integration.test.ts b/packages/app-api/src/controllers/__tests__/CSMMImport.integration.test.ts index be054dce04..8401c58a85 100644 --- a/packages/app-api/src/controllers/__tests__/CSMMImport.integration.test.ts +++ b/packages/app-api/src/controllers/__tests__/CSMMImport.integration.test.ts @@ -51,6 +51,10 @@ const tests = [ const players = (await this.client.player.playerControllerSearch()).data.data; expect(players).to.have.length(7); + + /* const listings = (await this.client.shopListing.shopListingControllerSearch()).data.data; + expect(listings).to.have.length(4); + expect(listings[0]).to.have.property('quality', null); */ }, }), new IntegrationTest({ diff --git a/packages/app-api/src/domainInit.ts b/packages/app-api/src/domainInit.ts index 8144c56268..51a8c2a8fe 100644 --- a/packages/app-api/src/domainInit.ts +++ b/packages/app-api/src/domainInit.ts @@ -40,7 +40,7 @@ async function main() { const results = await Promise.allSettled(domains.results.map(ctx.wrap('domainInit', domainInit))); const rejected = results.map((r) => (r.status === 'rejected' ? r.reason : null)).filter(Boolean); if (rejected.length) { - log.error('Failed to initialize some domains', { errors: rejected }); + log.error('Failed to initialize some domains', { errors: JSON.stringify(rejected) }); } process.exit(0); diff --git a/packages/app-api/src/middlewares/__tests__/adminAuth.unit.test.ts b/packages/app-api/src/middlewares/__tests__/adminAuth.unit.test.ts index fabca25acd..b34b7ae295 100644 --- a/packages/app-api/src/middlewares/__tests__/adminAuth.unit.test.ts +++ b/packages/app-api/src/middlewares/__tests__/adminAuth.unit.test.ts @@ -25,16 +25,13 @@ describe('adminAuth', () => { }); it('Rejects requests with no credentials', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const response = await supertest(http.expressInstance).get('/test'); - expect(response.status).to.be.equal(401); + await supertest(http.expressInstance).get('/test').expect(401); }); it('Accepts requests with valid credentials', async () => { const response = await supertest(http.expressInstance) - // @ts-expect-error Supertest typings are wrong .get('/test') + // @ts-expect-error Supertest typings are wrong .set('X-Takaro-Admin-Token', config.get('adminClientSecret')); expect(response.status).to.be.equal(200); @@ -42,8 +39,8 @@ describe('adminAuth', () => { it('Rejects requests with invalid credentials', async () => { const response = await supertest(http.expressInstance) - // @ts-expect-error Supertest typings are wrong .get('/test') + // @ts-expect-error Supertest typings are wrong .set('X-Takaro-Admin-Token', 'foobar'); expect(response.status).to.be.equal(403); }); diff --git a/packages/app-api/src/service/AuthService.ts b/packages/app-api/src/service/AuthService.ts index 1a61f36e38..c3c1212327 100644 --- a/packages/app-api/src/service/AuthService.ts +++ b/packages/app-api/src/service/AuthService.ts @@ -13,7 +13,7 @@ import { Routes, RESTGetAPICurrentUserGuildsResult } from 'discord-api-types/v10 import oauth from 'passport-oauth2'; import { DiscordService } from './DiscordService.js'; import { domainStateMiddleware } from '../middlewares/domainStateMiddleware.js'; -import { DomainService } from './DomainService.js'; +import { DOMAIN_STATES, DomainService } from './DomainService.js'; interface DiscordUserInfo { id: string; @@ -193,7 +193,15 @@ export class AuthService extends DomainScoped { log.warn(`No domain found for identity ${identity.id}`); throw new errors.UnauthorizedError(); } - domainId = domains[0].id; + // Find the first active domain + domainId = domains.find((d) => d.state === DOMAIN_STATES.ACTIVE)?.id; + + if (!domainId && domains.length) { + log.warn( + `No active domain found for identity (but domains found: ${domains.map((d) => ({ id: d.id, state: d.state })).join(',')})`, + ); + throw new errors.BadRequestError('Domain is disabled. Please contact support.'); + } // Set the domain cookie if (req.res?.cookie) @@ -215,6 +223,9 @@ export class AuthService extends DomainScoped { } catch (error) { // Not an ory session, throw a sanitized error log.warn(error); + // If we explicitly throw a BadRequestError, we want to pass it through + // So the client gets a meaningful error message + if (error instanceof errors.BadRequestError) throw error; throw new errors.UnauthorizedError(); } } @@ -250,6 +261,9 @@ export class AuthService extends DomainScoped { if (domainStateCheck) return domainStateMiddleware(req, _res, next); return next(); } catch (error) { + // If we explicitly throw a BadRequestError, we want to pass it through + // So the client gets a meaningful error message + if (error instanceof errors.BadRequestError) return next(error); log.error('Unexpected error in auth middleware', error); return next(new errors.ForbiddenError()); } diff --git a/packages/app-api/src/service/Ban/dto.ts b/packages/app-api/src/service/Ban/dto.ts index 7597de855c..f16ef73fb2 100644 --- a/packages/app-api/src/service/Ban/dto.ts +++ b/packages/app-api/src/service/Ban/dto.ts @@ -22,7 +22,8 @@ export class BanOutputDTO extends TakaroModelDTO { export class BanCreateDTO extends TakaroDTO { @IsUUID('4') - gameServerId: string; + @IsOptional() + gameServerId?: string; @IsUUID('4') playerId: string; @IsBoolean() @@ -38,6 +39,7 @@ export class BanCreateDTO extends TakaroDTO { @IsOptional() reason?: string; } + export class BanUpdateDTO extends TakaroDTO { @IsUUID('4') gameServerId: string; diff --git a/packages/app-api/src/service/Ban/index.ts b/packages/app-api/src/service/Ban/index.ts index 2d34afee74..f83a18d51d 100644 --- a/packages/app-api/src/service/Ban/index.ts +++ b/packages/app-api/src/service/Ban/index.ts @@ -32,7 +32,9 @@ export class BanService extends TakaroService { return gameServerService.banPlayer(pog.gameServerId, player.id, ban.reason, ban.until); }), diff --git a/packages/app-api/src/service/__tests__/BanService.integration.test.ts b/packages/app-api/src/service/__tests__/BanService.integration.test.ts index 0624513fc2..46de31a87f 100644 --- a/packages/app-api/src/service/__tests__/BanService.integration.test.ts +++ b/packages/app-api/src/service/__tests__/BanService.integration.test.ts @@ -1,6 +1,7 @@ import { IntegrationTest, SetupGameServerPlayers, expect } from '@takaro/test'; import { queueService } from '@takaro/queues'; import { randomUUID } from 'node:crypto'; +import { Client } from '@takaro/apiclient'; async function triggerBanSync(domainId: string, gameServerId: string) { const job = await queueService.queues.bansSync.queue.add({ domainId, gameServerId, triggerId: randomUUID() }); @@ -21,6 +22,24 @@ async function triggerBanSync(domainId: string, gameServerId: string) { } } +/** + * This function aims to simulate when an admin bans someone without using Takaro. + * So if effect, this creates bans that are not takaroManaged. + * @param gameServerId + * @param playerId + */ +async function banPlayerOutOfBand(client: Client, gameServerId: string, playerId: string, reason?: string) { + await client.gameserver.gameServerControllerExecuteCommand(gameServerId, { + command: `ban ${playerId} ${reason || 'unknown reason'}`, + }); +} + +async function unbanPlayerOutOfBand(client: Client, gameServerId: string, playerId: string) { + await client.gameserver.gameServerControllerExecuteCommand(gameServerId, { + command: `unban ${playerId}`, + }); +} + const group = 'BanService'; const tests = [ @@ -31,12 +50,9 @@ const tests = [ setup: SetupGameServerPlayers.setup, test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); - const bans = await this.client.ban.banControllerSearch({ + const bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); @@ -50,26 +66,21 @@ const tests = [ test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); // Step 1: Ban the player - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); + // Step 2: Trigger ban sync await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 3: Verify that the player is banned - let bans = await this.client.ban.banControllerSearch({ + let bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); // Step 4: Unban the player - await this.client.gameserver.gameServerControllerUnbanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await unbanPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); // Step 5: Trigger ban sync again after unban await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 6: Verify that the player is no longer banned - bans = await this.client.ban.banControllerSearch({ + bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(0); @@ -83,26 +94,20 @@ const tests = [ test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); // Step 1: Ban the player for the first time - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); // Step 2: Trigger ban sync await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 3: Verify that the player is banned - let bans = await this.client.ban.banControllerSearch({ + let bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); // Step 4: Ban the player again (same player, same game server) - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); // Step 5: Trigger ban sync again after second ban await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 6: Verify that the player is still banned - bans = await this.client.ban.banControllerSearch({ + bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); @@ -116,29 +121,21 @@ const tests = [ test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); // Step 1: Ban the player for the first time with reason1 - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - { reason: 'reason1' }, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId, 'reason1'); // Step 2: Trigger ban sync await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 3: Verify that the player is banned with reason1 - let bans = await this.client.ban.banControllerSearch({ + let bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); expect(bans.data.data[0].reason).to.equal('reason1'); // Step 4: Ban the player again with reason2 - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - { reason: 'reason2' }, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId, 'reason2'); // Step 5: Trigger ban sync again after second ban with a new reason await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 6: Verify that the player's ban exists with the new reason - bans = await this.client.ban.banControllerSearch({ + bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); @@ -153,30 +150,21 @@ const tests = [ test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); // Step 1: Ban player1 and player2 - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); - await this.client.gameserver.gameServerControllerBanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[1].playerId, - ); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); + await banPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[1].gameId); // Step 2: Trigger ban sync await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 3: Verify that both players are banned - let bans = await this.client.ban.banControllerSearch({ + let bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(2); // Step 4: Unban player1 - await this.client.gameserver.gameServerControllerUnbanPlayer( - this.setupData.gameServer1.id, - this.setupData.pogs1[0].playerId, - ); + await unbanPlayerOutOfBand(this.client, this.setupData.gameServer1.id, this.setupData.pogs1[0].gameId); // Step 5: Trigger ban sync again after unban await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); // Step 6: Verify that player1 is no longer banned, but player2 is still banned - bans = await this.client.ban.banControllerSearch({ + bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); @@ -190,7 +178,7 @@ const tests = [ setup: SetupGameServerPlayers.setup, test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); - await this.client.ban.banControllerCreate({ + await this.client.player.banControllerCreate({ gameServerId: this.setupData.gameServer1.id, playerId: this.setupData.pogs1[0].playerId, reason: 'reason', @@ -203,7 +191,7 @@ const tests = [ await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); - const bans = await this.client.ban.banControllerSearch({ + const bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); @@ -218,7 +206,7 @@ const tests = [ setup: SetupGameServerPlayers.setup, test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); - await this.client.ban.banControllerCreate({ + await this.client.player.banControllerCreate({ gameServerId: this.setupData.gameServer1.id, playerId: this.setupData.pogs1[0].playerId, reason: 'reason', @@ -231,7 +219,7 @@ const tests = [ await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); - const bans = await this.client.ban.banControllerSearch({ + const bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); @@ -246,7 +234,7 @@ const tests = [ setup: SetupGameServerPlayers.setup, test: async function () { if (!this.standardDomainId) throw new Error('Standard domain ID not found'); - await this.client.ban.banControllerCreate({ + await this.client.player.banControllerCreate({ gameServerId: this.setupData.gameServer1.id, playerId: this.setupData.pogs1[0].playerId, reason: 'reason', @@ -255,17 +243,17 @@ const tests = [ await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); - let bans = await this.client.ban.banControllerSearch({ + let bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); expect(bans.data.data.length).to.equal(1); - await this.client.ban.banControllerDelete(bans.data.data[0].id); + await this.client.player.banControllerDelete(bans.data.data[0].id); await triggerBanSync(this.standardDomainId, this.setupData.gameServer1.id); - bans = await this.client.ban.banControllerSearch({ + bans = await this.client.player.banControllerSearch({ filters: { gameServerId: [this.setupData.gameServer1.id] }, }); diff --git a/packages/app-api/src/workers/csmmImportWorker.ts b/packages/app-api/src/workers/csmmImportWorker.ts index 01a4ded52f..3aa11b9e2b 100644 --- a/packages/app-api/src/workers/csmmImportWorker.ts +++ b/packages/app-api/src/workers/csmmImportWorker.ts @@ -192,24 +192,27 @@ async function process(job: Job) { } } - const isReachable = await gameserverService.testReachability(server.id); - - if (isReachable) { - const res = await gameserverService.executeCommand(server.id, 'version'); - if (res.rawResult.includes('1CSMM_Patrons')) { - await gameserverService.update( - server.id, - new GameServerUpdateDTO({ - connectionInfo: JSON.stringify({ - host: `${data.server.ip}:${data.server.webPort.toString()}`, - adminUser: data.server.authName, - adminToken: data.server.authToken, - useTls: data.server.webPort === 443, - useCPM: true, + try { + const isReachable = await gameserverService.testReachability(server.id); + if (isReachable) { + const res = await gameserverService.executeCommand(server.id, 'version'); + if (res.rawResult.includes('1CSMM_Patrons')) { + await gameserverService.update( + server.id, + new GameServerUpdateDTO({ + connectionInfo: JSON.stringify({ + host: `${data.server.ip}:${data.server.webPort.toString()}`, + adminUser: data.server.authName, + adminToken: data.server.authToken, + useTls: data.server.webPort === 443, + useCPM: true, + }), }), - }), - ); + ); + } } + } catch (error) { + log.warn('Error while determining CPM compatiblity', error); } // Poll the item sync job until it's done @@ -231,6 +234,9 @@ async function process(job: Job) { if (job.data.options.shop) { for (const listing of data.shopListings) { const item = await itemService.find({ filters: { code: [listing.name] } }); + // CSMM stores quality null as 0... + const quality = listing.quality.toString() === '0' ? null : listing.quality; + await shopListingService.create( new ShopListingCreateDTO({ gameServerId: server.id, @@ -240,7 +246,7 @@ async function process(job: Job) { new ShopListingItemMetaInputDTO({ amount: listing.amount, itemId: item.results[0].id, - quality: listing.quality, + quality: quality, }), ], }), diff --git a/packages/lib-apiclient/src/generated/api.ts b/packages/lib-apiclient/src/generated/api.ts index 16ac5375bc..743f31a3f9 100644 --- a/packages/lib-apiclient/src/generated/api.ts +++ b/packages/lib-apiclient/src/generated/api.ts @@ -4,7 +4,7 @@ * Takaro app-api * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: development - 5199f209d75d036de130cfd1481e036535ab4d80 + * The version of the OpenAPI document: development - 113fb57eed0f5d322a110ccec38b872a84a11b26 * Contact: support@takaro.io * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -124,7 +124,7 @@ export interface BanCreateDTO { * @type {string} * @memberof BanCreateDTO */ - gameServerId: string; + gameServerId?: string; /** * * @type {string} @@ -10168,23 +10168,23 @@ export interface VariableUpdateDTO { } /** - * BanApi - axios parameter creator + * CommandApi - axios parameter creator * @export */ -export const BanApiAxiosParamCreator = function (configuration?: Configuration) { +export const CommandApiAxiosParamCreator = function (configuration?: Configuration) { return { /** - * Required permissions: `MANAGE_PLAYERS` + * Required permissions: `MANAGE_MODULES` * @summary Create - * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * @param {CommandCreateDTO} [commandCreateDTO] CommandCreateDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - banControllerCreate: async ( - banCreateDTO?: BanCreateDTO, + commandControllerCreate: async ( + commandCreateDTO?: CommandCreateDTO, options: RawAxiosRequestConfig = {}, ): Promise => { - const localVarPath = `/ban`; + const localVarPath = `/command`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -10203,7 +10203,7 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(banCreateDTO, localVarRequestOptions, configuration); + localVarRequestOptions.data = serializeDataIfNeeded(commandCreateDTO, localVarRequestOptions, configuration); return { url: toPathString(localVarUrlObj), @@ -10211,16 +10211,64 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Delete + * Required permissions: `MANAGE_MODULES` + * @summary Create argument + * @param {CommandArgumentCreateDTO} [commandArgumentCreateDTO] CommandArgumentCreateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + commandControllerCreateArgument: async ( + commandArgumentCreateDTO?: CommandArgumentCreateDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/command/argument`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded( + commandArgumentCreateDTO, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Required permissions: `READ_MODULES` + * @summary Get executions * @param {string} id + * @param {any} [success] + * @param {EventSearchInputDTO} [eventSearchInputDTO] EventSearchInputDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - banControllerDelete: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + commandControllerGetExecutions: async ( + id: string, + success?: any, + eventSearchInputDTO?: EventSearchInputDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('banControllerDelete', 'id', id); - const localVarPath = `/ban/{id}/delete`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + assertParamExists('commandControllerGetExecutions', 'id', id); + const localVarPath = `/command/{id}/executions`.replace(`{${'id'}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -10234,9 +10282,18 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) // authentication domainAuth required + if (success !== undefined) { + for (const [key, value] of Object.entries(success)) { + localVarQueryParameter[key] = value; + } + } + + localVarHeaderParameter['Content-Type'] = 'application/json'; + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded(eventSearchInputDTO, localVarRequestOptions, configuration); return { url: toPathString(localVarUrlObj), @@ -10244,16 +10301,16 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Required permissions: `READ_PLAYERS` + * Required permissions: `READ_MODULES` * @summary Get one * @param {string} id * @param {*} [options] Override http request option. * @throws {RequiredError} */ - banControllerGetOne: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + commandControllerGetOne: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('banControllerGetOne', 'id', id); - const localVarPath = `/ban/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + assertParamExists('commandControllerGetOne', 'id', id); + const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -10277,17 +10334,83 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Required permissions: `READ_PLAYERS` + * Required permissions: `MANAGE_MODULES` + * @summary Remove + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + commandControllerRemove: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('commandControllerRemove', 'id', id); + const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Required permissions: `MANAGE_MODULES` + * @summary Remove argument + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + commandControllerRemoveArgument: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('commandControllerRemoveArgument', 'id', id); + const localVarPath = `/command/argument/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Required permissions: `READ_MODULES` * @summary Search - * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO + * @param {CommandSearchInputDTO} [commandSearchInputDTO] CommandSearchInputDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - banControllerSearch: async ( - banSearchInputDTO?: BanSearchInputDTO, + commandControllerSearch: async ( + commandSearchInputDTO?: CommandSearchInputDTO, options: RawAxiosRequestConfig = {}, ): Promise => { - const localVarPath = `/ban/search`; + const localVarPath = `/command/search`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -10306,7 +10429,7 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(banSearchInputDTO, localVarRequestOptions, configuration); + localVarRequestOptions.data = serializeDataIfNeeded(commandSearchInputDTO, localVarRequestOptions, configuration); return { url: toPathString(localVarUrlObj), @@ -10314,21 +10437,21 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) }; }, /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Update + * Required permissions: `MANAGE_MODULES` + * @summary Trigger * @param {string} id - * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO + * @param {CommandTriggerDTO} [commandTriggerDTO] CommandTriggerDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - banControllerUpdate: async ( + commandControllerTrigger: async ( id: string, - banUpdateDTO?: BanUpdateDTO, + commandTriggerDTO?: CommandTriggerDTO, options: RawAxiosRequestConfig = {}, ): Promise => { // verify required parameter 'id' is not null or undefined - assertParamExists('banControllerUpdate', 'id', id); - const localVarPath = `/ban/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + assertParamExists('commandControllerTrigger', 'id', id); + const localVarPath = `/command/{id}/trigger`.replace(`{${'id'}}`, encodeURIComponent(String(id))); // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); let baseOptions; @@ -10336,7 +10459,7 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options }; + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; @@ -10347,650 +10470,63 @@ export const BanApiAxiosParamCreator = function (configuration?: Configuration) setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(banUpdateDTO, localVarRequestOptions, configuration); + localVarRequestOptions.data = serializeDataIfNeeded(commandTriggerDTO, localVarRequestOptions, configuration); return { url: toPathString(localVarUrlObj), options: localVarRequestOptions, }; }, - }; -}; - -/** - * BanApi - functional programming interface - * @export - */ -export const BanApiFp = function (configuration?: Configuration) { - const localVarAxiosParamCreator = BanApiAxiosParamCreator(configuration); - return { /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Create - * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * Required permissions: `MANAGE_MODULES` + * @summary Update + * @param {string} id + * @param {CommandUpdateDTO} [commandUpdateDTO] CommandUpdateDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async banControllerCreate( - banCreateDTO?: BanCreateDTO, - options?: RawAxiosRequestConfig, - ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerCreate(banCreateDTO, options); - const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = - operationServerMap['BanApi.banControllerCreate']?.[localVarOperationServerIndex]?.url; - return (axios, basePath) => - createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration, - )(axios, localVarOperationServerBasePath || basePath); + commandControllerUpdate: async ( + id: string, + commandUpdateDTO?: CommandUpdateDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('commandControllerUpdate', 'id', id); + const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded(commandUpdateDTO, localVarRequestOptions, configuration); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; }, /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Delete + * Required permissions: `MANAGE_MODULES` + * @summary Update argument * @param {string} id + * @param {CommandArgumentUpdateDTO} [commandArgumentUpdateDTO] CommandArgumentUpdateDTO * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async banControllerDelete( - id: string, - options?: RawAxiosRequestConfig, - ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerDelete(id, options); - const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = - operationServerMap['BanApi.banControllerDelete']?.[localVarOperationServerIndex]?.url; - return (axios, basePath) => - createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration, - )(axios, localVarOperationServerBasePath || basePath); - }, - /** - * Required permissions: `READ_PLAYERS` - * @summary Get one - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async banControllerGetOne( - id: string, - options?: RawAxiosRequestConfig, - ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerGetOne(id, options); - const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = - operationServerMap['BanApi.banControllerGetOne']?.[localVarOperationServerIndex]?.url; - return (axios, basePath) => - createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration, - )(axios, localVarOperationServerBasePath || basePath); - }, - /** - * Required permissions: `READ_PLAYERS` - * @summary Search - * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async banControllerSearch( - banSearchInputDTO?: BanSearchInputDTO, - options?: RawAxiosRequestConfig, - ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerSearch(banSearchInputDTO, options); - const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = - operationServerMap['BanApi.banControllerSearch']?.[localVarOperationServerIndex]?.url; - return (axios, basePath) => - createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration, - )(axios, localVarOperationServerBasePath || basePath); - }, - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Update - * @param {string} id - * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - async banControllerUpdate( - id: string, - banUpdateDTO?: BanUpdateDTO, - options?: RawAxiosRequestConfig, - ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerUpdate(id, banUpdateDTO, options); - const localVarOperationServerIndex = configuration?.serverIndex ?? 0; - const localVarOperationServerBasePath = - operationServerMap['BanApi.banControllerUpdate']?.[localVarOperationServerIndex]?.url; - return (axios, basePath) => - createRequestFunction( - localVarAxiosArgs, - globalAxios, - BASE_PATH, - configuration, - )(axios, localVarOperationServerBasePath || basePath); - }, - }; -}; - -/** - * BanApi - factory interface - * @export - */ -export const BanApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { - const localVarFp = BanApiFp(configuration); - return { - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Create - * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - banControllerCreate(banCreateDTO?: BanCreateDTO, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.banControllerCreate(banCreateDTO, options).then((request) => request(axios, basePath)); - }, - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Delete - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - banControllerDelete(id: string, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.banControllerDelete(id, options).then((request) => request(axios, basePath)); - }, - /** - * Required permissions: `READ_PLAYERS` - * @summary Get one - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - banControllerGetOne(id: string, options?: RawAxiosRequestConfig): AxiosPromise { - return localVarFp.banControllerGetOne(id, options).then((request) => request(axios, basePath)); - }, - /** - * Required permissions: `READ_PLAYERS` - * @summary Search - * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - banControllerSearch( - banSearchInputDTO?: BanSearchInputDTO, - options?: RawAxiosRequestConfig, - ): AxiosPromise { - return localVarFp.banControllerSearch(banSearchInputDTO, options).then((request) => request(axios, basePath)); - }, - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Update - * @param {string} id - * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - banControllerUpdate( - id: string, - banUpdateDTO?: BanUpdateDTO, - options?: RawAxiosRequestConfig, - ): AxiosPromise { - return localVarFp.banControllerUpdate(id, banUpdateDTO, options).then((request) => request(axios, basePath)); - }, - }; -}; - -/** - * BanApi - object-oriented interface - * @export - * @class BanApi - * @extends {BaseAPI} - */ -export class BanApi extends BaseAPI { - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Create - * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof BanApi - */ - public banControllerCreate(banCreateDTO?: BanCreateDTO, options?: RawAxiosRequestConfig) { - return BanApiFp(this.configuration) - .banControllerCreate(banCreateDTO, options) - .then((request) => request(this.axios, this.basePath)); - } - - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Delete - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof BanApi - */ - public banControllerDelete(id: string, options?: RawAxiosRequestConfig) { - return BanApiFp(this.configuration) - .banControllerDelete(id, options) - .then((request) => request(this.axios, this.basePath)); - } - - /** - * Required permissions: `READ_PLAYERS` - * @summary Get one - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof BanApi - */ - public banControllerGetOne(id: string, options?: RawAxiosRequestConfig) { - return BanApiFp(this.configuration) - .banControllerGetOne(id, options) - .then((request) => request(this.axios, this.basePath)); - } - - /** - * Required permissions: `READ_PLAYERS` - * @summary Search - * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof BanApi - */ - public banControllerSearch(banSearchInputDTO?: BanSearchInputDTO, options?: RawAxiosRequestConfig) { - return BanApiFp(this.configuration) - .banControllerSearch(banSearchInputDTO, options) - .then((request) => request(this.axios, this.basePath)); - } - - /** - * Required permissions: `MANAGE_PLAYERS` - * @summary Update - * @param {string} id - * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof BanApi - */ - public banControllerUpdate(id: string, banUpdateDTO?: BanUpdateDTO, options?: RawAxiosRequestConfig) { - return BanApiFp(this.configuration) - .banControllerUpdate(id, banUpdateDTO, options) - .then((request) => request(this.axios, this.basePath)); - } -} - -/** - * CommandApi - axios parameter creator - * @export - */ -export const CommandApiAxiosParamCreator = function (configuration?: Configuration) { - return { - /** - * Required permissions: `MANAGE_MODULES` - * @summary Create - * @param {CommandCreateDTO} [commandCreateDTO] CommandCreateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerCreate: async ( - commandCreateDTO?: CommandCreateDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - const localVarPath = `/command`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(commandCreateDTO, localVarRequestOptions, configuration); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Create argument - * @param {CommandArgumentCreateDTO} [commandArgumentCreateDTO] CommandArgumentCreateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerCreateArgument: async ( - commandArgumentCreateDTO?: CommandArgumentCreateDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - const localVarPath = `/command/argument`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded( - commandArgumentCreateDTO, - localVarRequestOptions, - configuration, - ); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `READ_MODULES` - * @summary Get executions - * @param {string} id - * @param {any} [success] - * @param {EventSearchInputDTO} [eventSearchInputDTO] EventSearchInputDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerGetExecutions: async ( - id: string, - success?: any, - eventSearchInputDTO?: EventSearchInputDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerGetExecutions', 'id', id); - const localVarPath = `/command/{id}/executions`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - if (success !== undefined) { - for (const [key, value] of Object.entries(success)) { - localVarQueryParameter[key] = value; - } - } - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(eventSearchInputDTO, localVarRequestOptions, configuration); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `READ_MODULES` - * @summary Get one - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerGetOne: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerGetOne', 'id', id); - const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Remove - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerRemove: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerRemove', 'id', id); - const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Remove argument - * @param {string} id - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerRemoveArgument: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerRemoveArgument', 'id', id); - const localVarPath = `/command/argument/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `READ_MODULES` - * @summary Search - * @param {CommandSearchInputDTO} [commandSearchInputDTO] CommandSearchInputDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerSearch: async ( - commandSearchInputDTO?: CommandSearchInputDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - const localVarPath = `/command/search`; - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(commandSearchInputDTO, localVarRequestOptions, configuration); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Trigger - * @param {string} id - * @param {CommandTriggerDTO} [commandTriggerDTO] CommandTriggerDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerTrigger: async ( - id: string, - commandTriggerDTO?: CommandTriggerDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerTrigger', 'id', id); - const localVarPath = `/command/{id}/trigger`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(commandTriggerDTO, localVarRequestOptions, configuration); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Update - * @param {string} id - * @param {CommandUpdateDTO} [commandUpdateDTO] CommandUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerUpdate: async ( - id: string, - commandUpdateDTO?: CommandUpdateDTO, - options: RawAxiosRequestConfig = {}, - ): Promise => { - // verify required parameter 'id' is not null or undefined - assertParamExists('commandControllerUpdate', 'id', id); - const localVarPath = `/command/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); - // use dummy base URL string because the URL constructor only accepts absolute URLs. - const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); - let baseOptions; - if (configuration) { - baseOptions = configuration.baseOptions; - } - - const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options }; - const localVarHeaderParameter = {} as any; - const localVarQueryParameter = {} as any; - - // authentication domainAuth required - - localVarHeaderParameter['Content-Type'] = 'application/json'; - - setSearchParams(localVarUrlObj, localVarQueryParameter); - let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; - localVarRequestOptions.data = serializeDataIfNeeded(commandUpdateDTO, localVarRequestOptions, configuration); - - return { - url: toPathString(localVarUrlObj), - options: localVarRequestOptions, - }; - }, - /** - * Required permissions: `MANAGE_MODULES` - * @summary Update argument - * @param {string} id - * @param {CommandArgumentUpdateDTO} [commandArgumentUpdateDTO] CommandArgumentUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - */ - commandControllerUpdateArgument: async ( + commandControllerUpdateArgument: async ( id: string, commandArgumentUpdateDTO?: CommandArgumentUpdateDTO, options: RawAxiosRequestConfig = {}, @@ -14352,6 +13888,7 @@ export const GameServerApiAxiosParamCreator = function (configuration?: Configur * @param {string} playerId * @param {BanPlayerInputDTO} [banPlayerInputDTO] BanPlayerInputDTO * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerBanPlayer: async ( @@ -14857,6 +14394,7 @@ export const GameServerApiAxiosParamCreator = function (configuration?: Configur * @summary List bans * @param {string} id * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerListBans: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { @@ -15167,6 +14705,7 @@ export const GameServerApiAxiosParamCreator = function (configuration?: Configur * @param {string} gameServerId * @param {string} playerId * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerUnbanPlayer: async ( @@ -15303,6 +14842,7 @@ export const GameServerApiFp = function (configuration?: Configuration) { * @param {string} playerId * @param {BanPlayerInputDTO} [banPlayerInputDTO] BanPlayerInputDTO * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ async gameServerControllerBanPlayer( @@ -15649,6 +15189,7 @@ export const GameServerApiFp = function (configuration?: Configuration) { * @summary List bans * @param {string} id * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ async gameServerControllerListBans( @@ -15856,6 +15397,7 @@ export const GameServerApiFp = function (configuration?: Configuration) { * @param {string} gameServerId * @param {string} playerId * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ async gameServerControllerUnbanPlayer( @@ -15954,6 +15496,7 @@ export const GameServerApiFactory = function (configuration?: Configuration, bas * @param {string} playerId * @param {BanPlayerInputDTO} [banPlayerInputDTO] BanPlayerInputDTO * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerBanPlayer( @@ -16143,6 +15686,7 @@ export const GameServerApiFactory = function (configuration?: Configuration, bas * @summary List bans * @param {string} id * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerListBans(id: string, options?: RawAxiosRequestConfig): AxiosPromise { @@ -16255,6 +15799,7 @@ export const GameServerApiFactory = function (configuration?: Configuration, bas * @param {string} gameServerId * @param {string} playerId * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} */ gameServerControllerUnbanPlayer( @@ -16317,6 +15862,7 @@ export class GameServerApi extends BaseAPI { * @param {string} playerId * @param {BanPlayerInputDTO} [banPlayerInputDTO] BanPlayerInputDTO * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} * @memberof GameServerApi */ @@ -16533,6 +16079,7 @@ export class GameServerApi extends BaseAPI { * @summary List bans * @param {string} id * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} * @memberof GameServerApi */ @@ -16664,6 +16211,7 @@ export class GameServerApi extends BaseAPI { * @param {string} gameServerId * @param {string} playerId * @param {*} [options] Override http request option. + * @deprecated * @throws {RequiredError} * @memberof GameServerApi */ @@ -18595,28 +18143,209 @@ export class ModuleApi extends BaseAPI { .then((request) => request(this.axios, this.basePath)); } - /** - * Required permissions: `MANAGE_MODULES` - * @summary Update - * @param {string} id - * @param {ModuleUpdateDTO} [moduleUpdateDTO] ModuleUpdateDTO - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof ModuleApi - */ - public moduleControllerUpdate(id: string, moduleUpdateDTO?: ModuleUpdateDTO, options?: RawAxiosRequestConfig) { - return ModuleApiFp(this.configuration) - .moduleControllerUpdate(id, moduleUpdateDTO, options) - .then((request) => request(this.axios, this.basePath)); - } -} + /** + * Required permissions: `MANAGE_MODULES` + * @summary Update + * @param {string} id + * @param {ModuleUpdateDTO} [moduleUpdateDTO] ModuleUpdateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ModuleApi + */ + public moduleControllerUpdate(id: string, moduleUpdateDTO?: ModuleUpdateDTO, options?: RawAxiosRequestConfig) { + return ModuleApiFp(this.configuration) + .moduleControllerUpdate(id, moduleUpdateDTO, options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * PlayerApi - axios parameter creator + * @export + */ +export const PlayerApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Create a new ban, creating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Ban player + * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerCreate: async ( + banCreateDTO?: BanCreateDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/player/ban`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded(banCreateDTO, localVarRequestOptions, configuration); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Unban player. This will remove the ban from Takaro and the gameserver(s) Required permissions: `MANAGE_PLAYERS` + * @summary Unban player + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerDelete: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('banControllerDelete', 'id', id); + const localVarPath = `/player/ban/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get a single ban Required permissions: `READ_PLAYERS` + * @summary Get a single ban + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerGetOne: async (id: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('banControllerGetOne', 'id', id); + const localVarPath = `/player/ban/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Search for bans Required permissions: `READ_PLAYERS` + * @summary Search for bans + * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerSearch: async ( + banSearchInputDTO?: BanSearchInputDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/player/ban/search`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication domainAuth required + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded(banSearchInputDTO, localVarRequestOptions, configuration); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update an existing ban, updating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Update ban + * @param {string} id + * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerUpdate: async ( + id: string, + banUpdateDTO?: BanUpdateDTO, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists('banControllerUpdate', 'id', id); + const localVarPath = `/player/ban/{id}`.replace(`{${'id'}}`, encodeURIComponent(String(id))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; -/** - * PlayerApi - axios parameter creator - * @export - */ -export const PlayerApiAxiosParamCreator = function (configuration?: Configuration) { - return { + // authentication domainAuth required + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; + localVarRequestOptions.data = serializeDataIfNeeded(banUpdateDTO, localVarRequestOptions, configuration); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * Required permissions: `MANAGE_PLAYERS`, `MANAGE_ROLES` * @summary Assign role @@ -18829,6 +18558,123 @@ export const PlayerApiAxiosParamCreator = function (configuration?: Configuratio export const PlayerApiFp = function (configuration?: Configuration) { const localVarAxiosParamCreator = PlayerApiAxiosParamCreator(configuration); return { + /** + * Create a new ban, creating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Ban player + * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async banControllerCreate( + banCreateDTO?: BanCreateDTO, + options?: RawAxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerCreate(banCreateDTO, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap['PlayerApi.banControllerCreate']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Unban player. This will remove the ban from Takaro and the gameserver(s) Required permissions: `MANAGE_PLAYERS` + * @summary Unban player + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async banControllerDelete( + id: string, + options?: RawAxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerDelete(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap['PlayerApi.banControllerDelete']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Get a single ban Required permissions: `READ_PLAYERS` + * @summary Get a single ban + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async banControllerGetOne( + id: string, + options?: RawAxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerGetOne(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap['PlayerApi.banControllerGetOne']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Search for bans Required permissions: `READ_PLAYERS` + * @summary Search for bans + * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async banControllerSearch( + banSearchInputDTO?: BanSearchInputDTO, + options?: RawAxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerSearch(banSearchInputDTO, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap['PlayerApi.banControllerSearch']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Update an existing ban, updating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Update ban + * @param {string} id + * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async banControllerUpdate( + id: string, + banUpdateDTO?: BanUpdateDTO, + options?: RawAxiosRequestConfig, + ): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.banControllerUpdate(id, banUpdateDTO, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap['PlayerApi.banControllerUpdate']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, /** * Required permissions: `MANAGE_PLAYERS`, `MANAGE_ROLES` * @summary Assign role @@ -18970,6 +18816,64 @@ export const PlayerApiFp = function (configuration?: Configuration) { export const PlayerApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { const localVarFp = PlayerApiFp(configuration); return { + /** + * Create a new ban, creating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Ban player + * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerCreate(banCreateDTO?: BanCreateDTO, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.banControllerCreate(banCreateDTO, options).then((request) => request(axios, basePath)); + }, + /** + * Unban player. This will remove the ban from Takaro and the gameserver(s) Required permissions: `MANAGE_PLAYERS` + * @summary Unban player + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerDelete(id: string, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.banControllerDelete(id, options).then((request) => request(axios, basePath)); + }, + /** + * Get a single ban Required permissions: `READ_PLAYERS` + * @summary Get a single ban + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerGetOne(id: string, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.banControllerGetOne(id, options).then((request) => request(axios, basePath)); + }, + /** + * Search for bans Required permissions: `READ_PLAYERS` + * @summary Search for bans + * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerSearch( + banSearchInputDTO?: BanSearchInputDTO, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp.banControllerSearch(banSearchInputDTO, options).then((request) => request(axios, basePath)); + }, + /** + * Update an existing ban, updating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Update ban + * @param {string} id + * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + banControllerUpdate( + id: string, + banUpdateDTO?: BanUpdateDTO, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp.banControllerUpdate(id, banUpdateDTO, options).then((request) => request(axios, basePath)); + }, /** * Required permissions: `MANAGE_PLAYERS`, `MANAGE_ROLES` * @summary Assign role @@ -19052,6 +18956,77 @@ export const PlayerApiFactory = function (configuration?: Configuration, basePat * @extends {BaseAPI} */ export class PlayerApi extends BaseAPI { + /** + * Create a new ban, creating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Ban player + * @param {BanCreateDTO} [banCreateDTO] BanCreateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PlayerApi + */ + public banControllerCreate(banCreateDTO?: BanCreateDTO, options?: RawAxiosRequestConfig) { + return PlayerApiFp(this.configuration) + .banControllerCreate(banCreateDTO, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Unban player. This will remove the ban from Takaro and the gameserver(s) Required permissions: `MANAGE_PLAYERS` + * @summary Unban player + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PlayerApi + */ + public banControllerDelete(id: string, options?: RawAxiosRequestConfig) { + return PlayerApiFp(this.configuration) + .banControllerDelete(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get a single ban Required permissions: `READ_PLAYERS` + * @summary Get a single ban + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PlayerApi + */ + public banControllerGetOne(id: string, options?: RawAxiosRequestConfig) { + return PlayerApiFp(this.configuration) + .banControllerGetOne(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Search for bans Required permissions: `READ_PLAYERS` + * @summary Search for bans + * @param {BanSearchInputDTO} [banSearchInputDTO] BanSearchInputDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PlayerApi + */ + public banControllerSearch(banSearchInputDTO?: BanSearchInputDTO, options?: RawAxiosRequestConfig) { + return PlayerApiFp(this.configuration) + .banControllerSearch(banSearchInputDTO, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update an existing ban, updating a ban via the API will always make it takaro managed. Required permissions: `MANAGE_PLAYERS` + * @summary Update ban + * @param {string} id + * @param {BanUpdateDTO} [banUpdateDTO] BanUpdateDTO + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PlayerApi + */ + public banControllerUpdate(id: string, banUpdateDTO?: BanUpdateDTO, options?: RawAxiosRequestConfig) { + return PlayerApiFp(this.configuration) + .banControllerUpdate(id, banUpdateDTO, options) + .then((request) => request(this.axios, this.basePath)); + } + /** * Required permissions: `MANAGE_PLAYERS`, `MANAGE_ROLES` * @summary Assign role diff --git a/packages/lib-apiclient/src/generated/base.ts b/packages/lib-apiclient/src/generated/base.ts index e7b97e21b7..3d5477ca17 100644 --- a/packages/lib-apiclient/src/generated/base.ts +++ b/packages/lib-apiclient/src/generated/base.ts @@ -4,7 +4,7 @@ * Takaro app-api * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: development - 5199f209d75d036de130cfd1481e036535ab4d80 + * The version of the OpenAPI document: development - 113fb57eed0f5d322a110ccec38b872a84a11b26 * Contact: support@takaro.io * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/lib-apiclient/src/generated/common.ts b/packages/lib-apiclient/src/generated/common.ts index 1582b1325e..3365df4d93 100644 --- a/packages/lib-apiclient/src/generated/common.ts +++ b/packages/lib-apiclient/src/generated/common.ts @@ -4,7 +4,7 @@ * Takaro app-api * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: development - 5199f209d75d036de130cfd1481e036535ab4d80 + * The version of the OpenAPI document: development - 113fb57eed0f5d322a110ccec38b872a84a11b26 * Contact: support@takaro.io * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/lib-apiclient/src/generated/configuration.ts b/packages/lib-apiclient/src/generated/configuration.ts index 9cf6237e69..1bbdefa708 100644 --- a/packages/lib-apiclient/src/generated/configuration.ts +++ b/packages/lib-apiclient/src/generated/configuration.ts @@ -4,7 +4,7 @@ * Takaro app-api * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: development - 5199f209d75d036de130cfd1481e036535ab4d80 + * The version of the OpenAPI document: development - 113fb57eed0f5d322a110ccec38b872a84a11b26 * Contact: support@takaro.io * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/lib-apiclient/src/generated/index.ts b/packages/lib-apiclient/src/generated/index.ts index ee33d9aff9..12452fd5e3 100644 --- a/packages/lib-apiclient/src/generated/index.ts +++ b/packages/lib-apiclient/src/generated/index.ts @@ -4,7 +4,7 @@ * Takaro app-api * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: development - 5199f209d75d036de130cfd1481e036535ab4d80 + * The version of the OpenAPI document: development - 113fb57eed0f5d322a110ccec38b872a84a11b26 * Contact: support@takaro.io * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/packages/lib-apiclient/src/lib/client.ts b/packages/lib-apiclient/src/lib/client.ts index 7e5496ffa5..1294f0ac46 100644 --- a/packages/lib-apiclient/src/lib/client.ts +++ b/packages/lib-apiclient/src/lib/client.ts @@ -17,7 +17,6 @@ import { StatsApi, ShopOrderApi, ShopListingApi, - BanApi, } from '../generated/api.js'; import { BaseApiClient, IBaseApiClientConfig } from './baseClient.js'; @@ -262,14 +261,4 @@ export class Client extends BaseApiClient { this.axios, ); } - - get ban() { - return new BanApi( - { - isJsonMime: this.isJsonMime, - }, - '', - this.axios, - ); - } } diff --git a/packages/lib-components/src/components/charts/GeoMercator/index.tsx b/packages/lib-components/src/components/charts/GeoMercator/index.tsx index 47c384798d..9c13f76b2e 100644 --- a/packages/lib-components/src/components/charts/GeoMercator/index.tsx +++ b/packages/lib-components/src/components/charts/GeoMercator/index.tsx @@ -1,6 +1,7 @@ import * as topojson from 'topojson-client'; import topology from './world.json'; import { Graticule, Mercator } from '@visx/geo'; +import { scaleLinear } from '@visx/scale'; import { ParentSize } from '@visx/responsive'; import { getDefaultTooltipStyles, InnerChartProps, Margin } from '../util'; @@ -9,7 +10,6 @@ import { useTooltip, Tooltip } from '@visx/tooltip'; import { Zoom } from '@visx/zoom'; import { useCallback } from 'react'; import { localPoint } from '@visx/event'; -import { shade } from 'polished'; import { ZoomControls } from '../ZoomControls'; import { alpha2ToAlpha3 } from './iso3166-alpha2-to-alpha3'; @@ -88,6 +88,11 @@ const Chart = ({ const centerY = height / 2 + 150; const scale = (width / 1000) * 100; + const colorScale = scaleLinear({ + domain: [Math.min(...data.map((d) => yAccessor(d))), Math.max(...data.map((d) => yAccessor(d)))], + range: [theme.colors.backgroundAlt, theme.colors.primary], + }); + const handleTooltip = useCallback( (event: React.TouchEvent | React.MouseEvent, countryData: T | undefined) => { const eventSvgCoords = localPoint(event); @@ -139,13 +144,15 @@ const Chart = ({ return xAccessor(d) === feature.id; }); + const fillColor = countryData ? colorScale(yAccessor(countryData)) : theme.colors.backgroundAlt; + return ( handleTooltip(e, countryData)} onTouchStart={(e) => handleTooltip(e, countryData)} diff --git a/packages/lib-db/src/migrations/sql/20241030130613-index-for-roleId.ts b/packages/lib-db/src/migrations/sql/20241030130613-index-for-roleId.ts new file mode 100644 index 0000000000..0214822e44 --- /dev/null +++ b/packages/lib-db/src/migrations/sql/20241030130613-index-for-roleId.ts @@ -0,0 +1,31 @@ +import { Knex } from 'knex'; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('permissionOnRole', (table) => { + table.index(['roleId']); + }); + + await knex.schema.alterTable('playerOnGameServer', (table) => { + table.index(['domain', 'playerId']); + }); + + await knex.schema.alterTable('playerInventory', (table) => { + table.dropIndex(['playerId', 'domain']); + table.index(['playerId', 'domain', 'itemId']); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('permissionOnRole', (table) => { + table.dropIndex(['roleId']); + }); + + await knex.schema.alterTable('playerOnGameServer', (table) => { + table.dropIndex(['domain', 'playerId']); + }); + + await knex.schema.alterTable('playerInventory', (table) => { + table.dropIndex(['playerId', 'domain', 'itemId']); + table.index(['playerId', 'domain']); + }); +} diff --git a/packages/lib-gameserver/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts b/packages/lib-gameserver/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts index 3521cb7b5c..f29747aa5f 100644 --- a/packages/lib-gameserver/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts +++ b/packages/lib-gameserver/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts @@ -34,7 +34,8 @@ describe('7d2d Actions', () => { expect(result).to.be.an('array'); expect(result).to.have.lengthOf(1); - expect(result[0].player.gameId).to.equal('EOS_00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[0].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[0].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); expect(result[0].expiresAt).to.equal('2023-06-29T19:39:56.000Z'); }); @@ -51,10 +52,12 @@ describe('7d2d Actions', () => { expect(result).to.be.an('array'); expect(result).to.have.lengthOf(2); - expect(result[0].player.gameId).to.equal('EOS_0002e0daea3b493fa146ce6d06e79a57'); + expect(result[0].player.gameId).to.equal('0002e0daea3b493fa146ce6d06e79a57'); + expect(result[0].player.epicOnlineServicesId).to.equal('0002e0daea3b493fa146ce6d06e79a57'); expect(result[0].expiresAt).to.equal('2028-06-29T17:49:45.000Z'); - expect(result[1].player.gameId).to.equal('EOS_00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[1].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[1].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); expect(result[1].expiresAt).to.equal('2028-06-29T19:19:40.000Z'); }); @@ -84,7 +87,8 @@ describe('7d2d Actions', () => { expect(result).to.be.an('array'); expect(result).to.have.lengthOf(1); - expect(result[0].player.gameId).to.equal('EOS_00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[0].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); + expect(result[0].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225'); expect(result[0].expiresAt).to.equal('2023-06-29T19:40:59.000Z'); }); }); diff --git a/packages/lib-gameserver/src/gameservers/7d2d/index.ts b/packages/lib-gameserver/src/gameservers/7d2d/index.ts index 50e93e67b6..d1218eac80 100644 --- a/packages/lib-gameserver/src/gameservers/7d2d/index.ts +++ b/packages/lib-gameserver/src/gameservers/7d2d/index.ts @@ -195,7 +195,7 @@ export class SevenDaysToDie implements IGameServer { async banPlayer(options: BanDTO) { // If no expiresAt is provided, assume 'permanent'. 500 years is pretty long ;) - const expiresAt = options.expiresAt ?? '2521-01-01 00:00:00'; + const expiresAt = options.expiresAt ?? '2521-01-01T00:00:00.000'; const expiresAtDate = DateTime.fromISO(expiresAt); const now = DateTime.local(); @@ -258,10 +258,13 @@ export class SevenDaysToDie implements IGameServer { if (match) { const [, date, gameId, _displayName, reason] = match; const expiresAt = date.replace(' ', 'T') + '.000Z'; // Keep the time in its original form + // If the saved ban isn't saved with EOS, we cannot resolve to gameId, so skip these. + if (!gameId.includes('EOS_')) continue; bans.push( new BanDTO({ - player: new IPlayerReferenceDTO({ - gameId, + player: new IGamePlayer({ + gameId: gameId.replace('EOS_', ''), + epicOnlineServicesId: gameId.replace('EOS_', ''), }), reason, expiresAt, diff --git a/packages/lib-gameserver/src/gameservers/rust/index.ts b/packages/lib-gameserver/src/gameservers/rust/index.ts index 33268771f7..3d79fa8df5 100644 --- a/packages/lib-gameserver/src/gameservers/rust/index.ts +++ b/packages/lib-gameserver/src/gameservers/rust/index.ts @@ -175,7 +175,7 @@ export class Rust implements IGameServer { async listBans(): Promise { const response = await this.executeConsoleCommand('banlistex'); - if (!response.success || !response.rawResult) { + if (!response.rawResult) { return []; } @@ -200,8 +200,9 @@ export class Rust implements IGameServer { const ban = new BanDTO({ reason: match.groups.reason, - player: new IPlayerReferenceDTO({ + player: new IGamePlayer({ gameId, + steamId: gameId, }), expiresAt, }); diff --git a/packages/lib-http/package.json b/packages/lib-http/package.json index 002cb80e6c..901d4e0ffe 100644 --- a/packages/lib-http/package.json +++ b/packages/lib-http/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@types/cookie-parser": "1.4.7", "@types/cors": "2.8.17", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "supertest": "6.3.4" } } diff --git a/packages/lib-http/src/controllers/__tests__/meta.integration.test.ts b/packages/lib-http/src/controllers/__tests__/meta.integration.test.ts index 81ba06515f..4b12143752 100644 --- a/packages/lib-http/src/controllers/__tests__/meta.integration.test.ts +++ b/packages/lib-http/src/controllers/__tests__/meta.integration.test.ts @@ -1,6 +1,5 @@ import { HTTP } from '../../app.js'; import supertest from 'supertest'; -import { expect } from '@takaro/test'; describe('app', () => { let http: HTTP; @@ -14,16 +13,10 @@ describe('app', () => { }); it('Serves a health status', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const response = await supertest(http.expressInstance).get('/healthz'); - expect(response.status).to.be.equal(200); + await supertest(http.expressInstance).get('/healthz').expect(200); }); it('Serves a open api spec', async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const response = await supertest(http.expressInstance).get('/openapi.json'); - expect(response.status).to.be.equal(200); + await supertest(http.expressInstance).get('/openapi.json').expect(200); }); }); diff --git a/packages/lib-http/src/middleware/__tests__/rateLimit.integration.test.ts b/packages/lib-http/src/middleware/__tests__/rateLimit.integration.test.ts index f78dec927b..0cc6539f44 100644 --- a/packages/lib-http/src/middleware/__tests__/rateLimit.integration.test.ts +++ b/packages/lib-http/src/middleware/__tests__/rateLimit.integration.test.ts @@ -1,6 +1,5 @@ import { Response, NextFunction, Request } from 'express'; import { Redis } from '@takaro/db'; -import { expect } from '@takaro/test'; import { Controller, UseBefore, Get } from 'routing-controllers'; import { HTTP } from '../../main.js'; import { createRateLimitMiddleware } from '../rateLimit.js'; @@ -112,17 +111,19 @@ describe('rateLimit middleware', () => { const agent = supertest(http.expressInstance); for (let i = 1; i < 5; i++) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const res = await agent.get('/low-limit').expect(200); - expect(res.header['x-ratelimit-remaining']).to.equal((5 - i).toString()); - expect(res.header['x-ratelimit-limit']).to.equal('5'); - expect(res.header['x-ratelimit-reset']).to.be.a('string'); + await agent + .get('/low-limit') + .expect(200) + .expect('x-ratelimit-remaining', (5 - i).toString()) + .expect('x-ratelimit-limit', '5') + .expect('x-ratelimit-reset', /\d+/); } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const res = await agent.get('/low-limit').expect(429); - expect(res.header['x-ratelimit-remaining']).to.equal('0'); - expect(res.header['x-ratelimit-limit']).to.equal('5'); + + agent + .get('/low-limit') + .expect(429) + .expect('x-ratelimit-remaining', '0') + .expect('x-ratelimit-limit', '5') + .expect('x-ratelimit-reset', /\d+/); }); }); diff --git a/packages/lib-http/src/middleware/errorHandler.ts b/packages/lib-http/src/middleware/errorHandler.ts index 43bda7f56a..8d0aa86446 100644 --- a/packages/lib-http/src/middleware/errorHandler.ts +++ b/packages/lib-http/src/middleware/errorHandler.ts @@ -85,5 +85,5 @@ export async function ErrorHandler( } res.status(status).json(apiResponse({}, { error: parsedError, req, res })); - return res.end(); + res.end(); } diff --git a/packages/lib-http/src/middleware/logger.ts b/packages/lib-http/src/middleware/logger.ts index 3a4508a540..1762e0cfed 100644 --- a/packages/lib-http/src/middleware/logger.ts +++ b/packages/lib-http/src/middleware/logger.ts @@ -10,7 +10,7 @@ const log = logger('http'); * This middleware is called very early in the request lifecycle, so it's * we leverage this fact to inject the context tracking at this stage */ -export const LoggingMiddleware = ctx.wrap('HTTP', loggingMiddleware); +export const LoggingMiddleware = ctx.wrap('HTTP', loggingMiddleware) as typeof loggingMiddleware; async function loggingMiddleware(req: Request, res: Response, next: NextFunction) { if (HIDDEN_ROUTES.some((route) => req.originalUrl.startsWith(route))) { diff --git a/packages/lib-modules/src/main.ts b/packages/lib-modules/src/main.ts index 4300cc49be..e2d76231f2 100644 --- a/packages/lib-modules/src/main.ts +++ b/packages/lib-modules/src/main.ts @@ -1,5 +1,6 @@ import { BuiltinModule } from './BuiltinModule.js'; import { ChatBridge } from './modules/chatBridge/index.js'; +import { DailyRewards } from './modules/dailyRewards/index.js'; import { EconomyUtils } from './modules/economyUtils/index.js'; import { GeoBlock } from './modules/geoBlock/index.js'; import { Gimme } from './modules/gimme/index.js'; @@ -30,6 +31,7 @@ export function getModules(): Array> { new Lottery(), new GeoBlock(), new TimedShutdown(), + new DailyRewards(), ]; } diff --git a/packages/lib-modules/src/modules/dailyRewards/commands/daily.js b/packages/lib-modules/src/modules/dailyRewards/commands/daily.js new file mode 100644 index 0000000000..736b75a776 --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/commands/daily.js @@ -0,0 +1,118 @@ +import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers'; +import { DAILY_KEY, STREAK_KEY, getMultiplier } from './utils.js'; + +async function main() { + const { pog, gameServerId, module: mod } = data; + + if (!checkPermission(pog, 'DAILY_CLAIM')) { + throw new TakaroUserError('You do not have permission to claim daily rewards.'); + } + + // Get last claim time + const lastClaimRes = await takaro.variable.variableControllerSearch({ + filters: { + key: [DAILY_KEY], + gameServerId: [gameServerId], + playerId: [pog.playerId], + moduleId: [mod.moduleId], + }, + }); + + const now = new Date(); + let streak = 1; + + if (lastClaimRes.data.data.length > 0) { + const lastClaim = new Date(JSON.parse(lastClaimRes.data.data[0].value)); + const hoursSinceLastClaim = (now - lastClaim) / (1000 * 60 * 60); + + // Check if 24 hours have passed + if (hoursSinceLastClaim < 24) { + const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000); + throw new TakaroUserError(`You can claim your next reward at ${nextClaimTime.toLocaleString()}`); + } + + // Get current streak + const streakRes = await takaro.variable.variableControllerSearch({ + filters: { + key: [STREAK_KEY], + gameServerId: [gameServerId], + playerId: [pog.playerId], + moduleId: [mod.moduleId], + }, + }); + + if (streakRes.data.data.length > 0) { + // If claimed within 48 hours, increment streak + if (hoursSinceLastClaim < 48) { + streak = Math.min(JSON.parse(streakRes.data.data[0].value) + 1, mod.userConfig.maxStreak); + await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, { + value: JSON.stringify(streak), + }); + } else { + // Reset streak if more than 48 hours + await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, { + value: JSON.stringify(1), + }); + } + } else { + // Create new streak record + await takaro.variable.variableControllerCreate({ + key: STREAK_KEY, + value: JSON.stringify(1), + gameServerId, + playerId: pog.playerId, + moduleId: mod.moduleId, + }); + } + + // Update last claim time + await takaro.variable.variableControllerUpdate(lastClaimRes.data.data[0].id, { + value: JSON.stringify(now), + }); + } else { + // First time claim + await takaro.variable.variableControllerCreate({ + key: DAILY_KEY, + value: JSON.stringify(now), + gameServerId, + playerId: pog.playerId, + moduleId: mod.moduleId, + }); + await takaro.variable.variableControllerCreate({ + key: STREAK_KEY, + value: JSON.stringify(1), + gameServerId, + playerId: pog.playerId, + moduleId: mod.moduleId, + }); + } + + const multiplier = await getMultiplier(pog); + const baseReward = mod.userConfig.baseReward * streak * multiplier; + let bonusReward = 0; + let milestoneMessage = ''; + + // Check for milestones + for (const milestone of mod.userConfig.milestoneRewards) { + if (streak === milestone.days) { + bonusReward = milestone.reward; + milestoneMessage = `\n${milestone.message}`; + break; + } + } + + // Award total rewards + const totalReward = baseReward + bonusReward; + await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, { + currency: totalReward, + }); + + const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value; + await pog.pm( + `Daily reward claimed! You received ${totalReward} ${currencyName}\n` + + `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x bonus!)` : ''}` + + milestoneMessage, + ); +} + +await main(); diff --git a/packages/lib-modules/src/modules/dailyRewards/commands/streak.js b/packages/lib-modules/src/modules/dailyRewards/commands/streak.js new file mode 100644 index 0000000000..587cb3f7c2 --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/commands/streak.js @@ -0,0 +1,42 @@ +import { data, takaro } from '@takaro/helpers'; +import { getPlayerStreak, getLastClaim, getMultiplier } from './utils.js'; + +async function main() { + const { pog, gameServerId, module: mod } = data; + + const streak = await getPlayerStreak(gameServerId, pog.playerId, mod.moduleId); + const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId); + const multiplier = await getMultiplier(pog); + const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value; + + if (!streak || !lastClaim) { + await pog.pm(`You haven't claimed any daily rewards yet! Use ${prefix}daily to get started.`); + return; + } + + const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000); + const now = new Date(); + const canClaim = now >= nextClaimTime; + + // Find next milestone + let nextMilestone = null; + for (const milestone of mod.userConfig.milestoneRewards) { + if (milestone.days > streak) { + nextMilestone = milestone; + break; + } + } + + let message = `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x donor bonus!)` : ''}\n`; + message += canClaim + ? `Your daily reward is available! Use ${prefix}daily to claim it!\n` + : `Next reward available at: ${nextClaimTime.toLocaleString()}\n`; + + if (nextMilestone) { + message += `\n🎯 Next milestone: ${nextMilestone.days} days (${nextMilestone.days - streak} days to go!)`; + } + + await pog.pm(message); +} + +await main(); diff --git a/packages/lib-modules/src/modules/dailyRewards/commands/topstreak.js b/packages/lib-modules/src/modules/dailyRewards/commands/topstreak.js new file mode 100644 index 0000000000..ab15fc1969 --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/commands/topstreak.js @@ -0,0 +1,54 @@ +import { takaro, data } from '@takaro/helpers'; +import { STREAK_KEY } from './utils.js'; + +async function main() { + const { pog, gameServerId, module: mod, arguments: args } = data; + + // Limit count to reasonable number + const count = Math.min(Math.max(1, args.count), 50); + + // Get all streaks + const streaksRes = await takaro.variable.variableControllerSearch({ + filters: { + key: [STREAK_KEY], + gameServerId: [gameServerId], + moduleId: [mod.moduleId], + }, + limit: 1000, // Get all possible streaks + }); + + if (streaksRes.data.data.length === 0) { + await pog.pm('No players have started their daily streak yet!'); + return; + } + + // Sort by streak value + const sortedStreaks = streaksRes.data.data + .map((record) => ({ + playerId: record.playerId, + streak: JSON.parse(record.value), + })) + .sort((a, b) => b.streak - a.streak) + .slice(0, count); + + // Get player names + const playerDetails = await Promise.all( + sortedStreaks.map(async (record) => { + const player = (await takaro.player.playerControllerGetOne(record.playerId)).data.data; + return { + name: player.name, + streak: record.streak, + }; + }), + ); + + // Build message + let message = `Top ${count} Daily Streaks:\n\n`; + playerDetails.forEach((player, index) => { + message += `${index + 1}. ${player.name}: ${player.streak} days\n`; + }); + + await pog.pm(message); +} + +await main(); diff --git a/packages/lib-modules/src/modules/dailyRewards/functions/utils.js b/packages/lib-modules/src/modules/dailyRewards/functions/utils.js new file mode 100644 index 0000000000..b876c3bea8 --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/functions/utils.js @@ -0,0 +1,36 @@ +import { takaro, checkPermission } from '@takaro/helpers'; + +export const DAILY_KEY = 'daily_timestamp'; +export const STREAK_KEY = 'daily_streak'; + +export async function getMultiplier(pog) { + const perm = checkPermission(pog, 'DAILY_REWARD_MULTIPLIER'); + if (perm) return perm.count; + return 1; +} + +export async function getPlayerStreak(gameServerId, playerId, moduleId) { + const streakRes = await takaro.variable.variableControllerSearch({ + filters: { + key: [STREAK_KEY], + gameServerId: [gameServerId], + playerId: [playerId], + moduleId: [moduleId], + }, + }); + + return streakRes.data.data.length ? parseInt(JSON.parse(streakRes.data.data[0].value)) : 0; +} + +export async function getLastClaim(gameServerId, playerId, moduleId) { + const lastClaimRes = await takaro.variable.variableControllerSearch({ + filters: { + key: [DAILY_KEY], + gameServerId: [gameServerId], + playerId: [playerId], + moduleId: [moduleId], + }, + }); + + return lastClaimRes.data.data.length ? new Date(JSON.parse(lastClaimRes.data.data[0].value)) : null; +} diff --git a/packages/lib-modules/src/modules/dailyRewards/hooks/dailyLoginCheck.js b/packages/lib-modules/src/modules/dailyRewards/hooks/dailyLoginCheck.js new file mode 100644 index 0000000000..3d80bf2f8d --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/hooks/dailyLoginCheck.js @@ -0,0 +1,24 @@ +import { data, takaro } from '@takaro/helpers'; +import { getLastClaim } from './utils.js'; + +async function main() { + const { pog, gameServerId, module: mod } = data; + const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value; + + const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId); + + // First time player + if (!lastClaim) { + await pog.pm(`Welcome! Use ${prefix}daily to claim your first daily reward and start your streak!`); + return; + } + + const now = new Date(); + const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000); + + if (now >= nextClaimTime) { + await pog.pm(`Your daily reward is ready! Use ${prefix}daily to claim it!`); + } +} + +await main(); diff --git a/packages/lib-modules/src/modules/dailyRewards/index.ts b/packages/lib-modules/src/modules/dailyRewards/index.ts new file mode 100644 index 0000000000..796c4c0af3 --- /dev/null +++ b/packages/lib-modules/src/modules/dailyRewards/index.ts @@ -0,0 +1,126 @@ +/* eslint-disable quotes */ +import { BuiltinModule, ICommand, IFunction, IHook, IPermission } from '../../BuiltinModule.js'; + +export class DailyRewards extends BuiltinModule { + constructor() { + super( + 'dailyRewards', + 'Provides daily login rewards with streak tracking', + JSON.stringify({ + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + baseReward: { + type: 'number', + title: 'Base Reward', + description: 'Base amount of currency given for daily rewards. This is multiplied by streak level.', + default: 100, + minimum: 1, + }, + maxStreak: { + type: 'number', + title: 'Maximum Streak', + description: 'Maximum streak level a player can reach', + default: 365, + minimum: 1, + }, + milestoneRewards: { + type: 'array', + title: 'Milestone Rewards', + description: 'Additional rewards for reaching certain streak milestones', + items: { + type: 'object', + properties: { + days: { + type: 'number', + description: 'Days needed to reach milestone', + minimum: 1, + }, + reward: { + type: 'number', + description: 'Bonus reward amount', + }, + message: { + type: 'string', + description: 'Message to show when milestone is reached', + }, + }, + }, + default: [ + { days: 7, reward: 1000, message: 'You did it! 7 days in a row!' }, + { days: 30, reward: 5000, message: "A whole month! You're on fire!" }, + { days: 90, reward: 20000, message: "90 days! You're unstoppable!" }, + { days: 180, reward: 50000, message: "Half a year! You're a legend!" }, + { days: 365, reward: 150000, message: "365 days! You're a true champion!" }, + ], + }, + }, + required: ['baseReward', 'maxStreak', 'milestoneRewards'], + additionalProperties: false, + }), + ); + + this.functions = [ + new IFunction({ + name: 'utils', + function: this.loadFn('functions', 'utils'), + }), + ]; + + this.permissions = [ + new IPermission({ + permission: 'DAILY_CLAIM', + friendlyName: 'Claim Daily Rewards', + description: 'Allows the player to claim daily rewards', + canHaveCount: false, + }), + new IPermission({ + permission: 'DAILY_REWARD_MULTIPLIER', + friendlyName: 'Multiplier', + description: + 'Control the multiplier per role. This is useful to give your donors a little extra. Count is an integer multiplier.', + canHaveCount: true, + }), + ]; + + this.commands = [ + new ICommand({ + function: this.loadFn('commands', 'daily'), + name: 'daily', + trigger: 'daily', + helpText: 'Claim your daily reward', + arguments: [], + }), + new ICommand({ + function: this.loadFn('commands', 'streak'), + name: 'streak', + trigger: 'streak', + helpText: 'Check your current daily reward streak and next claim time', + arguments: [], + }), + new ICommand({ + function: this.loadFn('commands', 'topstreak'), + name: 'topstreak', + trigger: 'topstreak', + helpText: 'Shows the players with highest daily reward streaks', + arguments: [ + { + name: 'count', + type: 'number', + defaultValue: '5', + helpText: 'Number of players to show (max 25)', + position: 0, + }, + ], + }), + ]; + + this.hooks = [ + new IHook({ + eventType: 'player-connected', + name: 'dailyLoginCheck', + function: this.loadFn('hooks', 'dailyLoginCheck'), + }), + ]; + } +} diff --git a/packages/lib-modules/src/modules/geoBlock/hooks/IPDetected.js b/packages/lib-modules/src/modules/geoBlock/hooks/IPDetected.js index 221ff1d4fa..de4cc1bc2b 100644 --- a/packages/lib-modules/src/modules/geoBlock/hooks/IPDetected.js +++ b/packages/lib-modules/src/modules/geoBlock/hooks/IPDetected.js @@ -9,9 +9,11 @@ async function main() { if (ban) { const now = new Date(); const expiresAt = new Date(now.getTime() + banDuration * 1000); - await takaro.gameserver.gameServerControllerBanPlayer(gameServerId, player.id, { + await takaro.player.banControllerCreate({ + gameServerId, + playerId: player.id, + until: expiresAt, reason: message, - expiresAt, }); } else { await takaro.gameserver.gameServerControllerKickPlayer(gameServerId, player.id, { diff --git a/packages/web-docs/assets/images/chatBridge/chatBridge_1_Takaro_settings.png b/packages/web-docs/assets/images/chatBridge/chatBridge_1_Takaro_settings.png new file mode 100644 index 0000000000..43ee814751 Binary files /dev/null and b/packages/web-docs/assets/images/chatBridge/chatBridge_1_Takaro_settings.png differ diff --git a/packages/web-docs/assets/images/chatBridge/chatBridge_3_Discord_bot.png b/packages/web-docs/assets/images/chatBridge/chatBridge_3_Discord_bot.png new file mode 100644 index 0000000000..43ee814751 Binary files /dev/null and b/packages/web-docs/assets/images/chatBridge/chatBridge_3_Discord_bot.png differ diff --git a/packages/web-docs/assets/images/chatBridge/chatBridge_4_DiscordChannelID.png b/packages/web-docs/assets/images/chatBridge/chatBridge_4_DiscordChannelID.png new file mode 100644 index 0000000000..43ee814751 Binary files /dev/null and b/packages/web-docs/assets/images/chatBridge/chatBridge_4_DiscordChannelID.png differ diff --git a/packages/web-docs/assets/images/chatBridge/chatBridge_5_Module_Channelid.png b/packages/web-docs/assets/images/chatBridge/chatBridge_5_Module_Channelid.png new file mode 100644 index 0000000000..43ee814751 Binary files /dev/null and b/packages/web-docs/assets/images/chatBridge/chatBridge_5_Module_Channelid.png differ diff --git a/packages/web-docs/assets/images/chatBridge/chatbridge_2_Takaro_Discord.png b/packages/web-docs/assets/images/chatBridge/chatbridge_2_Takaro_Discord.png new file mode 100644 index 0000000000..43ee814751 Binary files /dev/null and b/packages/web-docs/assets/images/chatBridge/chatbridge_2_Takaro_Discord.png differ diff --git a/packages/web-docs/assets/images/faq/roles-module-permissions.png b/packages/web-docs/assets/images/faq/roles-module-permissions.png deleted file mode 100644 index fbe8cb88e3..0000000000 Binary files a/packages/web-docs/assets/images/faq/roles-module-permissions.png and /dev/null differ diff --git a/packages/web-docs/docs/advanced/custom-modules.md b/packages/web-docs/docs/advanced/custom-modules.md index f50d8f34ed..3f00fa6bfd 100644 --- a/packages/web-docs/docs/advanced/custom-modules.md +++ b/packages/web-docs/docs/advanced/custom-modules.md @@ -6,11 +6,11 @@ sidebar_position: 3 While the Takaro Team is committed to providing a comprehensive set of built-in modules, there may be instances where a particular feature you need is unavailable, or perhaps -there is no existing module for a feature you desire. +there is no existing module for a feature you desire. -This is focused on advanced users familiar with JavaScript. +This is focused on advanced users familiar with JavaScript. If you need support, or if you believe your feature could enrich our suite of built-in modules, -we encourage you to reach out to our Discord community. +we encourage you to reach out to our Discord community. ## Functions diff --git a/packages/web-docs/docs/modules/chatBridge.mdx b/packages/web-docs/docs/modules/chatBridge.mdx index 69dd496a1a..051f3e4fc3 100644 --- a/packages/web-docs/docs/modules/chatBridge.mdx +++ b/packages/web-docs/docs/modules/chatBridge.mdx @@ -51,4 +51,60 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Step-by-step guide + +### Step 1: Connect Discord in Global Settings +- Go to **Global Game Server Settings > Discord** +![ChatBridge Settings](../../assets/images/chatBridge/chatBridge_1_Takaro_settings.png) +- Click **Add connection** +- Complete Discord authorization to allow Takaro to: + - Access your username, avatar and banner + - Access your email address + - Know what servers you're in +![ChatBridge Settings](../../assets/images/chatBridge/chatbridge_2_Takaro_Discord.png) +### Step 2: Add Takaro Bot to Your Server +1. Click **Invite Discord bot** in the same Global Settings page +2. Select your server from the dropdown +3. Authorize the bot permissions: + - Add a bot to a server + - Create commands +![ChatBridge Settings](../../assets/images/chatBridge/chatBridge_3_Discord_bot.png) +### Step 3: Enable Your Discord Server +1. Still in Global Settings, find the "Guilds" section +2. Toggle ON your Discord server (e.g., "Limon's server") + +### Step 4: Get Discord Channel ID + +1. Go to your Discord server +2. Right-click on the channel you want to use for the chat bridge +3. Click **Copy Channel ID** at the bottom of the menu + - If you don't see this option, make sure Developer Mode is enabled in Discord settings +![ChatBridge Settings](../../assets/images/chatBridge/chatBridge_4_DiscordChannelID.png) +### Step 5: Install and Configure ChatBridge + +1. Go to your game server's modules +2. Find and click "Install" on the ChatBridge module +3. Configure the settings: + + #### User Config + + - **Send Player Connected**: Toggle to announce joins + - **Send Player Disconnected**: Toggle to announce leaves + - **Only Global Chat**: Toggle to filter private/team chat + + #### System Config + + - Enable the module + - Set up Hooks: + - **DiscordToGame**: Enable and paste your copied Discord Channel ID + - **PlayerDisconnected**: Enable to track disconnects + - **GameToDiscord**: Enable game-to-Discord chat + - **PlayerConnected**: Enable to track connects + +![ChatBridge Settings](../../assets/images/chatBridge/chatBridge_5_Module_Channelid.png) + +### Step 6: Save Configuration + +- Click "Install module" to save all settings diff --git a/packages/web-docs/docs/modules/dailyRewards.mdx b/packages/web-docs/docs/modules/dailyRewards.mdx new file mode 100644 index 0000000000..cb323f7983 --- /dev/null +++ b/packages/web-docs/docs/modules/dailyRewards.mdx @@ -0,0 +1,87 @@ +{/* START AUTO-GENERATED CONTENT */} +import { Commands, Config, CronJobs, Hooks } from './helpers'; + +# dailyRewards + +export function Module() { + const mod = { + "commands": [ + { + "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\nimport { DAILY_KEY, STREAK_KEY, getMultiplier } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n if (!checkPermission(pog, 'DAILY_CLAIM')) {\n throw new TakaroUserError('You do not have permission to claim daily rewards.');\n }\n // Get last claim time\n const lastClaimRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [DAILY_KEY],\n gameServerId: [gameServerId],\n playerId: [pog.playerId],\n moduleId: [mod.moduleId],\n },\n });\n const now = new Date();\n let streak = 1;\n if (lastClaimRes.data.data.length > 0) {\n const lastClaim = new Date(JSON.parse(lastClaimRes.data.data[0].value));\n const hoursSinceLastClaim = (now - lastClaim) / (1000 * 60 * 60);\n // Check if 24 hours have passed\n if (hoursSinceLastClaim < 24) {\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n throw new TakaroUserError(`You can claim your next reward at ${nextClaimTime.toLocaleString()}`);\n }\n // Get current streak\n const streakRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n playerId: [pog.playerId],\n moduleId: [mod.moduleId],\n },\n });\n if (streakRes.data.data.length > 0) {\n // If claimed within 48 hours, increment streak\n if (hoursSinceLastClaim < 48) {\n streak = Math.min(JSON.parse(streakRes.data.data[0].value) + 1, mod.userConfig.maxStreak);\n await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n value: JSON.stringify(streak),\n });\n }\n else {\n // Reset streak if more than 48 hours\n await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n value: JSON.stringify(1),\n });\n }\n }\n else {\n // Create new streak record\n await takaro.variable.variableControllerCreate({\n key: STREAK_KEY,\n value: JSON.stringify(1),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n }\n // Update last claim time\n await takaro.variable.variableControllerUpdate(lastClaimRes.data.data[0].id, {\n value: JSON.stringify(now),\n });\n }\n else {\n // First time claim\n await takaro.variable.variableControllerCreate({\n key: DAILY_KEY,\n value: JSON.stringify(now),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n await takaro.variable.variableControllerCreate({\n key: STREAK_KEY,\n value: JSON.stringify(1),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n }\n const multiplier = await getMultiplier(pog);\n const baseReward = mod.userConfig.baseReward * streak * multiplier;\n let bonusReward = 0;\n let milestoneMessage = '';\n // Check for milestones\n for (const milestone of mod.userConfig.milestoneRewards) {\n if (streak === milestone.days) {\n bonusReward = milestone.reward;\n milestoneMessage = `\\n${milestone.message}`;\n break;\n }\n }\n // Award total rewards\n const totalReward = baseReward + bonusReward;\n await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, {\n currency: totalReward,\n });\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;\n await pog.pm(`Daily reward claimed! You received ${totalReward} ${currencyName}\\n` +\n `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x bonus!)` : ''}` +\n milestoneMessage);\n}\nawait main();\n//# sourceMappingURL=daily.js.map", + "name": "daily", + "trigger": "daily", + "helpText": "Claim your daily reward", + "arguments": [] + }, + { + "function": "import { data, takaro } from '@takaro/helpers';\nimport { getPlayerStreak, getLastClaim, getMultiplier } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n const streak = await getPlayerStreak(gameServerId, pog.playerId, mod.moduleId);\n const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n const multiplier = await getMultiplier(pog);\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n if (!streak || !lastClaim) {\n await pog.pm(`You haven't claimed any daily rewards yet! Use ${prefix}daily to get started.`);\n return;\n }\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n const now = new Date();\n const canClaim = now >= nextClaimTime;\n // Find next milestone\n let nextMilestone = null;\n for (const milestone of mod.userConfig.milestoneRewards) {\n if (milestone.days > streak) {\n nextMilestone = milestone;\n break;\n }\n }\n let message = `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x donor bonus!)` : ''}\\n`;\n message += canClaim\n ? `Your daily reward is available! Use ${prefix}daily to claim it!\\n`\n : `Next reward available at: ${nextClaimTime.toLocaleString()}\\n`;\n if (nextMilestone) {\n message += `\\n🎯 Next milestone: ${nextMilestone.days} days (${nextMilestone.days - streak} days to go!)`;\n }\n await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=streak.js.map", + "name": "streak", + "trigger": "streak", + "helpText": "Check your current daily reward streak and next claim time", + "arguments": [] + }, + { + "function": "import { takaro, data } from '@takaro/helpers';\nimport { STREAK_KEY } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod, arguments: args } = data;\n // Limit count to reasonable number\n const count = Math.min(Math.max(1, args.count), 50);\n // Get all streaks\n const streaksRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n moduleId: [mod.moduleId],\n },\n limit: 1000, // Get all possible streaks\n });\n if (streaksRes.data.data.length === 0) {\n await pog.pm('No players have started their daily streak yet!');\n return;\n }\n // Sort by streak value\n const sortedStreaks = streaksRes.data.data\n .map((record) => ({\n playerId: record.playerId,\n streak: JSON.parse(record.value),\n }))\n .sort((a, b) => b.streak - a.streak)\n .slice(0, count);\n // Get player names\n const playerDetails = await Promise.all(sortedStreaks.map(async (record) => {\n const player = (await takaro.player.playerControllerGetOne(record.playerId)).data.data;\n return {\n name: player.name,\n streak: record.streak,\n };\n }));\n // Build message\n let message = `Top ${count} Daily Streaks:\\n\\n`;\n playerDetails.forEach((player, index) => {\n message += `${index + 1}. ${player.name}: ${player.streak} days\\n`;\n });\n await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=topstreak.js.map", + "name": "topstreak", + "trigger": "topstreak", + "helpText": "Shows the players with highest daily reward streaks", + "arguments": [ + { + "name": "count", + "type": "number", + "defaultValue": "5", + "helpText": "Number of players to show (max 25)", + "position": 0 + } + ] + } + ], + "hooks": [ + { + "eventType": "player-connected", + "name": "dailyLoginCheck", + "function": "import { data, takaro } from '@takaro/helpers';\nimport { getLastClaim } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n // First time player\n if (!lastClaim) {\n await pog.pm(`Welcome! Use ${prefix}daily to claim your first daily reward and start your streak!`);\n return;\n }\n const now = new Date();\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n if (now >= nextClaimTime) {\n await pog.pm(`Your daily reward is ready! Use ${prefix}daily to claim it!`);\n }\n}\nawait main();\n//# sourceMappingURL=dailyLoginCheck.js.map" + } + ], + "cronJobs": [], + "functions": [ + { + "name": "utils", + "function": "import { takaro, checkPermission } from '@takaro/helpers';\nexport const DAILY_KEY = 'daily_timestamp';\nexport const STREAK_KEY = 'daily_streak';\nexport async function getMultiplier(pog) {\n const perm = checkPermission(pog, 'DAILY_REWARD_MULTIPLIER');\n if (perm)\n return perm.count;\n return 1;\n}\nexport async function getPlayerStreak(gameServerId, playerId, moduleId) {\n const streakRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n playerId: [playerId],\n moduleId: [moduleId],\n },\n });\n return streakRes.data.data.length ? parseInt(JSON.parse(streakRes.data.data[0].value)) : 0;\n}\nexport async function getLastClaim(gameServerId, playerId, moduleId) {\n const lastClaimRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [DAILY_KEY],\n gameServerId: [gameServerId],\n playerId: [playerId],\n moduleId: [moduleId],\n },\n });\n return lastClaimRes.data.data.length ? new Date(JSON.parse(lastClaimRes.data.data[0].value)) : null;\n}\n//# sourceMappingURL=utils.js.map" + } + ], + "permissions": [ + { + "permission": "DAILY_CLAIM", + "friendlyName": "Claim Daily Rewards", + "description": "Allows the player to claim daily rewards", + "canHaveCount": false + }, + { + "permission": "DAILY_REWARD_MULTIPLIER", + "friendlyName": "Multiplier", + "description": "Control the multiplier per role. This is useful to give your donors a little extra. Count is an integer multiplier.", + "canHaveCount": true + } + ], + "name": "dailyRewards", + "description": "Provides daily login rewards with streak tracking", + "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"baseReward\":{\"type\":\"number\",\"title\":\"Base Reward\",\"description\":\"Base amount of currency given for daily rewards. This is multiplied by streak level.\",\"default\":100,\"minimum\":1},\"maxStreak\":{\"type\":\"number\",\"title\":\"Maximum Streak\",\"description\":\"Maximum streak level a player can reach\",\"default\":365,\"minimum\":1},\"milestoneRewards\":{\"type\":\"array\",\"title\":\"Milestone Rewards\",\"description\":\"Additional rewards for reaching certain streak milestones\",\"items\":{\"type\":\"object\",\"properties\":{\"days\":{\"type\":\"number\",\"description\":\"Days needed to reach milestone\",\"minimum\":1},\"reward\":{\"type\":\"number\",\"description\":\"Bonus reward amount\"},\"message\":{\"type\":\"string\",\"description\":\"Message to show when milestone is reached\"}}},\"default\":[{\"days\":7,\"reward\":1000,\"message\":\"You did it! 7 days in a row!\"},{\"days\":30,\"reward\":5000,\"message\":\"A whole month! You're on fire!\"},{\"days\":90,\"reward\":20000,\"message\":\"90 days! You're unstoppable!\"},{\"days\":180,\"reward\":50000,\"message\":\"Half a year! You're a legend!\"},{\"days\":365,\"reward\":150000,\"message\":\"365 days! You're a true champion!\"}]}},\"required\":[\"baseReward\",\"maxStreak\",\"milestoneRewards\"],\"additionalProperties\":false}", + "uiSchema": "{}" +}; + + return ( +
+

{mod.description}

+ + + + +
+ ) +} + + + +--- +{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file diff --git a/packages/web-docs/docs/modules/economyUtils.mdx b/packages/web-docs/docs/modules/economyUtils.mdx index 7855a0ed61..88be17f19c 100644 --- a/packages/web-docs/docs/modules/economyUtils.mdx +++ b/packages/web-docs/docs/modules/economyUtils.mdx @@ -181,4 +181,150 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Features +- Balance checking and transfers +- Administrative currency controls +- Automated kill rewards +- Shop integration +- Currency leaderboard + +## Permissions + +### ECONOMY_UTILS_MANAGE_CURRENCY +- Allows granting and revoking currency +- Typically for admins/moderators + +### ZOMBIE_KILL_REWARD_OVERRIDE +- Overrides default kill reward amount +- Can set different amounts per role +- Count value determines reward amount + +## Variables + +### Currency Related +- `lastZombieKillReward` + - Tracks last reward distribution time + - Used by automatic reward system + - Scoped to gameserver and module + +### Shop Related +- `lottery_tickets_bought` + - Stores ticket quantity + - Format: `{ "amount": number }` + - Scoped to player, gameserver, and module + +### Transfer Related +- `confirmTransfer` + - Stores pending transfer details + - Temporary storage for large transfers + +## Automatic Rewards + +### Kill Rewards System +- Auto-processes every 5 minutes +- Awards currency for entity kills +- Default amount configurable +- Role-based override example: + ``` + Default Player: 1 coin per kill + VIP Role: 3 coins per kill + Elite Role: 5 coins per kill + ``` + +### Setting Up Role Rewards +1. Navigate to Roles +2. Enable ZOMBIE_KILL_REWARD_OVERRIDE +3. Set count value for reward amount +4. Assign role to players + +## Commands & Usage Examples + +### Basic Currency Commands +``` +# Check your balance +/balance +> balance: 500 coins + +# View richest players +/topcurrency +> Richest players: +> 1. PlayerOne - 10000 coins +> 2. PlayerTwo - 8500 coins +> [...] +``` + +### Transfer System +``` +# Simple transfer +/transfer John 100 +> You successfully transferred 100 coins to John +> (John receives: You received 100 coins from PlayerName) + +# Large transfer requiring confirmation +/transfer Jane 1000 +> You are about to send 1000 coins to Jane. (Please confirm by typing /confirmtransfer) +/confirmtransfer +> You successfully transferred 1000 coins to Jane +``` + +### Shop System +``` +# Browse shop +/shop +> Available items: +> 1. Diamond Sword - 500 coins +> 2. Golden Apple - 100 coins +> [...] + +# View item details +/shop 1 2 +> Golden Apple - 100 coins +> Restores health instantly +> Type '/shop 1 2 buy' to purchase + +# Purchase item +/shop 1 2 buy +> You have purchased Golden Apple for 100 coins + +# Claim purchased items +/claim +> Claimed all pending orders: 3 items +``` + +### Admin Commands +``` +# Grant currency +/grantcurrency PlayerName 500 +> You successfully granted 500 coins to PlayerName + +# Remove currency +/revokecurrency PlayerName 200 +> You successfully revoked 200 coins from PlayerName's balance +``` + + +## Best Practices + +### Economy Management +- Set reasonable transfer confirmation thresholds +- Monitor currency circulation +- Adjust reward values based on server activity + +### Role Configuration +- Create clear reward tiers +- Balance rewards across different roles +- Consider server economy when setting values +## Troubleshooting +1. Transfer Failed + - Check sender's balance + - Verify player names + - Confirm if confirmation needed + - Check for pending transfer variable + +2. Rewards Not Processing + - Verify kill events recording + - Check role permissions + - Confirm cron job running + - Check lastZombieKillReward timestamp diff --git a/packages/web-docs/docs/modules/geoBlock.mdx b/packages/web-docs/docs/modules/geoBlock.mdx index 379475874d..77255ceb49 100644 --- a/packages/web-docs/docs/modules/geoBlock.mdx +++ b/packages/web-docs/docs/modules/geoBlock.mdx @@ -10,7 +10,7 @@ export function Module() { { "eventType": "player-new-ip-detected", "name": "IPDetected", - "function": "import { takaro, data, checkPermission } from '@takaro/helpers';\nasync function main() {\n const { gameServerId, player, pog } = data;\n const { country } = data.eventData;\n const { ban, banDuration, countries, message, mode } = data.module.userConfig;\n async function handleAction() {\n if (ban) {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + banDuration * 1000);\n await takaro.gameserver.gameServerControllerBanPlayer(gameServerId, player.id, {\n reason: message,\n expiresAt,\n });\n }\n else {\n await takaro.gameserver.gameServerControllerKickPlayer(gameServerId, player.id, {\n reason: message,\n });\n }\n }\n const isImmune = checkPermission(pog, 'GEOBLOCK_IMMUNITY');\n if (isImmune) {\n console.log('Player has immunity, no action');\n return;\n }\n if (mode === 'allow') {\n if (countries.includes(country)) {\n console.log('Allowed country detected, no action');\n return;\n }\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n if (mode === 'deny') {\n if (countries.includes(country)) {\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n else {\n console.log('Allowed country detected, no action');\n return;\n }\n }\n}\nawait main();\n//# sourceMappingURL=IPDetected.js.map" + "function": "import { takaro, data, checkPermission } from '@takaro/helpers';\nasync function main() {\n const { gameServerId, player, pog } = data;\n const { country } = data.eventData;\n const { ban, banDuration, countries, message, mode } = data.module.userConfig;\n async function handleAction() {\n if (ban) {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + banDuration * 1000);\n await takaro.player.banControllerCreate({\n gameServerId,\n playerId: player.id,\n until: expiresAt,\n reason: message,\n });\n }\n else {\n await takaro.gameserver.gameServerControllerKickPlayer(gameServerId, player.id, {\n reason: message,\n });\n }\n }\n const isImmune = checkPermission(pog, 'GEOBLOCK_IMMUNITY');\n if (isImmune) {\n console.log('Player has immunity, no action');\n return;\n }\n if (mode === 'allow') {\n if (countries.includes(country)) {\n console.log('Allowed country detected, no action');\n return;\n }\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n if (mode === 'deny') {\n if (countries.includes(country)) {\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n else {\n console.log('Allowed country detected, no action');\n return;\n }\n }\n}\nawait main();\n//# sourceMappingURL=IPDetected.js.map" } ], "cronJobs": [], @@ -45,7 +45,43 @@ export function Module() { --- {/* END AUTO-GENERATED CONTENT */} -# How to Install and Configure the Geoblock Module + +## Core Functionality +- Country-based access control +- Configurable action settings (kick or ban) +- Customizable ban duration +- Immunity system through role permissions +- Custom messages for blocked players + +### Operation Modes +- **Allow Mode**: Only players from specified countries can join +- **Deny Mode**: Players from specified countries are blocked from joining + +## Configuration Options + +### Essential Settings +1. **Mode Selection** + - Allow: Whitelist mode - only listed countries can join + - Deny: Blacklist mode - listed countries are blocked + +2. **Action Settings** + - Ban (default: enabled) + - Ban Duration (default: 24 hours) + - Custom Message (default: "Your IP address is banned.") + +3. **Country List** + - Comprehensive list of countries to allow/deny + - Countries are selected using standard two-letter country codes + - Multiple countries can be selected simultaneously + +## Permissions + +### GeoBlock Immunity +- Permission Name: `GEOBLOCK_IMMUNITY` +- Description: Players with this permission will not be affected by GeoBlock restrictions +- Cannot have count value (boolean permission) + +## How to Install and Configure the Geoblock Module This guide explains how to install the Geoblock module, select countries to ban, and create roles to grant immunity to players from banned countries. Follow these steps to effectively manage your game server's access control. You can find more info related to built-in modules in [built-in modules](../modules/overview.mdx). @@ -58,85 +94,85 @@ This guide explains how to install the Geoblock module, select countries to ban, allowFullScreen /> -## Install the module to your game server +### Install the module to your game server -### Step 1: Navigate to the Modules Section +#### Step 1: Navigate to the Modules Section Go to the main menu and find the 'Modules' section in your game server's administration interface. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu8vexg000s356obenz1bji-annotated.png) -### Step 2: Start the Installation +#### Step 2: Start the Installation Click on the 'Install New Module' button and search for the 'Geoblock' module in the list of available modules. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu9m3uc0019356o4qyrrfz4-annotated.png) -### Step 3: Select Countries to Block +##$# Step 3: Select Countries to Block During the installation process, you'll be prompted to select the countries you want to block. Use the provided interface to choose the countries from the list. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu8w3u5000v356ollrkog5f-annotated.png) -### Step 4: Install the Geoblock Module +#$## Step 4: Install the Geoblock Module After selecting the countries, click the 'Install' button to complete the installation of the Geoblock module. ![](https://d1kbuqctacnbc1.cloudfront.net/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyrqudxg000i033p1tf3bd8h-annotated.png) -## Assign the permission to the right role +### Assign the permission to the right role -### Step 5: Navigate to the Roles Section +#### Step 5: Navigate to the Roles Section After installing the Geoblock module, go back to the main menu and find the 'Roles' section in the administration interface. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu8wfq1000w356oq30mzzv2-annotated.png) -### Step 6: Create a New Role +#### Step 6: Create a New Role Click the 'Create New Role' button to start the process of creating a role that will grant ban immunity. ![](https://d1kbuqctacnbc1.cloudfront.net/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyrquk53000k033pdmokia11-annotated.png) -### Step 7: Name Your New Role +#### Step 7: Name Your New Role Give your new role a recognizable name that indicates it grants immunity, such as 'Ban Immunity'. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu8xfyq000y356ocm8nwgiy-annotated.png) -### Step 8: Confirm Role Name +#### Step 8: Confirm Role Name After entering the name, click the 'Confirm' button to save the new role. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu8xi2s000z356omzq4gqax-annotated.png) -## Assign the role to the right player +### Assign the role to the right player -### Step 9: Go to Players Section +#### Step 9: Go to Players Section Navigate to the 'Players' section from the main menu to assign the new role to specific players. ![](https://d1kbuqctacnbc1.cloudfront.net/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyrquw3q000p033pepixxr01-annotated.png) -### Step 10: Select Player for Role Assignment +#### Step 10: Select Player for Role Assignment Find and select the player you want to assign the new role to from the list of registered players. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu94gla0013356oopo1k4ik-annotated.png) -### Step 11: Assign the newly created role +#### Step 11: Assign the newly created role Assign the newly created role to the selected player by checking the role in the player's profile. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu94jia0014356o8gg38rd2-annotated.png) -### Step 12: Decide Role Activation Scope +#### Step 12: Decide Role Activation Scope Choose whether to activate this role across all game servers or specific individual game servers by configuring the settings as needed. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyrqtuvs0016jo0cdutlvhl6/clyu97xk80017356oi0tv43q0-annotated.png) -### Step 13: Save Changes +#### Step 13: Save Changes Click the 'Save changes' button to apply all the configurations and finalize the process. diff --git a/packages/web-docs/docs/modules/gimme.mdx b/packages/web-docs/docs/modules/gimme.mdx index 69ff9210ad..834266facb 100644 --- a/packages/web-docs/docs/modules/gimme.mdx +++ b/packages/web-docs/docs/modules/gimme.mdx @@ -38,4 +38,25 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Key Features +- Randomly selects and distributes items from a configurable list +- Can execute random server commands from a predefined list +- Single command usage: `/gimme` + +## How It Works +1. When a player uses `/gimme`, the module: + - Randomly selects between giving an item and executing a command +2. Notifies the player of what they received + +## Setup Steps +1. Install the module from the selected game server +2. Configure item and command lists +3. Save configuration +4. Players can immediately start using `/gimme` + +## Best Practices +- Balance reward values to maintain game economy + +This module requires no special permissions and is accessible to all players by default. \ No newline at end of file diff --git a/packages/web-docs/docs/modules/highPingKicker.mdx b/packages/web-docs/docs/modules/highPingKicker.mdx index b6e68fb8c2..9c192fb20c 100644 --- a/packages/web-docs/docs/modules/highPingKicker.mdx +++ b/packages/web-docs/docs/modules/highPingKicker.mdx @@ -36,4 +36,102 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Key Features + +- Automatic ping monitoring +- Configurable ping thresholds +- Warning system before kicks +- Customizable number of warnings +- Player immunity options through permissions + +## Configuration Options + +### 1. Ping Threshold + +- Default value: 200ms +- Purpose: Defines the maximum acceptable ping value +- When a player's ping exceeds this threshold, they begin receiving warnings +- Can be adjusted based on your server's requirements and geographical location + +### 2. Warnings Before Kick + +- Default value: 3 warnings +- Purpose: Determines how many warnings a player receives before being kicked +- Provides players with opportunities to resolve connection issues +- Each warning is tracked individually per player + +## How It Works + +The module operates on a 5-minute cycle (configured via cron job: `*/5 * * * *`) and performs the following steps: + +1. **Monitoring**: Checks all online players' ping values +2. **Warning System**: + - If a player's ping exceeds the threshold, they receive a warning + - Warnings are tracked using Takaro's variable system + - Each subsequent high ping check increments the warning count +3. **Kick Action**: + - When warnings reach the configured limit, the player is kicked + - Kick message includes their ping value for reference + - Warning count is reset after a kick + +Example warning message: +`Server is shutting down in 5 minutes for daily maintenance. We'll be back online in ~10 minutes. Please find a safe spot!` + +## Implementation Details + +The module uses Takaro's variable system to track warnings with the key `highPingKicker:warnings`. This ensures persistence across server restarts and allows for accurate warning tracking. + +### Warning Process: + +1. First high ping detection: Creates warning variable +2. Subsequent detections: Increments warning count +3. Final warning: Executes kick and resets counter + +### Permissions + +The module includes one permission: + +- `GEOBLOCK_IMMUNITY`: Players with this permission will not be kicked regardless of their ping + - Useful for administrators or trusted players + - Can be assigned through Takaro's role system + +### Installation + +1. Navigate to your server's Modules section +2. Select "Install New Module" +3. Find and select "highPingKicker" +4. Configure the desired ping threshold and warning count +5. Save and activate the module + +## Best Practices + +1. **Initial Setup**: + - Start with default values (200ms ping threshold, 3 warnings) + - Monitor player feedback and server logs + - Adjust values based on your community's needs +2. **Threshold Configuration**: + - Consider your server's geographical location + - Account for typical ping ranges in your target regions + - Set thresholds that balance performance with accessibility +3. **Warning Management**: + - Use enough warnings to allow for temporary connection issues + - Keep warning count reasonable to maintain server performance + - Consider time between checks (default 5 minutes) + +## Troubleshooting + +Common issues and solutions: + +1. **Too Many Kicks**: + - Increase ping threshold + - Add more warnings before kick + - Review server performance metrics +2. **Ineffective Management**: + - Decrease ping threshold + - Reduce warning count + - Decrease check interval +3. **False Positives**: + - Grant immunity to affected players + - Adjust threshold based on time of day \ No newline at end of file diff --git a/packages/web-docs/docs/modules/lottery.mdx b/packages/web-docs/docs/modules/lottery.mdx index 356b25a9a8..a63c75ecc0 100644 --- a/packages/web-docs/docs/modules/lottery.mdx +++ b/packages/web-docs/docs/modules/lottery.mdx @@ -79,4 +79,109 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Overview + +The lottery system works by: + +- Players can purchase tickets using server currency +- A scheduled draw determines winners automatically +- Winners receive a prize pool based on ticket sales +- Configurable profit margin for the server +- Daily automated draws + +## Commands + +|Command|Description|Arguments| +|---|---|---| +|`/buyTicket`|Purchase lottery tickets|`amount`: Number of tickets to buy| +|`/viewTickets`|View your currently held tickets|None| +|`/nextDraw`|Check when the next lottery draw will occur|None| + +## Permissions + +The module includes two main permissions that can be assigned to roles: + +- **LOTTERY_BUY**: Allows players to purchase lottery tickets +- **LOTTERY_VIEW_TICKETS**: Allows players to view their currently held tickets + +## Configuration + +### Module Settings + +|Setting|Description|Default| +|---|---|---| +|`profitMargin`|Percentage of ticket sales kept by the server (0-1)|0.1 (10%)| + +### System Configuration + +The lottery draw is scheduled using a cron job that runs at `0 0 * * *` (midnight every day). + +## How It Works + +1. **Ticket Purchase**: + - Players use the `/buyTicket` command to purchase tickets + - Each ticket costs a configurable amount of currency + - Players can purchase multiple tickets at once + - Tickets are tracked per player until the next draw +2. **Prize Pool**: + - Total prize pool = (Total ticket sales) * (1 - profit margin) + - Server keeps the profit margin percentage + - The remaining amount goes to the winner +3. **Draw Process**: + - Occurs automatically at configured time + - One winner is randomly selected from all ticket holders + - More tickets = higher chance of winning + - Prize is automatically distributed to winner + - All tickets are cleared after each draw +4. **Safeguards**: + - Lottery cancels if only one player bought tickets + - Players receive refunds if lottery is cancelled + - Server announces winner and prize amount to all players + +## Setup Guide + +1. **Install the Module**: + - Navigate to your server's Modules section + - Find "lottery" in the module list + - Click Install + - Configure the profit margin as desired +2. **Configure Permissions**: + - Go to Roles section + - Assign LOTTERY_BUY and LOTTERY_VIEW_TICKETS permissions to appropriate roles + - Set any cost requirements for permissions if desired +3. **Verify Installation**: + - Check that commands are working using `/help lottery` + - Ensure draw schedule is active by using `/nextDraw` + - Test ticket purchase with `/buyTicket 1` + +## Best Practices + +1. **Economy Balance**: + - Set appropriate ticket costs based on your server's economy + - Adjust profit margin to control prize pool sizes + - Monitor ticket sales and adjust parameters as needed +2. **Player Communication**: + - Announce lottery events in advance + - Use server messages to remind players about upcoming draws + - Celebrate winners to encourage participation +3. **Permission Management**: + - Consider creating specific lottery roles + - Use permission costs to prevent abuse + - Monitor for unusual buying patterns + +## Troubleshooting + + +**Players can't buy tickets**: + - Verify LOTTERY_BUY permission is assigned + - Check if player has sufficient currency + - Ensure economy system is enabled + +## Notes + +- Tickets do not persist between draws +- Players must be online to receive win notifications +- The module requires an active economy system +- All transactions are logged for administrative review \ No newline at end of file diff --git a/packages/web-docs/docs/modules/modules.json b/packages/web-docs/docs/modules/modules.json index e5f4728684..89d1e2886c 100644 --- a/packages/web-docs/docs/modules/modules.json +++ b/packages/web-docs/docs/modules/modules.json @@ -533,7 +533,7 @@ { "eventType": "player-new-ip-detected", "name": "IPDetected", - "function": "import { takaro, data, checkPermission } from '@takaro/helpers';\nasync function main() {\n const { gameServerId, player, pog } = data;\n const { country } = data.eventData;\n const { ban, banDuration, countries, message, mode } = data.module.userConfig;\n async function handleAction() {\n if (ban) {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + banDuration * 1000);\n await takaro.gameserver.gameServerControllerBanPlayer(gameServerId, player.id, {\n reason: message,\n expiresAt,\n });\n }\n else {\n await takaro.gameserver.gameServerControllerKickPlayer(gameServerId, player.id, {\n reason: message,\n });\n }\n }\n const isImmune = checkPermission(pog, 'GEOBLOCK_IMMUNITY');\n if (isImmune) {\n console.log('Player has immunity, no action');\n return;\n }\n if (mode === 'allow') {\n if (countries.includes(country)) {\n console.log('Allowed country detected, no action');\n return;\n }\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n if (mode === 'deny') {\n if (countries.includes(country)) {\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n else {\n console.log('Allowed country detected, no action');\n return;\n }\n }\n}\nawait main();\n//# sourceMappingURL=IPDetected.js.map" + "function": "import { takaro, data, checkPermission } from '@takaro/helpers';\nasync function main() {\n const { gameServerId, player, pog } = data;\n const { country } = data.eventData;\n const { ban, banDuration, countries, message, mode } = data.module.userConfig;\n async function handleAction() {\n if (ban) {\n const now = new Date();\n const expiresAt = new Date(now.getTime() + banDuration * 1000);\n await takaro.player.banControllerCreate({\n gameServerId,\n playerId: player.id,\n until: expiresAt,\n reason: message,\n });\n }\n else {\n await takaro.gameserver.gameServerControllerKickPlayer(gameServerId, player.id, {\n reason: message,\n });\n }\n }\n const isImmune = checkPermission(pog, 'GEOBLOCK_IMMUNITY');\n if (isImmune) {\n console.log('Player has immunity, no action');\n return;\n }\n if (mode === 'allow') {\n if (countries.includes(country)) {\n console.log('Allowed country detected, no action');\n return;\n }\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n if (mode === 'deny') {\n if (countries.includes(country)) {\n console.log('Blocked country detected, performing actions');\n await handleAction();\n return;\n }\n else {\n console.log('Allowed country detected, no action');\n return;\n }\n }\n}\nawait main();\n//# sourceMappingURL=IPDetected.js.map" } ], "cronJobs": [], @@ -572,5 +572,70 @@ "description": "Automatically shut down the server at a specific time.", "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"warningMessage\":{\"type\":\"string\",\"title\":\"Warning message\",\"description\":\"Message to send to players before the server shuts down.\",\"default\":\"Server is shutting down in 5 minutes!\",\"minLength\":1,\"maxLength\":1024}},\"required\":[\"warningMessage\"]}", "uiSchema": "{}" + }, + { + "commands": [ + { + "function": "import { takaro, data, TakaroUserError, checkPermission } from '@takaro/helpers';\nimport { DAILY_KEY, STREAK_KEY, getMultiplier } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n if (!checkPermission(pog, 'DAILY_CLAIM')) {\n throw new TakaroUserError('You do not have permission to claim daily rewards.');\n }\n // Get last claim time\n const lastClaimRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [DAILY_KEY],\n gameServerId: [gameServerId],\n playerId: [pog.playerId],\n moduleId: [mod.moduleId],\n },\n });\n const now = new Date();\n let streak = 1;\n if (lastClaimRes.data.data.length > 0) {\n const lastClaim = new Date(JSON.parse(lastClaimRes.data.data[0].value));\n const hoursSinceLastClaim = (now - lastClaim) / (1000 * 60 * 60);\n // Check if 24 hours have passed\n if (hoursSinceLastClaim < 24) {\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n throw new TakaroUserError(`You can claim your next reward at ${nextClaimTime.toLocaleString()}`);\n }\n // Get current streak\n const streakRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n playerId: [pog.playerId],\n moduleId: [mod.moduleId],\n },\n });\n if (streakRes.data.data.length > 0) {\n // If claimed within 48 hours, increment streak\n if (hoursSinceLastClaim < 48) {\n streak = Math.min(JSON.parse(streakRes.data.data[0].value) + 1, mod.userConfig.maxStreak);\n await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n value: JSON.stringify(streak),\n });\n }\n else {\n // Reset streak if more than 48 hours\n await takaro.variable.variableControllerUpdate(streakRes.data.data[0].id, {\n value: JSON.stringify(1),\n });\n }\n }\n else {\n // Create new streak record\n await takaro.variable.variableControllerCreate({\n key: STREAK_KEY,\n value: JSON.stringify(1),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n }\n // Update last claim time\n await takaro.variable.variableControllerUpdate(lastClaimRes.data.data[0].id, {\n value: JSON.stringify(now),\n });\n }\n else {\n // First time claim\n await takaro.variable.variableControllerCreate({\n key: DAILY_KEY,\n value: JSON.stringify(now),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n await takaro.variable.variableControllerCreate({\n key: STREAK_KEY,\n value: JSON.stringify(1),\n gameServerId,\n playerId: pog.playerId,\n moduleId: mod.moduleId,\n });\n }\n const multiplier = await getMultiplier(pog);\n const baseReward = mod.userConfig.baseReward * streak * multiplier;\n let bonusReward = 0;\n let milestoneMessage = '';\n // Check for milestones\n for (const milestone of mod.userConfig.milestoneRewards) {\n if (streak === milestone.days) {\n bonusReward = milestone.reward;\n milestoneMessage = `\\n${milestone.message}`;\n break;\n }\n }\n // Award total rewards\n const totalReward = baseReward + bonusReward;\n await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, pog.playerId, {\n currency: totalReward,\n });\n const currencyName = (await takaro.settings.settingsControllerGetOne('currencyName', gameServerId)).data.data.value;\n await pog.pm(`Daily reward claimed! You received ${totalReward} ${currencyName}\\n` +\n `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x bonus!)` : ''}` +\n milestoneMessage);\n}\nawait main();\n//# sourceMappingURL=daily.js.map", + "name": "daily", + "trigger": "daily", + "helpText": "Claim your daily reward", + "arguments": [] + }, + { + "function": "import { data, takaro } from '@takaro/helpers';\nimport { getPlayerStreak, getLastClaim, getMultiplier } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n const streak = await getPlayerStreak(gameServerId, pog.playerId, mod.moduleId);\n const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n const multiplier = await getMultiplier(pog);\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n if (!streak || !lastClaim) {\n await pog.pm(`You haven't claimed any daily rewards yet! Use ${prefix}daily to get started.`);\n return;\n }\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n const now = new Date();\n const canClaim = now >= nextClaimTime;\n // Find next milestone\n let nextMilestone = null;\n for (const milestone of mod.userConfig.milestoneRewards) {\n if (milestone.days > streak) {\n nextMilestone = milestone;\n break;\n }\n }\n let message = `Current streak: ${streak} days${multiplier > 1 ? ` (${multiplier}x donor bonus!)` : ''}\\n`;\n message += canClaim\n ? `Your daily reward is available! Use ${prefix}daily to claim it!\\n`\n : `Next reward available at: ${nextClaimTime.toLocaleString()}\\n`;\n if (nextMilestone) {\n message += `\\n🎯 Next milestone: ${nextMilestone.days} days (${nextMilestone.days - streak} days to go!)`;\n }\n await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=streak.js.map", + "name": "streak", + "trigger": "streak", + "helpText": "Check your current daily reward streak and next claim time", + "arguments": [] + }, + { + "function": "import { takaro, data } from '@takaro/helpers';\nimport { STREAK_KEY } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod, arguments: args } = data;\n // Limit count to reasonable number\n const count = Math.min(Math.max(1, args.count), 50);\n // Get all streaks\n const streaksRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n moduleId: [mod.moduleId],\n },\n limit: 1000, // Get all possible streaks\n });\n if (streaksRes.data.data.length === 0) {\n await pog.pm('No players have started their daily streak yet!');\n return;\n }\n // Sort by streak value\n const sortedStreaks = streaksRes.data.data\n .map((record) => ({\n playerId: record.playerId,\n streak: JSON.parse(record.value),\n }))\n .sort((a, b) => b.streak - a.streak)\n .slice(0, count);\n // Get player names\n const playerDetails = await Promise.all(sortedStreaks.map(async (record) => {\n const player = (await takaro.player.playerControllerGetOne(record.playerId)).data.data;\n return {\n name: player.name,\n streak: record.streak,\n };\n }));\n // Build message\n let message = `Top ${count} Daily Streaks:\\n\\n`;\n playerDetails.forEach((player, index) => {\n message += `${index + 1}. ${player.name}: ${player.streak} days\\n`;\n });\n await pog.pm(message);\n}\nawait main();\n//# sourceMappingURL=topstreak.js.map", + "name": "topstreak", + "trigger": "topstreak", + "helpText": "Shows the players with highest daily reward streaks", + "arguments": [ + { + "name": "count", + "type": "number", + "defaultValue": "5", + "helpText": "Number of players to show (max 25)", + "position": 0 + } + ] + } + ], + "hooks": [ + { + "eventType": "player-connected", + "name": "dailyLoginCheck", + "function": "import { data, takaro } from '@takaro/helpers';\nimport { getLastClaim } from './utils.js';\nasync function main() {\n const { pog, gameServerId, module: mod } = data;\n const prefix = (await takaro.settings.settingsControllerGetOne('commandPrefix', gameServerId)).data.data.value;\n const lastClaim = await getLastClaim(gameServerId, pog.playerId, mod.moduleId);\n // First time player\n if (!lastClaim) {\n await pog.pm(`Welcome! Use ${prefix}daily to claim your first daily reward and start your streak!`);\n return;\n }\n const now = new Date();\n const nextClaimTime = new Date(lastClaim.getTime() + 24 * 60 * 60 * 1000);\n if (now >= nextClaimTime) {\n await pog.pm(`Your daily reward is ready! Use ${prefix}daily to claim it!`);\n }\n}\nawait main();\n//# sourceMappingURL=dailyLoginCheck.js.map" + } + ], + "cronJobs": [], + "functions": [ + { + "name": "utils", + "function": "import { takaro, checkPermission } from '@takaro/helpers';\nexport const DAILY_KEY = 'daily_timestamp';\nexport const STREAK_KEY = 'daily_streak';\nexport async function getMultiplier(pog) {\n const perm = checkPermission(pog, 'DAILY_REWARD_MULTIPLIER');\n if (perm)\n return perm.count;\n return 1;\n}\nexport async function getPlayerStreak(gameServerId, playerId, moduleId) {\n const streakRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [STREAK_KEY],\n gameServerId: [gameServerId],\n playerId: [playerId],\n moduleId: [moduleId],\n },\n });\n return streakRes.data.data.length ? parseInt(JSON.parse(streakRes.data.data[0].value)) : 0;\n}\nexport async function getLastClaim(gameServerId, playerId, moduleId) {\n const lastClaimRes = await takaro.variable.variableControllerSearch({\n filters: {\n key: [DAILY_KEY],\n gameServerId: [gameServerId],\n playerId: [playerId],\n moduleId: [moduleId],\n },\n });\n return lastClaimRes.data.data.length ? new Date(JSON.parse(lastClaimRes.data.data[0].value)) : null;\n}\n//# sourceMappingURL=utils.js.map" + } + ], + "permissions": [ + { + "permission": "DAILY_CLAIM", + "friendlyName": "Claim Daily Rewards", + "description": "Allows the player to claim daily rewards", + "canHaveCount": false + }, + { + "permission": "DAILY_REWARD_MULTIPLIER", + "friendlyName": "Multiplier", + "description": "Control the multiplier per role. This is useful to give your donors a little extra. Count is an integer multiplier.", + "canHaveCount": true + } + ], + "name": "dailyRewards", + "description": "Provides daily login rewards with streak tracking", + "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"baseReward\":{\"type\":\"number\",\"title\":\"Base Reward\",\"description\":\"Base amount of currency given for daily rewards. This is multiplied by streak level.\",\"default\":100,\"minimum\":1},\"maxStreak\":{\"type\":\"number\",\"title\":\"Maximum Streak\",\"description\":\"Maximum streak level a player can reach\",\"default\":365,\"minimum\":1},\"milestoneRewards\":{\"type\":\"array\",\"title\":\"Milestone Rewards\",\"description\":\"Additional rewards for reaching certain streak milestones\",\"items\":{\"type\":\"object\",\"properties\":{\"days\":{\"type\":\"number\",\"description\":\"Days needed to reach milestone\",\"minimum\":1},\"reward\":{\"type\":\"number\",\"description\":\"Bonus reward amount\"},\"message\":{\"type\":\"string\",\"description\":\"Message to show when milestone is reached\"}}},\"default\":[{\"days\":7,\"reward\":1000,\"message\":\"You did it! 7 days in a row!\"},{\"days\":30,\"reward\":5000,\"message\":\"A whole month! You're on fire!\"},{\"days\":90,\"reward\":20000,\"message\":\"90 days! You're unstoppable!\"},{\"days\":180,\"reward\":50000,\"message\":\"Half a year! You're a legend!\"},{\"days\":365,\"reward\":150000,\"message\":\"365 days! You're a true champion!\"}]}},\"required\":[\"baseReward\",\"maxStreak\",\"milestoneRewards\"],\"additionalProperties\":false}", + "uiSchema": "{}" } ] \ No newline at end of file diff --git a/packages/web-docs/docs/modules/playerOnboarding.mdx b/packages/web-docs/docs/modules/playerOnboarding.mdx index a2dd3bc608..db39c0f33a 100644 --- a/packages/web-docs/docs/modules/playerOnboarding.mdx +++ b/packages/web-docs/docs/modules/playerOnboarding.mdx @@ -44,4 +44,70 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Features + +### Welcome Messages + +- Automatically triggers when a player connects to the server +- Supports player name variables using player placeholder +- Customizable message content +- Helps create a welcoming atmosphere for new players + +### Starter Kit System + +- Players can claim a starter kit using the `/starterkit` command +- One-time use per player per server +- Configurable item list with support for: + - Item quantities + - Item quality levels + - Multiple items per kit + +## Configuration + +### Message Configuration + +- **Message**: Customize the welcome message that players receive when joining +- Default message: "Welcome player to the server!" +- Use player to include the player's name in the message + +### Starter Kit Configuration + +- **Items**: Configure a list of items to be given to players +- Each item entry supports: + - Item name (must match exact game item ID) + - Amount + - Quality level (where applicable) + - Usage percentage +## Commands + +### /starterkit + +- **Usage**: `/starterkit` +- **Description**: Claims the starter kit items +- **Limitations**: + - Can only be used once per player per server + - Requires starter kit items to be configured +- **Response Messages**: + - Success: "Gave [X] items, enjoy!" + - Already claimed: "You already used starterkit on this server" + - No items configured: "No starter kit items configured. Please ask your server administrator to configure this." + +## Variables + +The module uses Takaro's variable system to track starter kit claims: +- Creates a variable with key 't_starterkit_lock' when a player claims their kit +- Checks this variable to prevent multiple claims +- Variables are server-specific, allowing players to claim starter kits on different servers + +## Best Practices + +1. **Welcome Message** + - Keep messages concise and informative + - Include essential server information or rules + - Use the player placeholder to personalize the message +2. **Starter Kit Items** + - Balance items to help new players without making them overpowered + - Consider your server's economy when selecting items + diff --git a/packages/web-docs/docs/modules/serverMessages.mdx b/packages/web-docs/docs/modules/serverMessages.mdx index 49faf5752d..2062b20e88 100644 --- a/packages/web-docs/docs/modules/serverMessages.mdx +++ b/packages/web-docs/docs/modules/serverMessages.mdx @@ -36,4 +36,107 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Overview + +The Server Messages module sends automated messages to players on your server at regular intervals. Messages are broadcast in sequential order from your configured list. + +## Configuration + +### Basic Setup + +1. Install the module from your server's Modules section +2. Configure at least one message in the messages array +3. Messages must be between 5 and 1024 characters + +### Message Configuration + +```json +{ + "messages": [ + "Welcome to our server! Type /help for commands", // First message + "Join our Discord community: discord.gg/example", // Second message + "Vote daily for rewards at /vote", // Third message + "Check out our shop with /shop" // Fourth message, then cycles back to first + ] +} +``` + +### Scheduling + +- Default: Every 30 minutes (`*/30 * * * *`) +- Common schedules: + ``` + */30 * * * * Every 30 minutes + 0 * * * * Every hour + 0 */4 * * * Every 4 hours + 0 0 * * * Daily at midnight + 0 12 * * * Daily at noon + ``` + +## How It Works + +1. When the cron job triggers (default: every 30 minutes) +2. The next message in sequence is selected from your list +3. After reaching the last message, it starts again from the beginning +4. Example sequence: + ``` + 12:00 - First message + 12:30 - Second message + 13:00 - Third message + 13:30 - Fourth message + 14:00 - First message (cycle repeats) + ``` + +## Best Practices + +### Effective Message Examples + +**Server Rules & Guidelines (Ordered by Priority)** + ``` + PvP is only allowed in designated zones. Type /pvp for info + Protect your base! Use /claim to secure your territory + Raiding is allowed, but no griefing. See /rules + Be respectful in chat. Toxicity = ban + ``` +**Game Tips & Features (Ordered by Progression)** + ``` + New? Type /help to see all commands + Ready to trade? Visit /shop for items + Want a challenge? Join events with /events + Veteran player? Help newbies for rewards! + ``` + +**Community Engagement (Ordered by Importance)** + ``` + Join our Discord for important updates: discord.gg/example + Support us by voting at /vote - daily rewards! + Follow announcements at @serverName + Questions? Our staff team is here to help + ``` + +### Optimization Tips + +1. **Message Order** + - Arrange messages by priority or logical sequence + - Put most important messages first + - Group related messages together + - Consider time between messages when ordering + +2. **Message Categories** + - Start with essential information + - Follow with gameplay tips + - End with community/social messages + - Keep consistent themes together + +3. **Timing** + - Match interval to message count + - Consider peak player times + - Longer intervals for important messages + - Shorter intervals for tips/reminders + +4. **Format** + - Keep messages concise + - Include clear calls to action + - Specify commands when relevant \ No newline at end of file diff --git a/packages/web-docs/docs/modules/teleports.mdx b/packages/web-docs/docs/modules/teleports.mdx index 44225d71d6..25a4b0106f 100644 --- a/packages/web-docs/docs/modules/teleports.mdx +++ b/packages/web-docs/docs/modules/teleports.mdx @@ -183,7 +183,108 @@ export function Module() { --- {/* END AUTO-GENERATED CONTENT */} -# How to add teleports module to your game server +## Features + +- Personal teleport points +- Public/private teleport management +- Waypoint system +- Configurable cooldowns +- Permission-based access control + +## Configuration Options + +When installing the module, administrators can configure: + +- **Timeout**: The cooldown period between teleports (in seconds) +- **Allow Public Teleports**: Toggle whether players can create public teleport points +- **Command Costs**: Set currency costs for teleport commands + +## Permissions System + +### Core Permissions + +1. **Use Teleports** (`TELEPORTS_USE`) + - Allows basic teleport functionality + - Can be configured with a count limit + - Required for using `/tp` and `/tplist` commands +2. **Create Public Teleports** (`TELEPORTS_CREATE_PUBLIC`) + - Allows creation of public teleport points + - Can be configured with a count limit + - Required for `/setpublic` command +3. **Manage Waypoints** (`TELEPORTS_MANAGE_WAYPOINTS`) + - Administrative permission for waypoint management + - Required for `/setwaypoint` and `/deletewaypoint` commands + - No count limit applicable + +### Setting Up Role Permissions + +## Basic Player Access + +1. Edit the default "Player" role +2. Enable "Use Teleports" permission +3. Set maximum number of teleports (recommended: 3-5) +4. Save changes + +## VIP/Donor Setup + +1. Create a new role (e.g., "VIP") +2. Enable both "Use Teleports" and "Create Public Teleports" +3. Set higher teleport limits (recommended: 10-15) +4. Assign to VIP players + +## Admin Configuration + +1. Edit admin role or create dedicated teleport admin role +2. Enable all teleport permissions +3. Enable "Manage Waypoints" permission +4. No need to set count limits + +## Command examples + +| Command | Description | Example | +| ------------------------ | ---------------------------------- | ----------------------- | +| `/tp ` | Teleport to a saved location | `/tp home` | +| `/tplist` | List all available teleport points | `/tplist` | +| `/settp ` | Create a new teleport point | `/settp base` | +| `/deletetp ` | Remove a teleport point | `/deletetp base` | +| `/setpublic ` | Make a teleport point public | `/setpublic shop` | +| `/setprivate ` | Make a teleport point private | `/setprivate base` | +| `/setwaypoint ` | Create a waypoint (admin only) | `/setwaypoint spawn` | +| `/deletewaypoint ` | Delete a waypoint (admin only) | `/deletewaypoint spawn` | +| `/waypoints` | List all available waypoints | `/waypoints` | +| | | | + + +## Best Practices + +1. **Permission Hierarchy** + - Start with basic permissions for all players + - Add additional capabilities for trusted/VIP players + - Reserve waypoint management for administrators +2. **Teleport Limits** + - Set reasonable limits based on server size + - Consider increasing limits for loyal players + - Monitor usage patterns and adjust as needed +3. **Public Teleports** + - Consider restricting public teleport creation to trusted players + - Monitor public teleport locations for abuse + - Use waypoints for essential server locations + +## Troubleshooting + +- If players cannot teleport, check their permission setup +- Verify timeout settings if teleports seem unresponsive +- Check currency requirements if teleports fail +- Ensure waypoint permissions are properly configured for administrators + +## Security Considerations + +- Regularly review public teleport locations +- Monitor for teleport abuse or exploitation +- Consider implementing stricter timeout periods in PvP areas +- Use waypoints instead of public teleports for crucial locations like traders + +## How to add teleports module to your game server This guide walks you through the process of integrating the Teleports module into your game servers. You'll learn how to set up teleport permissions for different player roles, create custom teleport roles for enhanced privileges, and manage teleport-related features effectively. @@ -196,85 +297,85 @@ This guide walks you through the process of integrating the Teleports module int allowFullScreen /> -## Install the module to your game server +### step-by-step guide to install the module to your game server -### Step 1: Navigate to game servers +#### Step 1: Navigate to game servers ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrki4hl00e8356vnaco8xn0-annotated.png) -### Step 2: Select the game server you want the module to be installed on +#### Step 2: Select the game server you want the module to be installed on ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrf44cb002d356vzrgud1i3-annotated.png) -### Step 3: Navigate to Modules of the selected game server +#### Step 3: Navigate to Modules of the selected game server ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyr6qier0009356vyv6luu30-annotated.png) -### Step 4: Click the install button +#### Step 4: Click the install button ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrkmfi000eb356vgb4jcsjl-annotated.png) -### Step 5: This opens the module config +#### Step 5: This opens the module config You will see a list of settings that you can configure. You will also find the list of commands that are part of this module. $tplist - Lists all your set locations. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfgxmx0030356vcsdn6949-annotated.png) -### Step 6: $teleportwaypoint command +#### Step 6: $teleportwaypoint command The module will install aliases for this command corresponding to the waypoint names. The $teleportwaypoint command is a base command that won't be used directly. Instead, the module will create custom commands for each waypoint name you set up. This allows players to teleport to specific locations easily. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfh0ch0031356veuthxwdw-annotated.png) -### Step 7: Select the amount of currency one teleport command costs +#### Step 7: Select the amount of currency one teleport command costs $teleport command teleports you to one of your set locations. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrkn7bf00ee356vec8w2jac-annotated.png) -### Step 8: $setwaypoint creates a new waypoint +#### Step 8: $setwaypoint creates a new waypoint ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfh2zj0032356vxpdnfcao-annotated.png) -### Step 9: $settp sets a location to teleport to +#### Step 9: $settp sets a location to teleport to ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfh6ci0033356v20yp6mlv-annotated.png) -### Step 10: $setpublic sets a teleport to be public, allowing other players to teleport to it +#### Step 10: $setpublic sets a teleport to be public, allowing other players to teleport to it ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfflru002w356vejuvlnyy-annotated.png) -### Step 11: $setprivate sets a teleport to be private, only the teleport owner can teleport to it +#### Step 11: $setprivate sets a teleport to be private, only the teleport owner can teleport to it ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfhf6t0034356v8zo9l6ei-annotated.png) -### Step 12: $listwaypoints lists all waypoints +#### Step 12: $listwaypoints lists all waypoints ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrffrkd002y356vehfrrtgl-annotated.png) -### Step 13: $deletewaypoint deletes a waypoint +#### Step 13: $deletewaypoint deletes a waypoint ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfhzvi0037356vdm1sez5x-annotated.png) -### Step 14: Click Install module button when you are finished with the config +#### Step 14: Click Install module button when you are finished with the config ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrkndxi00ef356vjx98hsqg-annotated.png) -## Assign the permission to the right role +### Assign the permission to the right role -### Step 15: Now you need to assign the permission to the right role +#### Step 15: Now you need to assign the permission to the right role Now that you've installed the Teleports module, the next step is to set up the appropriate permissions. Navigate to the "Roles" section in the global navigation to begin assigning teleport-related permissions to player roles. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrenji3001q356vs5b8uta8-annotated.png) -### Step 16: Now you need to assign the permission to the right role +#### Step 16: Now you need to assign the permission to the right role You can decide to add this permission to the standard Player role, or you can create a new role dedicated for teleports. In this guide, we are going to do both. We will first start with the Player role. The Player role is the role that all players will have when they join your game server. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrejj8w0011356vg8t94k5p-annotated.png) -### Step 17: Teleports Module Permissions Overview +#### Step 17: Teleports Module Permissions Overview Create Public Teleports: Allows players to create teleport points accessible to others. Use Teleports: Enables players to use existing teleport points. Manage Waypoints: Permits creating, modifying, and deleting personal or shared waypoints. @@ -284,55 +385,55 @@ Toggle the switch to the right (on position). Set a cost in the "Amount" field i ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyre55ge000x356vqh7cq87h-annotated.png) -### Step 18: Click Save changes button +#### Step 18: Click Save changes button ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrknird00eg356vsmu74np2-annotated.png) -### Step 19: Add a custom role for Teleports +#### Step 19: Add a custom role for Teleports Creating a dedicated teleport role allows for more flexible permission management. This is useful when you want to give enhanced teleport capabilities to certain players without changing permissions for all users. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrknlij00eh356v8bmmu32u-annotated.png) -### Step 20: Give your new role a recognizable name +#### Step 20: Give your new role a recognizable name ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrew0c9001v356v6qdqb47a-annotated.png) -### Step 21: Configure teleport permissions for the new role +#### Step 21: Configure teleport permissions for the new role You'll see the same options as in Step 17: Create Public Teleports Use Teleports Manage Waypoints ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyreielj0010356vdutepys1-annotated.png) -## Assign the role to the right player +### Assign the role to the right player -### Step 22: Go to Players to assign the right role +#### Step 22: Go to Players to assign the right role ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrknq0d00ei356vbuve02c5-annotated.png) -### Step 23: Navigate to the player you want to assign the special role to +#### Step 23: Navigate to the player you want to assign the special role to ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrepqj1001u356v5fepto4w-annotated.png) -### Step 24: Click Assign role button +#### Step 24: Click Assign role button ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrf8n5a002e356vmyn5b9ee-annotated.png) -### Step 25: Select the right role +#### Step 25: Select the right role ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrknuo400ej356vbbjkv8uh-annotated.png) -### Step 26: Choose extra teleports from the list +#### Step 26: Choose extra teleports from the list ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrf8wcs002f356vacwkzk01-annotated.png) -### Step 27: Choose your preferred game server +#### Step 27: Choose your preferred game server Choose to activate the teleport role on individual game servers or globally across all servers, allowing flexible permission management based on your game's structure and needs. ![](https://layerpath-recording-prod.s3-accelerate.amazonaws.com/clyr0pthc0008ju0cpgk6zvy7/clyr4po6b0065l90ch2iwjbq3/clyrfj6jk0039356vwo9846tm-annotated.png) -### Step 28: Click Save changes button +#### Step 28: Click Save changes button You made it to the end! diff --git a/packages/web-docs/docs/modules/timedShutdown.mdx b/packages/web-docs/docs/modules/timedShutdown.mdx index f94a73bba5..e23f3ea6d5 100644 --- a/packages/web-docs/docs/modules/timedShutdown.mdx +++ b/packages/web-docs/docs/modules/timedShutdown.mdx @@ -41,4 +41,77 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +The Timed Shutdown module provides automated server shutdown functionality with configurable warning messages to players. This guide explains how to configure and use the module effectively. + +## Overview + +The Timed Shutdown module performs two main functions: + +1. Sends a warning message to all players before shutdown +2. Automatically shuts down the server at a specified time + +## Configuration + +### Warning Message + +- **Setting**: `warningMessage` +- **Default**: "Server is shutting down in 5 minutes!" +- **Description**: The message that will be broadcast to all players 5 minutes before shutdown +- **Length Limits**: 1-1024 characters + +### Timing + +The module operates by default on a fixed schedule: + +- **Warning Time**: 3:25 AM (server time) +- **Shutdown Time**: 3:30 AM (server time) + +These times are set using cron expressions: + +- Warning message: `3 25 * * *` +- Server shutdown: `3 30 * * *` + +You can change the cron jobs to your liking. + +## Installation Steps + +1. Navigate to your game server's Modules section +2. Click "Install New Module" +3. Search for "timedShutdown" +4. Configure your warning message +5. Click Install + +## Best Practices + +### Warning Message Format + +Consider including: + +- Specific time remaining until shutdown +- Reason for shutdown (e.g., maintenance, daily restart) +- When the server will be back online + +Example message: + +`Server is shutting down in 5 minutes for daily maintenance. We'll be back online in ~10 minutes. Please find a safe spot!` + +### Server Time Considerations + +- Ensure your server's time zone is correctly set +- Choose shutdown times during low-activity periods +- Account for regular maintenance windows + +## Troublesh ooting + +Common issues and solutions: + +1. **Shutdown not occurring** + - Verify server time matches expected shutdown time + - Check module installation status + - Review server logs for errors +2. **Warning message not displaying** + - Confirm message is properly configured + - Check chat/message system functionality + - Verify module permissions \ No newline at end of file diff --git a/packages/web-docs/docs/modules/utils.mdx b/packages/web-docs/docs/modules/utils.mdx index 0684f3cb4d..6970e60fea 100644 --- a/packages/web-docs/docs/modules/utils.mdx +++ b/packages/web-docs/docs/modules/utils.mdx @@ -53,4 +53,50 @@ export function Module() { --- -{/* END AUTO-GENERATED CONTENT */} \ No newline at end of file +{/* END AUTO-GENERATED CONTENT */} + +## Features + +### Commands + +## 1. Ping Command + +- **Trigger**: `/ping` +- **Description**: A simple command that replies with "Pong!". This command is useful for: + - Testing if the server connection is working + - Verifying command functionality + - Checking player permissions + - Basic latency testing + +## 2. Help Command + +- **Trigger**: `/help [command]` +- **Description**: Provides information about available commands and their usage +- **Arguments**: + - `command` (optional): The specific command to get help for + - Default: Shows all available commands if no specific command is specified + +### Usage Examples + + +`/ping > Response: "Pong!" /help > Response: Lists all available commands` +`/help ping > Response: Shows detailed help for the ping command` + +## Technical Details + +### Module Configuration + +The Utils module has a minimal configuration schema with no required settings, making it simple to deploy and maintain. + +### Permissions + +The Utils module's commands are typically available to all players by default, as they provide essential functionality for server interaction. + +## Best Practices + +1. **Server Setup** + - Install the Utils module first when setting up a new server + - Keep it enabled to ensure players can always access basic commands +3. **Troubleshooting** + - Use the ping command as a first step in diagnosing connection issues + - Refer players to the help command when they're unsure about command usage \ No newline at end of file diff --git a/packages/web-main/src/queries/role.tsx b/packages/web-main/src/queries/role.tsx index cfd04d3a7a..8531c3d1d2 100644 --- a/packages/web-main/src/queries/role.tsx +++ b/packages/web-main/src/queries/role.tsx @@ -72,9 +72,9 @@ export const useRoleCreate = () => { return mutationWrapper( useMutation, RoleCreateInputDTO>({ mutationFn: async (role) => (await apiClient.role.roleControllerCreate(role)).data.data, - onSuccess: async (newRole) => { + onSuccess: (newRole) => { enqueueSnackbar('Role created!', { variant: 'default', type: 'success' }); - await queryClient.invalidateQueries({ queryKey: roleKeys.list() }); + queryClient.invalidateQueries({ queryKey: roleKeys.list() }); queryClient.setQueryData(roleKeys.detail(newRole.id), newRole); }, }), diff --git a/packages/web-main/src/routes/-module-builder/Editor/monacoCustomTypes.json b/packages/web-main/src/routes/-module-builder/Editor/monacoCustomTypes.json index bfa2afa973..bff77c0aba 100644 --- a/packages/web-main/src/routes/-module-builder/Editor/monacoCustomTypes.json +++ b/packages/web-main/src/routes/-module-builder/Editor/monacoCustomTypes.json @@ -1 +1 @@ -{"file://node_modules/axios/package.json":"{\"name\":\"axios\",\"types\":\"./index.d.ts\"}","file://node_modules/axios/index.d.ts":"// TypeScript Version: 4.7\ntype AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null;\n\ninterface RawAxiosHeaders {\n [key: string]: AxiosHeaderValue;\n}\n\ntype MethodsHeaders = Partial<{\n [Key in Method as Lowercase]: AxiosHeaders;\n} & {common: AxiosHeaders}>;\n\ntype AxiosHeaderMatcher = string | RegExp | ((this: AxiosHeaders, value: string, name: string) => boolean);\n\ntype AxiosHeaderParser = (this: AxiosHeaders, value: AxiosHeaderValue, header: string) => any;\n\nclass AxiosHeaders {\n constructor(\n headers?: RawAxiosHeaders | AxiosHeaders | string\n );\n\n [key: string]: any;\n\n set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders;\n\n get(headerName: string, parser: RegExp): RegExpExecArray | null;\n get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue;\n\n has(header: string, matcher?: AxiosHeaderMatcher): boolean;\n\n delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean;\n\n clear(matcher?: AxiosHeaderMatcher): boolean;\n\n normalize(format: boolean): AxiosHeaders;\n\n concat(...targets: Array): AxiosHeaders;\n\n toJSON(asStrings?: boolean): RawAxiosHeaders;\n\n static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders;\n\n static accessor(header: string | string[]): AxiosHeaders;\n\n static concat(...targets: Array): AxiosHeaders;\n\n setContentType(value: ContentType, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentType(parser?: RegExp): RegExpExecArray | null;\n getContentType(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentType(matcher?: AxiosHeaderMatcher): boolean;\n\n setContentLength(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentLength(parser?: RegExp): RegExpExecArray | null;\n getContentLength(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentLength(matcher?: AxiosHeaderMatcher): boolean;\n\n setAccept(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getAccept(parser?: RegExp): RegExpExecArray | null;\n getAccept(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasAccept(matcher?: AxiosHeaderMatcher): boolean;\n\n setUserAgent(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getUserAgent(parser?: RegExp): RegExpExecArray | null;\n getUserAgent(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasUserAgent(matcher?: AxiosHeaderMatcher): boolean;\n\n setContentEncoding(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentEncoding(parser?: RegExp): RegExpExecArray | null;\n getContentEncoding(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentEncoding(matcher?: AxiosHeaderMatcher): boolean;\n\n setAuthorization(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getAuthorization(parser?: RegExp): RegExpExecArray | null;\n getAuthorization(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasAuthorization(matcher?: AxiosHeaderMatcher): boolean;\n\n [Symbol.iterator](): IterableIterator<[string, AxiosHeaderValue]>;\n}\n\ntype CommonRequestHeadersList = 'Accept' | 'Content-Length' | 'User-Agent' | 'Content-Encoding' | 'Authorization';\n\ntype ContentType = AxiosHeaderValue | 'text/html' | 'text/plain' | 'multipart/form-data' | 'application/json' | 'application/x-www-form-urlencoded' | 'application/octet-stream';\n\ntype RawAxiosRequestHeaders = Partial;\n\ntype AxiosRequestHeaders = RawAxiosRequestHeaders & AxiosHeaders;\n\ntype CommonResponseHeadersList = 'Server' | 'Content-Type' | 'Content-Length' | 'Cache-Control'| 'Content-Encoding';\n\ntype RawCommonResponseHeaders = {\n [Key in CommonResponseHeadersList]: AxiosHeaderValue;\n} & {\n \"set-cookie\": string[];\n};\n\ntype RawAxiosResponseHeaders = Partial;\n\ntype AxiosResponseHeaders = RawAxiosResponseHeaders & AxiosHeaders;\n\ninterface AxiosRequestTransformer {\n (this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any;\n}\n\ninterface AxiosResponseTransformer {\n (this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number): any;\n}\n\ninterface AxiosAdapter {\n (config: InternalAxiosRequestConfig): AxiosPromise;\n}\n\ninterface AxiosBasicCredentials {\n username: string;\n password: string;\n}\n\ninterface AxiosProxyConfig {\n host: string;\n port: number;\n auth?: AxiosBasicCredentials;\n protocol?: string;\n}\n\nenum HttpStatusCode {\n Continue = 100,\n SwitchingProtocols = 101,\n Processing = 102,\n EarlyHints = 103,\n Ok = 200,\n Created = 201,\n Accepted = 202,\n NonAuthoritativeInformation = 203,\n NoContent = 204,\n ResetContent = 205,\n PartialContent = 206,\n MultiStatus = 207,\n AlreadyReported = 208,\n ImUsed = 226,\n MultipleChoices = 300,\n MovedPermanently = 301,\n Found = 302,\n SeeOther = 303,\n NotModified = 304,\n UseProxy = 305,\n Unused = 306,\n TemporaryRedirect = 307,\n PermanentRedirect = 308,\n BadRequest = 400,\n Unauthorized = 401,\n PaymentRequired = 402,\n Forbidden = 403,\n NotFound = 404,\n MethodNotAllowed = 405,\n NotAcceptable = 406,\n ProxyAuthenticationRequired = 407,\n RequestTimeout = 408,\n Conflict = 409,\n Gone = 410,\n LengthRequired = 411,\n PreconditionFailed = 412,\n PayloadTooLarge = 413,\n UriTooLong = 414,\n UnsupportedMediaType = 415,\n RangeNotSatisfiable = 416,\n ExpectationFailed = 417,\n ImATeapot = 418,\n MisdirectedRequest = 421,\n UnprocessableEntity = 422,\n Locked = 423,\n FailedDependency = 424,\n TooEarly = 425,\n UpgradeRequired = 426,\n PreconditionRequired = 428,\n TooManyRequests = 429,\n RequestHeaderFieldsTooLarge = 431,\n UnavailableForLegalReasons = 451,\n InternalServerError = 500,\n NotImplemented = 501,\n BadGateway = 502,\n ServiceUnavailable = 503,\n GatewayTimeout = 504,\n HttpVersionNotSupported = 505,\n VariantAlsoNegotiates = 506,\n InsufficientStorage = 507,\n LoopDetected = 508,\n NotExtended = 510,\n NetworkAuthenticationRequired = 511,\n}\n\ntype Method =\n | 'get' | 'GET'\n | 'delete' | 'DELETE'\n | 'head' | 'HEAD'\n | 'options' | 'OPTIONS'\n | 'post' | 'POST'\n | 'put' | 'PUT'\n | 'patch' | 'PATCH'\n | 'purge' | 'PURGE'\n | 'link' | 'LINK'\n | 'unlink' | 'UNLINK';\n\ntype ResponseType =\n | 'arraybuffer'\n | 'blob'\n | 'document'\n | 'json'\n | 'text'\n | 'stream'\n | 'formdata';\n\ntype responseEncoding =\n | 'ascii' | 'ASCII'\n | 'ansi' | 'ANSI'\n | 'binary' | 'BINARY'\n | 'base64' | 'BASE64'\n | 'base64url' | 'BASE64URL'\n | 'hex' | 'HEX'\n | 'latin1' | 'LATIN1'\n | 'ucs-2' | 'UCS-2'\n | 'ucs2' | 'UCS2'\n | 'utf-8' | 'UTF-8'\n | 'utf8' | 'UTF8'\n | 'utf16le' | 'UTF16LE';\n\ninterface TransitionalOptions {\n silentJSONParsing?: boolean;\n forcedJSONParsing?: boolean;\n clarifyTimeoutError?: boolean;\n}\n\ninterface GenericAbortSignal {\n readonly aborted: boolean;\n onabort?: ((...args: any) => any) | null;\n addEventListener?: (...args: any) => any;\n removeEventListener?: (...args: any) => any;\n}\n\ninterface FormDataVisitorHelpers {\n defaultVisitor: SerializerVisitor;\n convertValue: (value: any) => any;\n isVisitable: (value: any) => boolean;\n}\n\ninterface SerializerVisitor {\n (\n this: GenericFormData,\n value: any,\n key: string | number,\n path: null | Array,\n helpers: FormDataVisitorHelpers\n ): boolean;\n}\n\ninterface SerializerOptions {\n visitor?: SerializerVisitor;\n dots?: boolean;\n metaTokens?: boolean;\n indexes?: boolean | null;\n}\n\n// tslint:disable-next-line\ninterface FormSerializerOptions extends SerializerOptions {\n}\n\ninterface ParamEncoder {\n (value: any, defaultEncoder: (value: any) => any): any;\n}\n\ninterface CustomParamsSerializer {\n (params: Record, options?: ParamsSerializerOptions): string;\n}\n\ninterface ParamsSerializerOptions extends SerializerOptions {\n encode?: ParamEncoder;\n serialize?: CustomParamsSerializer;\n}\n\ntype MaxUploadRate = number;\n\ntype MaxDownloadRate = number;\n\ntype BrowserProgressEvent = any;\n\ninterface AxiosProgressEvent {\n loaded: number;\n total?: number;\n progress?: number;\n bytes: number;\n rate?: number;\n estimated?: number;\n upload?: boolean;\n download?: boolean;\n event?: BrowserProgressEvent;\n lengthComputable: boolean;\n}\n\ntype Milliseconds = number;\n\ntype AxiosAdapterName = 'fetch' | 'xhr' | 'http' | string;\n\ntype AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;\n\ntype AddressFamily = 4 | 6 | undefined;\n\ninterface LookupAddressEntry {\n address: string;\n family?: AddressFamily;\n}\n\ntype LookupAddress = string | LookupAddressEntry;\n\ninterface AxiosRequestConfig {\n url?: string;\n method?: Method | string;\n baseURL?: string;\n transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];\n transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];\n headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;\n params?: any;\n paramsSerializer?: ParamsSerializerOptions | CustomParamsSerializer;\n data?: D;\n timeout?: Milliseconds;\n timeoutErrorMessage?: string;\n withCredentials?: boolean;\n adapter?: AxiosAdapterConfig | AxiosAdapterConfig[];\n auth?: AxiosBasicCredentials;\n responseType?: ResponseType;\n responseEncoding?: responseEncoding | string;\n xsrfCookieName?: string;\n xsrfHeaderName?: string;\n onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;\n onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;\n maxContentLength?: number;\n validateStatus?: ((status: number) => boolean) | null;\n maxBodyLength?: number;\n maxRedirects?: number;\n maxRate?: number | [MaxUploadRate, MaxDownloadRate];\n beforeRedirect?: (options: Record, responseDetails: {headers: Record, statusCode: HttpStatusCode}) => void;\n socketPath?: string | null;\n transport?: any;\n httpAgent?: any;\n httpsAgent?: any;\n proxy?: AxiosProxyConfig | false;\n cancelToken?: CancelToken;\n decompress?: boolean;\n transitional?: TransitionalOptions;\n signal?: GenericAbortSignal;\n insecureHTTPParser?: boolean;\n env?: {\n FormData?: new (...args: any[]) => object;\n };\n formSerializer?: FormSerializerOptions;\n family?: AddressFamily;\n lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |\n ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);\n withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);\n fetchOptions?: Record;\n}\n\n// Alias\ntype RawAxiosRequestConfig = AxiosRequestConfig;\n\ninterface InternalAxiosRequestConfig extends AxiosRequestConfig {\n headers: AxiosRequestHeaders;\n}\n\ninterface HeadersDefaults {\n common: RawAxiosRequestHeaders;\n delete: RawAxiosRequestHeaders;\n get: RawAxiosRequestHeaders;\n head: RawAxiosRequestHeaders;\n post: RawAxiosRequestHeaders;\n put: RawAxiosRequestHeaders;\n patch: RawAxiosRequestHeaders;\n options?: RawAxiosRequestHeaders;\n purge?: RawAxiosRequestHeaders;\n link?: RawAxiosRequestHeaders;\n unlink?: RawAxiosRequestHeaders;\n}\n\ninterface AxiosDefaults extends Omit, 'headers'> {\n headers: HeadersDefaults;\n}\n\ninterface CreateAxiosDefaults extends Omit, 'headers'> {\n headers?: RawAxiosRequestHeaders | AxiosHeaders | Partial;\n}\n\ninterface AxiosResponse {\n data: T;\n status: number;\n statusText: string;\n headers: RawAxiosResponseHeaders | AxiosResponseHeaders;\n config: InternalAxiosRequestConfig;\n request?: any;\n}\n\nclass AxiosError extends Error {\n constructor(\n message?: string,\n code?: string,\n config?: InternalAxiosRequestConfig,\n request?: any,\n response?: AxiosResponse\n );\n\n config?: InternalAxiosRequestConfig;\n code?: string;\n request?: any;\n response?: AxiosResponse;\n isAxiosError: boolean;\n status?: number;\n toJSON: () => object;\n cause?: Error;\n static from(\n error: Error | unknown,\n code?: string,\n config?: InternalAxiosRequestConfig,\n request?: any,\n response?: AxiosResponse,\n customProps?: object,\n): AxiosError;\n static readonly ERR_FR_TOO_MANY_REDIRECTS = \"ERR_FR_TOO_MANY_REDIRECTS\";\n static readonly ERR_BAD_OPTION_VALUE = \"ERR_BAD_OPTION_VALUE\";\n static readonly ERR_BAD_OPTION = \"ERR_BAD_OPTION\";\n static readonly ERR_NETWORK = \"ERR_NETWORK\";\n static readonly ERR_DEPRECATED = \"ERR_DEPRECATED\";\n static readonly ERR_BAD_RESPONSE = \"ERR_BAD_RESPONSE\";\n static readonly ERR_BAD_REQUEST = \"ERR_BAD_REQUEST\";\n static readonly ERR_NOT_SUPPORT = \"ERR_NOT_SUPPORT\";\n static readonly ERR_INVALID_URL = \"ERR_INVALID_URL\";\n static readonly ERR_CANCELED = \"ERR_CANCELED\";\n static readonly ECONNABORTED = \"ECONNABORTED\";\n static readonly ETIMEDOUT = \"ETIMEDOUT\";\n}\n\nclass CanceledError extends AxiosError {\n}\n\ntype AxiosPromise = Promise>;\n\ninterface CancelStatic {\n new (message?: string): Cancel;\n}\n\ninterface Cancel {\n message: string | undefined;\n}\n\ninterface Canceler {\n (message?: string, config?: AxiosRequestConfig, request?: any): void;\n}\n\ninterface CancelTokenStatic {\n new (executor: (cancel: Canceler) => void): CancelToken;\n source(): CancelTokenSource;\n}\n\ninterface CancelToken {\n promise: Promise;\n reason?: Cancel;\n throwIfRequested(): void;\n}\n\ninterface CancelTokenSource {\n token: CancelToken;\n cancel: Canceler;\n}\n\ninterface AxiosInterceptorOptions {\n synchronous?: boolean;\n runWhen?: (config: InternalAxiosRequestConfig) => boolean;\n}\n\ninterface AxiosInterceptorManager {\n use(onFulfilled?: ((value: V) => V | Promise) | null, onRejected?: ((error: any) => any) | null, options?: AxiosInterceptorOptions): number;\n eject(id: number): void;\n clear(): void;\n}\n\nclass Axios {\n constructor(config?: AxiosRequestConfig);\n defaults: AxiosDefaults;\n interceptors: {\n request: AxiosInterceptorManager;\n response: AxiosInterceptorManager;\n };\n getUri(config?: AxiosRequestConfig): string;\n request, D = any>(config: AxiosRequestConfig): Promise;\n get, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n delete, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n head, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n options, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n post, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n put, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n patch, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n postForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n putForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n patchForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n}\n\ninterface AxiosInstance extends Axios {\n , D = any>(config: AxiosRequestConfig): Promise;\n , D = any>(url: string, config?: AxiosRequestConfig): Promise;\n\n defaults: Omit & {\n headers: HeadersDefaults & {\n [key: string]: AxiosHeaderValue\n }\n };\n}\n\ninterface GenericFormData {\n append(name: string, value: any, options?: any): any;\n}\n\ninterface GenericHTMLFormElement {\n name: string;\n method: string;\n submit(): void;\n}\n\nfunction getAdapter(adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined): AxiosAdapter;\n\nfunction toFormData(sourceObj: object, targetFormData?: GenericFormData, options?: FormSerializerOptions): GenericFormData;\n\nfunction formToJSON(form: GenericFormData|GenericHTMLFormElement): object;\n\nfunction isAxiosError(payload: any): payload is AxiosError;\n\nfunction spread(callback: (...args: T[]) => R): (array: T[]) => R;\n\nfunction isCancel(value: any): value is Cancel;\n\nfunction all(values: Array>): Promise;\n\ninterface AxiosStatic extends AxiosInstance {\n create(config?: CreateAxiosDefaults): AxiosInstance;\n Cancel: CancelStatic;\n CancelToken: CancelTokenStatic;\n Axios: typeof Axios;\n AxiosError: typeof AxiosError;\n HttpStatusCode: typeof HttpStatusCode;\n readonly VERSION: string;\n isCancel: typeof isCancel;\n all: typeof all;\n spread: typeof spread;\n isAxiosError: typeof isAxiosError;\n toFormData: typeof toFormData;\n formToJSON: typeof formToJSON;\n getAdapter: typeof getAdapter;\n CanceledError: typeof CanceledError;\n AxiosHeaders: typeof AxiosHeaders;\n}\n\ndeclare const axios: AxiosStatic;\n\ndefault axios;\n","file://node_modules/form-data/package.json":"{\"name\":\"form-data\",\"types\":\"./index.d.ts\"}","file://node_modules/form-data/index.d.ts":"// Definitions by: Carlos Ballesteros Velasco \n// Leon Yu \n// BendingBender \n// Maple Miao \n\n/// \n\n= FormData;\n\n// Extracted because @types/node doesn't export interfaces.\ninterface ReadableOptions {\n highWaterMark?: number;\n encoding?: string;\n objectMode?: boolean;\n read?(this: stream.Readable, size: number): void;\n destroy?(this: stream.Readable, error: Error | null, callback: (error: Error | null) => void): void;\n autoDestroy?: boolean;\n}\n\ninterface Options extends ReadableOptions {\n writable?: boolean;\n readable?: boolean;\n dataSize?: number;\n maxDataSize?: number;\n pauseStreams?: boolean;\n}\n\ndeclare class FormData extends stream.Readable {\n constructor(options?: Options);\n append(key: string, value: any, options?: FormData.AppendOptions | string): void;\n getHeaders(userHeaders?: FormData.Headers): FormData.Headers;\n submit(\n params: string | FormData.SubmitOptions,\n callback?: (error: Error | null, response: http.IncomingMessage) => void\n ): http.ClientRequest;\n getBuffer(): Buffer;\n setBoundary(boundary: string): void;\n getBoundary(): string;\n getLength(callback: (err: Error | null, length: number) => void): void;\n getLengthSync(): number;\n hasKnownLength(): boolean;\n}\n\ndeclare namespace FormData {\n interface Headers {\n [key: string]: any;\n }\n\n interface AppendOptions {\n header?: string | Headers;\n knownLength?: number;\n filename?: string;\n filepath?: string;\n contentType?: string;\n }\n\n interface SubmitOptions extends http.RequestOptions {\n protocol?: 'https:' | 'http:';\n }\n}\n","file://node_modules/@takaro/helpers/package.json":"{\"name\":\"@takaro/helpers\",\"types\":\"dist/main.d.ts\"}","file://node_modules/@takaro/helpers/dist/TakaroUserError.d.ts":"declare class TakaroUserError extends Error {\n constructor(message: string);\n}\n","file://node_modules/@takaro/helpers/dist/checkPermission.d.ts":"declare function checkPermission(player: PlayerOnGameserverOutputWithRolesDTO, permission: string): any;\n","file://node_modules/@takaro/helpers/dist/config.d.ts":"interface IHelpersConfig extends IBaseConfig {\n apiClient: {\n token: string;\n url: string;\n };\n data: string;\n}\ndeclare const config: Config;\n{};\n","file://node_modules/@takaro/helpers/dist/getData.d.ts":"declare function getData(): any;\n","file://node_modules/@takaro/helpers/dist/getTakaro.d.ts":"declare function getTakaro(data: Record, logger?: Pick): {\n takaro: Client;\n data: Record;\n};\n","file://node_modules/@takaro/helpers/dist/main.d.ts":"{ getTakaro } from './getTakaro.js';\n{ checkPermission } from './checkPermission.js';\n{ nextCronJobRun } from './nextCronJobRun.js';\n{ TakaroUserError } from './TakaroUserError.js';\n* as _ from 'lodash-es';\n* as axios from 'axios';\ndeclare const takaro: Client;\n","file://node_modules/@takaro/helpers/dist/nextCronJobRun.d.ts":"declare const nextCronJobRun: (cron: string) => Date | null;\n","file://node_modules/croner/package.json":"{\"name\":\"croner\",\"types\":\"./types/croner.d.ts\"}","file://node_modules/croner/./types/croner.d.ts":"type TimePoint = {\n /**\n * - 1970--\n */\n y: number;\n /**\n * - 1-12\n */\n m: number;\n /**\n * - 1-31\n */\n d: number;\n /**\n * - 0-24\n */\n h: number;\n /**\n * - 0-60 Minute\n */\n i: number;\n /**\n * - 0-60\n */\n s: number;\n /**\n * - Time zone in IANA database format 'Europe/Stockholm'\n */\n tz: string;\n};\ntype CatchCallbackFn = (e: unknown, job: Cron) => any;\ntype ProtectCallbackFn = (job: Cron) => any;\n/**\n * - Cron scheduler options\n */\ntype CronOptions = {\n /**\n * - Name of a job\n */\n name?: string;\n /**\n * - Job is paused\n */\n paused?: boolean;\n /**\n * - Job is about to be killed or killed\n */\n kill?: boolean;\n /**\n * - Continue exection even if a unhandled error is thrown by triggered function\n * - If set to a function, execute function on catching the error.\n */\n catch?: boolean | CatchCallbackFn;\n /**\n * - Abort job instantly if nothing else keeps the event loop running.\n */\n unref?: boolean;\n /**\n * - Maximum nuber of executions\n */\n maxRuns?: number;\n /**\n * - Minimum interval between executions, in seconds\n */\n interval?: number;\n /**\n * - Skip current run if job is already running\n */\n protect?: boolean | ProtectCallbackFn;\n /**\n * - When to start running\n */\n startAt?: string | Date;\n /**\n * - When to stop running\n */\n stopAt?: string | Date;\n /**\n * - Time zone in Europe/Stockholm format\n */\n timezone?: string;\n /**\n * - Offset from UTC in minutes\n */\n utcOffset?: number;\n /**\n * - Combine day-of-month and day-of-week using true = OR, false = AND. Default is true = OR.\n */\n legacyMode?: boolean;\n /**\n * - Used to pass any object to scheduled function\n */\n context?: unknown;\n};\n/**\n * Name for each part of the cron pattern\n */\ntype CronPatternPart = (\"second\" | \"minute\" | \"hour\" | \"day\" | \"month\" | \"dayOfWeek\");\n/**\n * Offset, 0 or -1.\n *\n * 0 offset is used for seconds,minutes and hours as they start on 1.\n * -1 on days and months, as they start on 0\n */\ntype CronIndexOffset = number;\n/**\n * Cron entrypoint\n *\n * @constructor\n * @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string\n * @param {CronOptions|Function} [fnOrOptions1] - Options or function to be run each iteration of pattern\n * @param {CronOptions|Function} [fnOrOptions2] - Options or function to be run each iteration of pattern\n * @returns {Cron}\n */\nfunction Cron(pattern: string | Date, fnOrOptions1?: CronOptions | Function, fnOrOptions2?: CronOptions | Function): Cron;\nclass Cron {\n /**\n * Cron entrypoint\n *\n * @constructor\n * @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string\n * @param {CronOptions|Function} [fnOrOptions1] - Options or function to be run each iteration of pattern\n * @param {CronOptions|Function} [fnOrOptions2] - Options or function to be run each iteration of pattern\n * @returns {Cron}\n */\n constructor(pattern: string | Date, fnOrOptions1?: CronOptions | Function, fnOrOptions2?: CronOptions | Function);\n /**\n * @public\n * @type {string|undefined} */\n public name: string | undefined;\n /**\n * @public\n * @type {CronOptions} */\n public options: CronOptions;\n /**\n * Encapsulate all internal states in an object.\n * Duplicate all options that can change to internal states, for example maxRuns and paused.\n * @private\n */\n private _states;\n fn: Function | CronOptions;\n /**\n * Find next runtime, based on supplied date. Strips milliseconds.\n *\n * @param {CronDate|Date|string} [prev] - Date to start from\n * @returns {Date | null} - Next run time\n */\n nextRun(prev?: CronDate | Date | string): Date | null;\n /**\n * Find next n runs, based on supplied date. Strips milliseconds.\n *\n * @param {number} n - Number of runs to enumerate\n * @param {Date|string} [previous] - Date to start from\n * @returns {Date[]} - Next n run times\n */\n nextRuns(n: number, previous?: Date | string): Date[];\n /**\n * Return the original pattern, if there was one\n *\n * @returns {string|undefined} - Original pattern\n */\n getPattern(): string | undefined;\n /**\n * Indicates whether or not the cron job is scheduled and running, e.g. awaiting next trigger\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isRunning(): boolean;\n /**\n * Indicates whether or not the cron job is permanently stopped\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isStopped(): boolean;\n /**\n * Indicates whether or not the cron job is currently working\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isBusy(): boolean;\n /**\n * Return current/previous run start time\n * @public\n *\n * @returns {Date | null} - Previous run time\n */\n public currentRun(): Date | null;\n /**\n * Return previous run start time\n * @public\n *\n * @returns {Date | null} - Previous run time\n */\n public previousRun(): Date | null;\n /**\n * Returns number of milliseconds to next run\n * @public\n *\n * @param {CronDate|Date|string} [prev] - Starting date, defaults to now - minimum interval\n * @returns {number | null}\n */\n public msToNext(prev?: CronDate | Date | string): number | null;\n /**\n * Stop execution\n *\n * Running this will forcefully stop the job, and prevent furter exection. `.resume()` will not work after stopping.\n * It will also be removed from the scheduledJobs array if it were named.\n *\n * @public\n */\n public stop(): void;\n /**\n * Pause execution\n * @public\n *\n * @returns {boolean} - Wether pause was successful\n */\n public pause(): boolean;\n /**\n * Resume execution\n * @public\n *\n * @returns {boolean} - Wether resume was successful\n */\n public resume(): boolean;\n /**\n * Schedule a new job\n * @public\n *\n * @param {Function} func - Function to be run each iteration of pattern\n * @returns {Cron}\n */\n public schedule(func: Function): Cron;\n private _trigger;\n /**\n * Trigger a run manually\n * @public\n */\n public trigger(): Promise;\n private _checkTrigger;\n private _next;\n private _calculatePreviousRun;\n}\nnamespace Cron {\n export { Cron };\n export { scheduledJobs };\n}\n/**\n * An array containing all named cron jobs.\n *\n * @constant\n * @type {Cron[]}\n */\nconst scheduledJobs: Cron[];\n/**\n * Converts date to CronDate\n * @constructor\n *\n * @param {CronDate|Date|string} [d] - Input date, if using string representation ISO 8001 (2015-11-24T19:40:00) local timezone is expected\n * @param {string|number} [tz] - String representation of target timezone in Europe/Stockholm format, or a number representing offset in minutes.\n*/\ndeclare function CronDate(d?: CronDate | Date | string, tz?: string | number): void;\ndeclare class CronDate {\n /**\n * Converts date to CronDate\n * @constructor\n *\n * @param {CronDate|Date|string} [d] - Input date, if using string representation ISO 8001 (2015-11-24T19:40:00) local timezone is expected\n * @param {string|number} [tz] - String representation of target timezone in Europe/Stockholm format, or a number representing offset in minutes.\n */\n constructor(d?: CronDate | Date | string, tz?: string | number);\n /**\n * TimeZone\n * @type {string|number|undefined}\n */\n tz: string | number | undefined;\n private isNthWeekdayOfMonth;\n private fromDate;\n ms: number;\n second: number;\n minute: number;\n hour: number;\n day: number;\n month: number;\n year: number;\n private fromCronDate;\n private apply;\n private fromString;\n private findNext;\n private recurse;\n /**\n * Increment to next run time\n * @public\n *\n * @param {string} pattern - The pattern used to increment current state\n * @param {CronOptions} options - Cron options used for incrementing\n * @param {boolean} [hasPreviousRun] - If this run should adhere to minimum interval\n * @return {CronDate|null} - Returns itthis for chaining, or null if increment wasnt possible\n */\n public increment(pattern: string, options: CronOptions, hasPreviousRun?: boolean): CronDate | null;\n /**\n * Convert current state back to a javascript Date()\n * @public\n *\n * @param {boolean} internal - If this is an internal call\n * @returns {Date}\n */\n public getDate(internal: boolean): Date;\n /**\n * Convert current state back to a javascript Date() and return UTC milliseconds\n * @public\n *\n * @returns {Date}\n */\n public getTime(): Date;\n}\n{ Cron as default };\n","file://node_modules/@takaro/queues/package.json":"{\"name\":\"@takaro/queues\",\"types\":\"dist/main.d.ts\"}","file://node_modules/@takaro/queues/dist/QueueService.d.ts":"declare class QueuesService {\n private static instance;\n static getInstance(): QueuesService;\n disconnect(): Promise;\n private queuesMap;\n get queues(): {\n commands: {\n queue: TakaroQueue;\n };\n cronjobs: {\n queue: TakaroQueue;\n };\n hooks: {\n queue: TakaroQueue;\n };\n events: {\n queue: TakaroQueue;\n };\n connector: {\n queue: TakaroQueue;\n };\n itemsSync: {\n queue: TakaroQueue;\n };\n bansSync: {\n queue: TakaroQueue;\n };\n playerSync: {\n queue: TakaroQueue;\n };\n steamSync: {\n queue: TakaroQueue;\n };\n csmmImport: {\n queue: TakaroQueue;\n };\n kpi: {\n queue: TakaroQueue>;\n };\n system: {\n queue: TakaroQueue>;\n };\n };\n}\ndeclare const queueService: QueuesService;\n{};\n","file://node_modules/@takaro/queues/dist/TakaroQueue.d.ts":"declare class TakaroQueue> {\n name: string;\n bullQueue: Queue;\n constructor(name: string);\n /**\n * Generating a job ID like this effectively de-duplicates all jobs with the same data.\n * @see https://docs.bullmq.io/guide/jobs/job-ids\n * @param data\n * @returns\n */\n private getJobId;\n add(data: T, extra?: JobsOptions): Promise>;\n getRepeatableJobs(): Promise;\n removeRepeatableByKey(id: string): Promise;\n}\n","file://node_modules/@takaro/queues/dist/TakaroWorker.d.ts":"type WorkerOptionsWithoutConnectionOptions = Omit;\ndeclare abstract class TakaroWorker {\n log: import(\"winston\").Logger;\n bullWorker: Worker;\n constructor(name: string, concurrency: number | undefined, fn: Processor, extraBullOpts?: WorkerOptionsWithoutConnectionOptions);\n}\n{};\n","file://node_modules/@takaro/queues/dist/bullboard.d.ts":"declare function getBullBoard(): any;\n","file://node_modules/@takaro/queues/dist/config.d.ts":"interface IQueuesConfig extends IBaseConfig {\n queues: {\n commands: {\n name: string;\n concurrency: number;\n };\n cronjobs: {\n name: string;\n concurrency: number;\n };\n hooks: {\n name: string;\n concurrency: number;\n };\n events: {\n name: string;\n concurrency: number;\n };\n connector: {\n name: string;\n };\n itemsSync: {\n name: string;\n interval: number;\n };\n bansSync: {\n name: string;\n interval: number;\n };\n steamSync: {\n name: string;\n interval: number;\n };\n playerSync: {\n name: string;\n interval: number;\n concurrency: number;\n };\n kpi: {\n name: string;\n interval: number;\n concurrency: number;\n };\n csmmImport: {\n name: string;\n };\n system: {\n name: string;\n };\n };\n redis: {\n host: string;\n port: number;\n username: string;\n password: string;\n };\n}\ndeclare const queuesConfigSchema: {\n redis: {\n host: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n port: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n username: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n password: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n queues: {\n commands: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n cronjobs: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n hooks: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n events: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n connector: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n itemsSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n bansSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n steamSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n playerSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n kpi: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n csmmImport: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n system: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n };\n};\ndeclare const config: Config;\n","file://node_modules/@takaro/queues/dist/dataDefinitions.d.ts":"interface IParsedCommand {\n command: string;\n arguments: Record;\n [key: string]: string | Record;\n}\ninterface IBaseJobData {\n [key: string]: unknown;\n domainId: string;\n}\ninterface IJobData extends IBaseJobData {\n functionId: string;\n /**\n * The id of the item that triggered this job (cronjobId, commandId or hookId)\n */\n itemId: string;\n /**\n * The id of the gameserver that triggered this job\n */\n gameServerId: string;\n /**\n * The module installation object, including the configs\n */\n module: ModuleInstallationOutputDTO;\n}\ninterface IHookJobData extends IJobData {\n eventData: EventPayload;\n player?: PlayerOutputWithRolesDTO;\n pog?: PlayerOnGameserverOutputWithRolesDTO;\n}\ninterface ICommandJobData extends IJobData {\n player: PlayerOutputWithRolesDTO;\n pog: PlayerOnGameserverOutputWithRolesDTO;\n arguments: IParsedCommand['arguments'];\n chatMessage: EventChatMessage;\n trigger: string;\n}\ntype ICronJobData = IJobData;\ndeclare function isCommandData(data: IJobData): data is ICommandJobData;\ndeclare function isHookData(data: IJobData): data is IHookJobData;\ndeclare function isCronData(data: IJobData): data is ICronJobData;\ninterface IEventQueueData extends IBaseJobData {\n type: ValueOf;\n gameServerId: string;\n event: ValueOf<(typeof GameEventsMapping)[ValueOf]>;\n}\ninterface IConnectorQueueData extends IBaseJobData {\n gameServerId: string;\n operation: 'create' | 'update' | 'delete';\n}\ninterface IGameServerQueueData extends IBaseJobData {\n gameServerId?: string;\n}\ninterface ICSMMImportData extends IBaseJobData {\n csmmExport: Record;\n options: {\n currency: boolean;\n players: boolean;\n roles: boolean;\n shop: boolean;\n };\n}\n","file://node_modules/@takaro/queues/dist/main.d.ts":"{ queueService } from './QueueService.js';\n{ queuesConfigSchema, IQueuesConfig } from './config.js';\n* from './dataDefinitions.js';\n{ TakaroQueue } from './TakaroQueue.js';\n{ TakaroWorker } from './TakaroWorker.js';\n{ getBullBoard } from './bullboard.js';\n","file://node_modules/@takaro/queues/dist/util/redisConnectionOptions.d.ts":"declare function getRedisConnectionOptions(): {\n host: string;\n port: number;\n username: string;\n password: string;\n};\n","file://node_modules/chalk/package.json":"{\"name\":\"chalk\",\"types\":\"types/index.d.ts\"}","file://node_modules/chalk/types/index.d.ts":"// Type definitions for Chalk\n// Definitions by: Thomas Sauer \n\nconst enum Level {\n\tNone = 0,\n\tBasic = 1,\n\tAnsi256 = 2,\n\tTrueColor = 3\n}\n\ninterface ChalkOptions {\n\tenabled?: boolean;\n\tlevel?: Level;\n}\n\ninterface ChalkConstructor {\n\tnew (options?: ChalkOptions): Chalk;\n\t(options?: ChalkOptions): Chalk;\n}\n\ninterface ColorSupport {\n\tlevel: Level;\n\thasBasic: boolean;\n\thas256: boolean;\n\thas16m: boolean;\n}\n\ninterface Chalk {\n\t(...text: string[]): string;\n\t(text: TemplateStringsArray, ...placeholders: string[]): string;\n\tconstructor: ChalkConstructor;\n\tenabled: boolean;\n\tlevel: Level;\n\trgb(r: number, g: number, b: number): this;\n\thsl(h: number, s: number, l: number): this;\n\thsv(h: number, s: number, v: number): this;\n\thwb(h: number, w: number, b: number): this;\n\tbgHex(color: string): this;\n\tbgKeyword(color: string): this;\n\tbgRgb(r: number, g: number, b: number): this;\n\tbgHsl(h: number, s: number, l: number): this;\n\tbgHsv(h: number, s: number, v: number): this;\n\tbgHwb(h: number, w: number, b: number): this;\n\thex(color: string): this;\n\tkeyword(color: string): this;\n\n\treadonly reset: this;\n\treadonly bold: this;\n\treadonly dim: this;\n\treadonly italic: this;\n\treadonly underline: this;\n\treadonly inverse: this;\n\treadonly hidden: this;\n\treadonly strikethrough: this;\n\n\treadonly visible: this;\n\n\treadonly black: this;\n\treadonly red: this;\n\treadonly green: this;\n\treadonly yellow: this;\n\treadonly blue: this;\n\treadonly magenta: this;\n\treadonly cyan: this;\n\treadonly white: this;\n\treadonly gray: this;\n\treadonly grey: this;\n\treadonly blackBright: this;\n\treadonly redBright: this;\n\treadonly greenBright: this;\n\treadonly yellowBright: this;\n\treadonly blueBright: this;\n\treadonly magentaBright: this;\n\treadonly cyanBright: this;\n\treadonly whiteBright: this;\n\n\treadonly bgBlack: this;\n\treadonly bgRed: this;\n\treadonly bgGreen: this;\n\treadonly bgYellow: this;\n\treadonly bgBlue: this;\n\treadonly bgMagenta: this;\n\treadonly bgCyan: this;\n\treadonly bgWhite: this;\n\treadonly bgBlackBright: this;\n\treadonly bgRedBright: this;\n\treadonly bgGreenBright: this;\n\treadonly bgYellowBright: this;\n\treadonly bgBlueBright: this;\n\treadonly bgMagentaBright: this;\n\treadonly bgCyanBright: this;\n\treadonly bgWhiteBright: this;\n}\n\ndeclare const chalk: Chalk & { supportsColor: ColorSupport };\n\ndefault chalk\n","file://node_modules/filelist/package.json":"{\"name\":\"filelist\",\"types\":\"index.d.ts\"}","file://node_modules/filelist/index.d.ts":"// IncludeOptions definitions copied from minimatch (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/minimatch/index.d.ts)\ninterface IncludeOptions {\n /**\n * Dump a ton of stuff to stderr.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Do not expand {a,b} and {1..3} brace sets.\n *\n * @default false\n */\n nobrace?: boolean;\n\n /**\n * Disable ** matching against multiple folder names.\n *\n * @default false\n */\n noglobstar?: boolean;\n\n /**\n * Allow patterns to match filenames starting with a period,\n * even if the pattern does not explicitly have a period in that spot.\n *\n * @default false\n */\n dot?: boolean;\n\n /**\n * Disable \"extglob\" style patterns like +(a|b).\n *\n * @default false\n */\n noext?: boolean;\n\n /**\n * Perform a case-insensitive match.\n *\n * @default false\n */\n nocase?: boolean;\n\n /**\n * When a match is not found by minimatch.match,\n * return a list containing the pattern itself if this option is set.\n * Otherwise, an empty list is returned if there are no matches.\n *\n * @default false\n */\n nonull?: boolean;\n\n /**\n * If set, then patterns without slashes will be matched against\n * the basename of the path if it contains slashes.\n *\n * @default false\n */\n matchBase?: boolean;\n\n /**\n * Suppress the behavior of treating #\n * at the start of a pattern as a comment.\n *\n * @default false\n */\n nocomment?: boolean;\n\n /**\n * Suppress the behavior of treating a leading ! character as negation.\n *\n * @default false\n */\n nonegate?: boolean;\n\n /**\n * Returns from negate expressions the same as if they were not negated.\n * (Ie, true on a hit, false on a miss.)\n *\n * @default false\n */\n flipNegate?: boolean;\n}\n\nclass FileList {\n static clone(): FileList\n static verbose: boolean\n}\n\ninterface FileList extends Omit, \"length\"> {\n pendingAdd: string[]\n pending: boolean\n excludes: {\n pats: RegExp[],\n funcs: Function[],\n regex: null | RegExp\n }\n items: string[]\n toArray(): string[]\n include(...items: string[]): this\n include(...items: (IncludeOptions | string)[]): this\n exclude(...items: string[]): this\n shouldExclude(item: string): boolean\n resolve(): this\n clearInclusions(): this\n clearExclusions(): this\n length(): number\n}","file://node_modules/setprototypeof/package.json":"{\"name\":\"setprototypeof\",\"types\":\"index.d.ts\"}","file://node_modules/setprototypeof/index.d.ts":"declare function setPrototypeOf(o: any, proto: object | null): any;\n= setPrototypeOf;\n","file://node_modules/iconv-lite/package.json":"{\"name\":\"iconv-lite\",\"types\":\"./lib/index.d.ts\"}","file://node_modules/iconv-lite/./lib/index.d.ts":"/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n * REQUIREMENT: This definition is dependent on the @types/node definition.\n * Install with `npm install @types/node --save-dev`\n *--------------------------------------------------------------------------------------------*/\n\ndeclare module 'iconv-lite' {\n\texport function decode(buffer: Buffer, encoding: string, options?: Options): string;\n\n\texport function encode(content: string, encoding: string, options?: Options): Buffer;\n\n\texport function encodingExists(encoding: string): boolean;\n\n\texport function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;\n\n\texport function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;\n}\n\ninterface Options {\n stripBOM?: boolean;\n addBOM?: boolean;\n defaultEncoding?: string;\n}\n","file://node_modules/side-channel/package.json":"{\"name\":\"side-channel\",\"types\":\"./index.d.ts\"}","file://node_modules/side-channel/index.d.ts":"declare namespace getSideChannel {\n\ttype Key = unknown;\n\ttype ListNode = {\n\t\tkey: Key;\n\t\tnext: ListNode;\n\t\tvalue: T;\n\t};\n\ttype RootNode = {\n\t\tkey: object;\n\t\tnext: null | ListNode;\n\t};\n\tfunction listGetNode(list: RootNode, key: ListNode['key']): ListNode | void;\n\tfunction listGet(objects: RootNode, key: ListNode['key']): T | void;\n\tfunction listSet(objects: RootNode, key: ListNode['key'], value: T): void;\n\tfunction listHas(objects: RootNode, key: ListNode['key']): boolean;\n\n\ttype Channel = {\n\t\tassert: (key: Key) => void;\n\t\thas: (key: Key) => boolean;\n\t\tget: (key: Key) => T;\n\t\tset: (key: Key, value: T) => void;\n\t}\n}\n\ndeclare function getSideChannel(): getSideChannel.Channel;\n\n= getSideChannel;\n","file://node_modules/es-define-property/package.json":"{\"name\":\"es-define-property\",\"types\":\"./index.d.ts\"}","file://node_modules/es-define-property/index.d.ts":"declare const defineProperty: false | typeof Object.defineProperty;\n\n= defineProperty;","file://node_modules/hasown/package.json":"{\"name\":\"hasown\",\"types\":\"index.d.ts\"}","file://node_modules/hasown/index.d.ts":"declare function hasOwn(o: O, p: K): o is O & Record;\n\n= hasOwn;\n","file://node_modules/define-data-property/package.json":"{\"name\":\"define-data-property\",\"types\":\"./index.d.ts\"}","file://node_modules/define-data-property/index.d.ts":"\ndeclare function defineDataProperty(\n obj: Record,\n property: keyof typeof obj,\n value: typeof obj[typeof property],\n nonEnumerable?: boolean | null,\n nonWritable?: boolean | null,\n nonConfigurable?: boolean | null,\n loose?: boolean\n): void;\n\n= defineDataProperty;","file://node_modules/safe-buffer/package.json":"{\"name\":\"safe-buffer\",\"types\":\"index.d.ts\"}","file://node_modules/safe-buffer/index.d.ts":"declare module \"safe-buffer\" {\n export class Buffer {\n length: number\n write(string: string, offset?: number, length?: number, encoding?: string): number;\n toString(encoding?: string, start?: number, end?: number): string;\n toJSON(): { type: 'Buffer', data: any[] };\n equals(otherBuffer: Buffer): boolean;\n compare(otherBuffer: Buffer, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number;\n copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;\n slice(start?: number, end?: number): Buffer;\n writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n readIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n readIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n readUInt8(offset: number, noAssert?: boolean): number;\n readUInt16LE(offset: number, noAssert?: boolean): number;\n readUInt16BE(offset: number, noAssert?: boolean): number;\n readUInt32LE(offset: number, noAssert?: boolean): number;\n readUInt32BE(offset: number, noAssert?: boolean): number;\n readInt8(offset: number, noAssert?: boolean): number;\n readInt16LE(offset: number, noAssert?: boolean): number;\n readInt16BE(offset: number, noAssert?: boolean): number;\n readInt32LE(offset: number, noAssert?: boolean): number;\n readInt32BE(offset: number, noAssert?: boolean): number;\n readFloatLE(offset: number, noAssert?: boolean): number;\n readFloatBE(offset: number, noAssert?: boolean): number;\n readDoubleLE(offset: number, noAssert?: boolean): number;\n readDoubleBE(offset: number, noAssert?: boolean): number;\n swap16(): Buffer;\n swap32(): Buffer;\n swap64(): Buffer;\n writeUInt8(value: number, offset: number, noAssert?: boolean): number;\n writeUInt16LE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt16BE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt32LE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt32BE(value: number, offset: number, noAssert?: boolean): number;\n writeInt8(value: number, offset: number, noAssert?: boolean): number;\n writeInt16LE(value: number, offset: number, noAssert?: boolean): number;\n writeInt16BE(value: number, offset: number, noAssert?: boolean): number;\n writeInt32LE(value: number, offset: number, noAssert?: boolean): number;\n writeInt32BE(value: number, offset: number, noAssert?: boolean): number;\n writeFloatLE(value: number, offset: number, noAssert?: boolean): number;\n writeFloatBE(value: number, offset: number, noAssert?: boolean): number;\n writeDoubleLE(value: number, offset: number, noAssert?: boolean): number;\n writeDoubleBE(value: number, offset: number, noAssert?: boolean): number;\n fill(value: any, offset?: number, end?: number): this;\n indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;\n lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;\n includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean;\n\n /**\n * Allocates a new buffer containing the given {str}.\n *\n * @param str String to store in buffer.\n * @param encoding encoding to use, optional. Default is 'utf8'\n */\n constructor (str: string, encoding?: string);\n /**\n * Allocates a new buffer of {size} octets.\n *\n * @param size count of octets to allocate.\n */\n constructor (size: number);\n /**\n * Allocates a new buffer containing the given {array} of octets.\n *\n * @param array The octets to store.\n */\n constructor (array: Uint8Array);\n /**\n * Produces a Buffer backed by the same allocated memory as\n * the given {ArrayBuffer}.\n *\n *\n * @param arrayBuffer The ArrayBuffer with which to share memory.\n */\n constructor (arrayBuffer: ArrayBuffer);\n /**\n * Allocates a new buffer containing the given {array} of octets.\n *\n * @param array The octets to store.\n */\n constructor (array: any[]);\n /**\n * Copies the passed {buffer} data onto a new {Buffer} instance.\n *\n * @param buffer The buffer to copy.\n */\n constructor (buffer: Buffer);\n prototype: Buffer;\n /**\n * Allocates a new Buffer using an {array} of octets.\n *\n * @param array\n */\n static from(array: any[]): Buffer;\n /**\n * When passed a reference to the .buffer property of a TypedArray instance,\n * the newly created Buffer will share the same allocated memory as the TypedArray.\n * The optional {byteOffset} and {length} arguments specify a memory range\n * within the {arrayBuffer} that will be shared by the Buffer.\n *\n * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer()\n * @param byteOffset\n * @param length\n */\n static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;\n /**\n * Copies the passed {buffer} data onto a new Buffer instance.\n *\n * @param buffer\n */\n static from(buffer: Buffer): Buffer;\n /**\n * Creates a new Buffer containing the given JavaScript string {str}.\n * If provided, the {encoding} parameter identifies the character encoding.\n * If not provided, {encoding} defaults to 'utf8'.\n *\n * @param str\n */\n static from(str: string, encoding?: string): Buffer;\n /**\n * Returns true if {obj} is a Buffer\n *\n * @param obj object to test.\n */\n static isBuffer(obj: any): obj is Buffer;\n /**\n * Returns true if {encoding} is a valid encoding argument.\n * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex'\n *\n * @param encoding string to test.\n */\n static isEncoding(encoding: string): boolean;\n /**\n * Gives the actual byte length of a string. encoding defaults to 'utf8'.\n * This is not the same as String.prototype.length since that returns the number of characters in a string.\n *\n * @param string string to test.\n * @param encoding encoding used to evaluate (defaults to 'utf8')\n */\n static byteLength(string: string, encoding?: string): number;\n /**\n * Returns a buffer which is the result of concatenating all the buffers in the list together.\n *\n * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer.\n * If the list has exactly one item, then the first item of the list is returned.\n * If the list has more than one item, then a new Buffer is created.\n *\n * @param list An array of Buffer objects to concatenate\n * @param totalLength Total length of the buffers when concatenated.\n * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly.\n */\n static concat(list: Buffer[], totalLength?: number): Buffer;\n /**\n * The same as buf1.compare(buf2).\n */\n static compare(buf1: Buffer, buf2: Buffer): number;\n /**\n * Allocates a new buffer of {size} octets.\n *\n * @param size count of octets to allocate.\n * @param fill if specified, buffer will be initialized by calling buf.fill(fill).\n * If parameter is omitted, buffer will be filled with zeros.\n * @param encoding encoding used for call to buf.fill while initalizing\n */\n static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer;\n /**\n * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents\n * of the newly created Buffer are unknown and may contain sensitive data.\n *\n * @param size count of octets to allocate\n */\n static allocUnsafe(size: number): Buffer;\n /**\n * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents\n * of the newly created Buffer are unknown and may contain sensitive data.\n *\n * @param size count of octets to allocate\n */\n static allocUnsafeSlow(size: number): Buffer;\n }\n}","file://node_modules/ipaddr.js/package.json":"{\"name\":\"ipaddr.js\",\"types\":\"./lib/ipaddr.js.d.ts\"}","file://node_modules/ipaddr.js/./lib/ipaddr.js.d.ts":"declare module \"ipaddr.js\" {\n type IPv4Range = 'unicast' | 'unspecified' | 'broadcast' | 'multicast' | 'linkLocal' | 'loopback' | 'carrierGradeNat' | 'private' | 'reserved';\n type IPv6Range = 'unicast' | 'unspecified' | 'linkLocal' | 'multicast' | 'loopback' | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'reserved';\n\n interface RangeList {\n [name: string]: [T, number] | [T, number][];\n }\n\n // Common methods/properties for IPv4 and IPv6 classes.\n class IP {\n prefixLengthFromSubnetMask(): number | null;\n toByteArray(): number[];\n toNormalizedString(): string;\n toString(): string;\n }\n\n namespace Address {\n export function isValid(addr: string): boolean;\n export function fromByteArray(bytes: number[]): IPv4 | IPv6;\n export function parse(addr: string): IPv4 | IPv6;\n export function parseCIDR(mask: string): [IPv4 | IPv6, number];\n export function process(addr: string): IPv4 | IPv6;\n export function subnetMatch(addr: IPv4, rangeList: RangeList, defaultName?: string): string;\n export function subnetMatch(addr: IPv6, rangeList: RangeList, defaultName?: string): string;\n\n export class IPv4 extends IP {\n static broadcastAddressFromCIDR(addr: string): IPv4;\n static isIPv4(addr: string): boolean;\n static isValidFourPartDecimal(addr: string): boolean;\n static isValid(addr: string): boolean;\n static networkAddressFromCIDR(addr: string): IPv4;\n static parse(addr: string): IPv4;\n static parseCIDR(addr: string): [IPv4, number];\n static subnetMaskFromPrefixLength(prefix: number): IPv4;\n constructor(octets: number[]);\n octets: number[]\n\n kind(): 'ipv4';\n match(addr: IPv4, bits: number): boolean;\n match(mask: [IPv4, number]): boolean;\n range(): IPv4Range;\n subnetMatch(rangeList: RangeList, defaultName?: string): string;\n toIPv4MappedAddress(): IPv6;\n }\n\n export class IPv6 extends IP {\n static broadcastAddressFromCIDR(addr: string): IPv6;\n static isIPv6(addr: string): boolean;\n static isValid(addr: string): boolean;\n static parse(addr: string): IPv6;\n static parseCIDR(addr: string): [IPv6, number];\n static subnetMaskFromPrefixLength(prefix: number): IPv6;\n constructor(parts: number[]);\n parts: number[]\n zoneId?: string\n\n isIPv4MappedAddress(): boolean;\n kind(): 'ipv6';\n match(addr: IPv6, bits: number): boolean;\n match(mask: [IPv6, number]): boolean;\n range(): IPv6Range;\n subnetMatch(rangeList: RangeList, defaultName?: string): string;\n toIPv4Address(): IPv4;\n }\n }\n\n export = Address;\n}\n"} +{"file://node_modules/axios/package.json":"{\"name\":\"axios\",\"types\":\"./index.d.ts\"}","file://node_modules/axios/index.d.ts":"// TypeScript Version: 4.7\ntype AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null;\n\ninterface RawAxiosHeaders {\n [key: string]: AxiosHeaderValue;\n}\n\ntype MethodsHeaders = Partial<{\n [Key in Method as Lowercase]: AxiosHeaders;\n} & {common: AxiosHeaders}>;\n\ntype AxiosHeaderMatcher = string | RegExp | ((this: AxiosHeaders, value: string, name: string) => boolean);\n\ntype AxiosHeaderParser = (this: AxiosHeaders, value: AxiosHeaderValue, header: string) => any;\n\nclass AxiosHeaders {\n constructor(\n headers?: RawAxiosHeaders | AxiosHeaders | string\n );\n\n [key: string]: any;\n\n set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders;\n\n get(headerName: string, parser: RegExp): RegExpExecArray | null;\n get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue;\n\n has(header: string, matcher?: AxiosHeaderMatcher): boolean;\n\n delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean;\n\n clear(matcher?: AxiosHeaderMatcher): boolean;\n\n normalize(format: boolean): AxiosHeaders;\n\n concat(...targets: Array): AxiosHeaders;\n\n toJSON(asStrings?: boolean): RawAxiosHeaders;\n\n static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders;\n\n static accessor(header: string | string[]): AxiosHeaders;\n\n static concat(...targets: Array): AxiosHeaders;\n\n setContentType(value: ContentType, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentType(parser?: RegExp): RegExpExecArray | null;\n getContentType(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentType(matcher?: AxiosHeaderMatcher): boolean;\n\n setContentLength(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentLength(parser?: RegExp): RegExpExecArray | null;\n getContentLength(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentLength(matcher?: AxiosHeaderMatcher): boolean;\n\n setAccept(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getAccept(parser?: RegExp): RegExpExecArray | null;\n getAccept(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasAccept(matcher?: AxiosHeaderMatcher): boolean;\n\n setUserAgent(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getUserAgent(parser?: RegExp): RegExpExecArray | null;\n getUserAgent(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasUserAgent(matcher?: AxiosHeaderMatcher): boolean;\n\n setContentEncoding(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getContentEncoding(parser?: RegExp): RegExpExecArray | null;\n getContentEncoding(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasContentEncoding(matcher?: AxiosHeaderMatcher): boolean;\n\n setAuthorization(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;\n getAuthorization(parser?: RegExp): RegExpExecArray | null;\n getAuthorization(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;\n hasAuthorization(matcher?: AxiosHeaderMatcher): boolean;\n\n [Symbol.iterator](): IterableIterator<[string, AxiosHeaderValue]>;\n}\n\ntype CommonRequestHeadersList = 'Accept' | 'Content-Length' | 'User-Agent' | 'Content-Encoding' | 'Authorization';\n\ntype ContentType = AxiosHeaderValue | 'text/html' | 'text/plain' | 'multipart/form-data' | 'application/json' | 'application/x-www-form-urlencoded' | 'application/octet-stream';\n\ntype RawAxiosRequestHeaders = Partial;\n\ntype AxiosRequestHeaders = RawAxiosRequestHeaders & AxiosHeaders;\n\ntype CommonResponseHeadersList = 'Server' | 'Content-Type' | 'Content-Length' | 'Cache-Control'| 'Content-Encoding';\n\ntype RawCommonResponseHeaders = {\n [Key in CommonResponseHeadersList]: AxiosHeaderValue;\n} & {\n \"set-cookie\": string[];\n};\n\ntype RawAxiosResponseHeaders = Partial;\n\ntype AxiosResponseHeaders = RawAxiosResponseHeaders & AxiosHeaders;\n\ninterface AxiosRequestTransformer {\n (this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any;\n}\n\ninterface AxiosResponseTransformer {\n (this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number): any;\n}\n\ninterface AxiosAdapter {\n (config: InternalAxiosRequestConfig): AxiosPromise;\n}\n\ninterface AxiosBasicCredentials {\n username: string;\n password: string;\n}\n\ninterface AxiosProxyConfig {\n host: string;\n port: number;\n auth?: AxiosBasicCredentials;\n protocol?: string;\n}\n\nenum HttpStatusCode {\n Continue = 100,\n SwitchingProtocols = 101,\n Processing = 102,\n EarlyHints = 103,\n Ok = 200,\n Created = 201,\n Accepted = 202,\n NonAuthoritativeInformation = 203,\n NoContent = 204,\n ResetContent = 205,\n PartialContent = 206,\n MultiStatus = 207,\n AlreadyReported = 208,\n ImUsed = 226,\n MultipleChoices = 300,\n MovedPermanently = 301,\n Found = 302,\n SeeOther = 303,\n NotModified = 304,\n UseProxy = 305,\n Unused = 306,\n TemporaryRedirect = 307,\n PermanentRedirect = 308,\n BadRequest = 400,\n Unauthorized = 401,\n PaymentRequired = 402,\n Forbidden = 403,\n NotFound = 404,\n MethodNotAllowed = 405,\n NotAcceptable = 406,\n ProxyAuthenticationRequired = 407,\n RequestTimeout = 408,\n Conflict = 409,\n Gone = 410,\n LengthRequired = 411,\n PreconditionFailed = 412,\n PayloadTooLarge = 413,\n UriTooLong = 414,\n UnsupportedMediaType = 415,\n RangeNotSatisfiable = 416,\n ExpectationFailed = 417,\n ImATeapot = 418,\n MisdirectedRequest = 421,\n UnprocessableEntity = 422,\n Locked = 423,\n FailedDependency = 424,\n TooEarly = 425,\n UpgradeRequired = 426,\n PreconditionRequired = 428,\n TooManyRequests = 429,\n RequestHeaderFieldsTooLarge = 431,\n UnavailableForLegalReasons = 451,\n InternalServerError = 500,\n NotImplemented = 501,\n BadGateway = 502,\n ServiceUnavailable = 503,\n GatewayTimeout = 504,\n HttpVersionNotSupported = 505,\n VariantAlsoNegotiates = 506,\n InsufficientStorage = 507,\n LoopDetected = 508,\n NotExtended = 510,\n NetworkAuthenticationRequired = 511,\n}\n\ntype Method =\n | 'get' | 'GET'\n | 'delete' | 'DELETE'\n | 'head' | 'HEAD'\n | 'options' | 'OPTIONS'\n | 'post' | 'POST'\n | 'put' | 'PUT'\n | 'patch' | 'PATCH'\n | 'purge' | 'PURGE'\n | 'link' | 'LINK'\n | 'unlink' | 'UNLINK';\n\ntype ResponseType =\n | 'arraybuffer'\n | 'blob'\n | 'document'\n | 'json'\n | 'text'\n | 'stream'\n | 'formdata';\n\ntype responseEncoding =\n | 'ascii' | 'ASCII'\n | 'ansi' | 'ANSI'\n | 'binary' | 'BINARY'\n | 'base64' | 'BASE64'\n | 'base64url' | 'BASE64URL'\n | 'hex' | 'HEX'\n | 'latin1' | 'LATIN1'\n | 'ucs-2' | 'UCS-2'\n | 'ucs2' | 'UCS2'\n | 'utf-8' | 'UTF-8'\n | 'utf8' | 'UTF8'\n | 'utf16le' | 'UTF16LE';\n\ninterface TransitionalOptions {\n silentJSONParsing?: boolean;\n forcedJSONParsing?: boolean;\n clarifyTimeoutError?: boolean;\n}\n\ninterface GenericAbortSignal {\n readonly aborted: boolean;\n onabort?: ((...args: any) => any) | null;\n addEventListener?: (...args: any) => any;\n removeEventListener?: (...args: any) => any;\n}\n\ninterface FormDataVisitorHelpers {\n defaultVisitor: SerializerVisitor;\n convertValue: (value: any) => any;\n isVisitable: (value: any) => boolean;\n}\n\ninterface SerializerVisitor {\n (\n this: GenericFormData,\n value: any,\n key: string | number,\n path: null | Array,\n helpers: FormDataVisitorHelpers\n ): boolean;\n}\n\ninterface SerializerOptions {\n visitor?: SerializerVisitor;\n dots?: boolean;\n metaTokens?: boolean;\n indexes?: boolean | null;\n}\n\n// tslint:disable-next-line\ninterface FormSerializerOptions extends SerializerOptions {\n}\n\ninterface ParamEncoder {\n (value: any, defaultEncoder: (value: any) => any): any;\n}\n\ninterface CustomParamsSerializer {\n (params: Record, options?: ParamsSerializerOptions): string;\n}\n\ninterface ParamsSerializerOptions extends SerializerOptions {\n encode?: ParamEncoder;\n serialize?: CustomParamsSerializer;\n}\n\ntype MaxUploadRate = number;\n\ntype MaxDownloadRate = number;\n\ntype BrowserProgressEvent = any;\n\ninterface AxiosProgressEvent {\n loaded: number;\n total?: number;\n progress?: number;\n bytes: number;\n rate?: number;\n estimated?: number;\n upload?: boolean;\n download?: boolean;\n event?: BrowserProgressEvent;\n lengthComputable: boolean;\n}\n\ntype Milliseconds = number;\n\ntype AxiosAdapterName = 'fetch' | 'xhr' | 'http' | string;\n\ntype AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;\n\ntype AddressFamily = 4 | 6 | undefined;\n\ninterface LookupAddressEntry {\n address: string;\n family?: AddressFamily;\n}\n\ntype LookupAddress = string | LookupAddressEntry;\n\ninterface AxiosRequestConfig {\n url?: string;\n method?: Method | string;\n baseURL?: string;\n transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[];\n transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[];\n headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;\n params?: any;\n paramsSerializer?: ParamsSerializerOptions | CustomParamsSerializer;\n data?: D;\n timeout?: Milliseconds;\n timeoutErrorMessage?: string;\n withCredentials?: boolean;\n adapter?: AxiosAdapterConfig | AxiosAdapterConfig[];\n auth?: AxiosBasicCredentials;\n responseType?: ResponseType;\n responseEncoding?: responseEncoding | string;\n xsrfCookieName?: string;\n xsrfHeaderName?: string;\n onUploadProgress?: (progressEvent: AxiosProgressEvent) => void;\n onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void;\n maxContentLength?: number;\n validateStatus?: ((status: number) => boolean) | null;\n maxBodyLength?: number;\n maxRedirects?: number;\n maxRate?: number | [MaxUploadRate, MaxDownloadRate];\n beforeRedirect?: (options: Record, responseDetails: {headers: Record, statusCode: HttpStatusCode}) => void;\n socketPath?: string | null;\n transport?: any;\n httpAgent?: any;\n httpsAgent?: any;\n proxy?: AxiosProxyConfig | false;\n cancelToken?: CancelToken;\n decompress?: boolean;\n transitional?: TransitionalOptions;\n signal?: GenericAbortSignal;\n insecureHTTPParser?: boolean;\n env?: {\n FormData?: new (...args: any[]) => object;\n };\n formSerializer?: FormSerializerOptions;\n family?: AddressFamily;\n lookup?: ((hostname: string, options: object, cb: (err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily) => void) => void) |\n ((hostname: string, options: object) => Promise<[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress>);\n withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);\n fetchOptions?: Record;\n}\n\n// Alias\ntype RawAxiosRequestConfig = AxiosRequestConfig;\n\ninterface InternalAxiosRequestConfig extends AxiosRequestConfig {\n headers: AxiosRequestHeaders;\n}\n\ninterface HeadersDefaults {\n common: RawAxiosRequestHeaders;\n delete: RawAxiosRequestHeaders;\n get: RawAxiosRequestHeaders;\n head: RawAxiosRequestHeaders;\n post: RawAxiosRequestHeaders;\n put: RawAxiosRequestHeaders;\n patch: RawAxiosRequestHeaders;\n options?: RawAxiosRequestHeaders;\n purge?: RawAxiosRequestHeaders;\n link?: RawAxiosRequestHeaders;\n unlink?: RawAxiosRequestHeaders;\n}\n\ninterface AxiosDefaults extends Omit, 'headers'> {\n headers: HeadersDefaults;\n}\n\ninterface CreateAxiosDefaults extends Omit, 'headers'> {\n headers?: RawAxiosRequestHeaders | AxiosHeaders | Partial;\n}\n\ninterface AxiosResponse {\n data: T;\n status: number;\n statusText: string;\n headers: RawAxiosResponseHeaders | AxiosResponseHeaders;\n config: InternalAxiosRequestConfig;\n request?: any;\n}\n\nclass AxiosError extends Error {\n constructor(\n message?: string,\n code?: string,\n config?: InternalAxiosRequestConfig,\n request?: any,\n response?: AxiosResponse\n );\n\n config?: InternalAxiosRequestConfig;\n code?: string;\n request?: any;\n response?: AxiosResponse;\n isAxiosError: boolean;\n status?: number;\n toJSON: () => object;\n cause?: Error;\n static from(\n error: Error | unknown,\n code?: string,\n config?: InternalAxiosRequestConfig,\n request?: any,\n response?: AxiosResponse,\n customProps?: object,\n): AxiosError;\n static readonly ERR_FR_TOO_MANY_REDIRECTS = \"ERR_FR_TOO_MANY_REDIRECTS\";\n static readonly ERR_BAD_OPTION_VALUE = \"ERR_BAD_OPTION_VALUE\";\n static readonly ERR_BAD_OPTION = \"ERR_BAD_OPTION\";\n static readonly ERR_NETWORK = \"ERR_NETWORK\";\n static readonly ERR_DEPRECATED = \"ERR_DEPRECATED\";\n static readonly ERR_BAD_RESPONSE = \"ERR_BAD_RESPONSE\";\n static readonly ERR_BAD_REQUEST = \"ERR_BAD_REQUEST\";\n static readonly ERR_NOT_SUPPORT = \"ERR_NOT_SUPPORT\";\n static readonly ERR_INVALID_URL = \"ERR_INVALID_URL\";\n static readonly ERR_CANCELED = \"ERR_CANCELED\";\n static readonly ECONNABORTED = \"ECONNABORTED\";\n static readonly ETIMEDOUT = \"ETIMEDOUT\";\n}\n\nclass CanceledError extends AxiosError {\n}\n\ntype AxiosPromise = Promise>;\n\ninterface CancelStatic {\n new (message?: string): Cancel;\n}\n\ninterface Cancel {\n message: string | undefined;\n}\n\ninterface Canceler {\n (message?: string, config?: AxiosRequestConfig, request?: any): void;\n}\n\ninterface CancelTokenStatic {\n new (executor: (cancel: Canceler) => void): CancelToken;\n source(): CancelTokenSource;\n}\n\ninterface CancelToken {\n promise: Promise;\n reason?: Cancel;\n throwIfRequested(): void;\n}\n\ninterface CancelTokenSource {\n token: CancelToken;\n cancel: Canceler;\n}\n\ninterface AxiosInterceptorOptions {\n synchronous?: boolean;\n runWhen?: (config: InternalAxiosRequestConfig) => boolean;\n}\n\ninterface AxiosInterceptorManager {\n use(onFulfilled?: ((value: V) => V | Promise) | null, onRejected?: ((error: any) => any) | null, options?: AxiosInterceptorOptions): number;\n eject(id: number): void;\n clear(): void;\n}\n\nclass Axios {\n constructor(config?: AxiosRequestConfig);\n defaults: AxiosDefaults;\n interceptors: {\n request: AxiosInterceptorManager;\n response: AxiosInterceptorManager;\n };\n getUri(config?: AxiosRequestConfig): string;\n request, D = any>(config: AxiosRequestConfig): Promise;\n get, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n delete, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n head, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n options, D = any>(url: string, config?: AxiosRequestConfig): Promise;\n post, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n put, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n patch, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n postForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n putForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n patchForm, D = any>(url: string, data?: D, config?: AxiosRequestConfig): Promise;\n}\n\ninterface AxiosInstance extends Axios {\n , D = any>(config: AxiosRequestConfig): Promise;\n , D = any>(url: string, config?: AxiosRequestConfig): Promise;\n\n defaults: Omit & {\n headers: HeadersDefaults & {\n [key: string]: AxiosHeaderValue\n }\n };\n}\n\ninterface GenericFormData {\n append(name: string, value: any, options?: any): any;\n}\n\ninterface GenericHTMLFormElement {\n name: string;\n method: string;\n submit(): void;\n}\n\nfunction getAdapter(adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined): AxiosAdapter;\n\nfunction toFormData(sourceObj: object, targetFormData?: GenericFormData, options?: FormSerializerOptions): GenericFormData;\n\nfunction formToJSON(form: GenericFormData|GenericHTMLFormElement): object;\n\nfunction isAxiosError(payload: any): payload is AxiosError;\n\nfunction spread(callback: (...args: T[]) => R): (array: T[]) => R;\n\nfunction isCancel(value: any): value is Cancel;\n\nfunction all(values: Array>): Promise;\n\ninterface AxiosStatic extends AxiosInstance {\n create(config?: CreateAxiosDefaults): AxiosInstance;\n Cancel: CancelStatic;\n CancelToken: CancelTokenStatic;\n Axios: typeof Axios;\n AxiosError: typeof AxiosError;\n HttpStatusCode: typeof HttpStatusCode;\n readonly VERSION: string;\n isCancel: typeof isCancel;\n all: typeof all;\n spread: typeof spread;\n isAxiosError: typeof isAxiosError;\n toFormData: typeof toFormData;\n formToJSON: typeof formToJSON;\n getAdapter: typeof getAdapter;\n CanceledError: typeof CanceledError;\n AxiosHeaders: typeof AxiosHeaders;\n}\n\ndeclare const axios: AxiosStatic;\n\ndefault axios;\n","file://node_modules/form-data/package.json":"{\"name\":\"form-data\",\"types\":\"./index.d.ts\"}","file://node_modules/form-data/index.d.ts":"// Definitions by: Carlos Ballesteros Velasco \n// Leon Yu \n// BendingBender \n// Maple Miao \n\n/// \n\n= FormData;\n\n// Extracted because @types/node doesn't export interfaces.\ninterface ReadableOptions {\n highWaterMark?: number;\n encoding?: string;\n objectMode?: boolean;\n read?(this: stream.Readable, size: number): void;\n destroy?(this: stream.Readable, error: Error | null, callback: (error: Error | null) => void): void;\n autoDestroy?: boolean;\n}\n\ninterface Options extends ReadableOptions {\n writable?: boolean;\n readable?: boolean;\n dataSize?: number;\n maxDataSize?: number;\n pauseStreams?: boolean;\n}\n\ndeclare class FormData extends stream.Readable {\n constructor(options?: Options);\n append(key: string, value: any, options?: FormData.AppendOptions | string): void;\n getHeaders(userHeaders?: FormData.Headers): FormData.Headers;\n submit(\n params: string | FormData.SubmitOptions,\n callback?: (error: Error | null, response: http.IncomingMessage) => void\n ): http.ClientRequest;\n getBuffer(): Buffer;\n setBoundary(boundary: string): void;\n getBoundary(): string;\n getLength(callback: (err: Error | null, length: number) => void): void;\n getLengthSync(): number;\n hasKnownLength(): boolean;\n}\n\ndeclare namespace FormData {\n interface Headers {\n [key: string]: any;\n }\n\n interface AppendOptions {\n header?: string | Headers;\n knownLength?: number;\n filename?: string;\n filepath?: string;\n contentType?: string;\n }\n\n interface SubmitOptions extends http.RequestOptions {\n protocol?: 'https:' | 'http:';\n }\n}\n","file://node_modules/@takaro/helpers/package.json":"{\"name\":\"@takaro/helpers\",\"types\":\"dist/main.d.ts\"}","file://node_modules/@takaro/helpers/dist/TakaroUserError.d.ts":"declare class TakaroUserError extends Error {\n constructor(message: string);\n}\n","file://node_modules/@takaro/helpers/dist/checkPermission.d.ts":"declare function checkPermission(player: PlayerOnGameserverOutputWithRolesDTO, permission: string): any;\n","file://node_modules/@takaro/helpers/dist/config.d.ts":"interface IHelpersConfig extends IBaseConfig {\n apiClient: {\n token: string;\n url: string;\n };\n data: string;\n}\ndeclare const config: Config;\n{};\n","file://node_modules/@takaro/helpers/dist/getData.d.ts":"declare function getData(): any;\n","file://node_modules/@takaro/helpers/dist/getTakaro.d.ts":"declare function getTakaro(data: Record, logger?: Pick): {\n takaro: Client;\n data: Record;\n};\n","file://node_modules/@takaro/helpers/dist/main.d.ts":"{ getTakaro } from './getTakaro.js';\n{ checkPermission } from './checkPermission.js';\n{ nextCronJobRun } from './nextCronJobRun.js';\n{ TakaroUserError } from './TakaroUserError.js';\n* as _ from 'lodash-es';\n* as axios from 'axios';\ndeclare const takaro: Client;\n","file://node_modules/@takaro/helpers/dist/nextCronJobRun.d.ts":"declare const nextCronJobRun: (cron: string) => Date | null;\n","file://node_modules/croner/package.json":"{\"name\":\"croner\",\"types\":\"./types/croner.d.ts\"}","file://node_modules/croner/./types/croner.d.ts":"type TimePoint = {\n /**\n * - 1970--\n */\n y: number;\n /**\n * - 1-12\n */\n m: number;\n /**\n * - 1-31\n */\n d: number;\n /**\n * - 0-24\n */\n h: number;\n /**\n * - 0-60 Minute\n */\n i: number;\n /**\n * - 0-60\n */\n s: number;\n /**\n * - Time zone in IANA database format 'Europe/Stockholm'\n */\n tz: string;\n};\ntype CatchCallbackFn = (e: unknown, job: Cron) => any;\ntype ProtectCallbackFn = (job: Cron) => any;\n/**\n * - Cron scheduler options\n */\ntype CronOptions = {\n /**\n * - Name of a job\n */\n name?: string;\n /**\n * - Job is paused\n */\n paused?: boolean;\n /**\n * - Job is about to be killed or killed\n */\n kill?: boolean;\n /**\n * - Continue exection even if a unhandled error is thrown by triggered function\n * - If set to a function, execute function on catching the error.\n */\n catch?: boolean | CatchCallbackFn;\n /**\n * - Abort job instantly if nothing else keeps the event loop running.\n */\n unref?: boolean;\n /**\n * - Maximum nuber of executions\n */\n maxRuns?: number;\n /**\n * - Minimum interval between executions, in seconds\n */\n interval?: number;\n /**\n * - Skip current run if job is already running\n */\n protect?: boolean | ProtectCallbackFn;\n /**\n * - When to start running\n */\n startAt?: string | Date;\n /**\n * - When to stop running\n */\n stopAt?: string | Date;\n /**\n * - Time zone in Europe/Stockholm format\n */\n timezone?: string;\n /**\n * - Offset from UTC in minutes\n */\n utcOffset?: number;\n /**\n * - Combine day-of-month and day-of-week using true = OR, false = AND. Default is true = OR.\n */\n legacyMode?: boolean;\n /**\n * - Used to pass any object to scheduled function\n */\n context?: unknown;\n};\n/**\n * Name for each part of the cron pattern\n */\ntype CronPatternPart = (\"second\" | \"minute\" | \"hour\" | \"day\" | \"month\" | \"dayOfWeek\");\n/**\n * Offset, 0 or -1.\n *\n * 0 offset is used for seconds,minutes and hours as they start on 1.\n * -1 on days and months, as they start on 0\n */\ntype CronIndexOffset = number;\n/**\n * Cron entrypoint\n *\n * @constructor\n * @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string\n * @param {CronOptions|Function} [fnOrOptions1] - Options or function to be run each iteration of pattern\n * @param {CronOptions|Function} [fnOrOptions2] - Options or function to be run each iteration of pattern\n * @returns {Cron}\n */\nfunction Cron(pattern: string | Date, fnOrOptions1?: CronOptions | Function, fnOrOptions2?: CronOptions | Function): Cron;\nclass Cron {\n /**\n * Cron entrypoint\n *\n * @constructor\n * @param {string|Date} pattern - Input pattern, input date, or input ISO 8601 time string\n * @param {CronOptions|Function} [fnOrOptions1] - Options or function to be run each iteration of pattern\n * @param {CronOptions|Function} [fnOrOptions2] - Options or function to be run each iteration of pattern\n * @returns {Cron}\n */\n constructor(pattern: string | Date, fnOrOptions1?: CronOptions | Function, fnOrOptions2?: CronOptions | Function);\n /**\n * @public\n * @type {string|undefined} */\n public name: string | undefined;\n /**\n * @public\n * @type {CronOptions} */\n public options: CronOptions;\n /**\n * Encapsulate all internal states in an object.\n * Duplicate all options that can change to internal states, for example maxRuns and paused.\n * @private\n */\n private _states;\n fn: Function | CronOptions;\n /**\n * Find next runtime, based on supplied date. Strips milliseconds.\n *\n * @param {CronDate|Date|string} [prev] - Date to start from\n * @returns {Date | null} - Next run time\n */\n nextRun(prev?: CronDate | Date | string): Date | null;\n /**\n * Find next n runs, based on supplied date. Strips milliseconds.\n *\n * @param {number} n - Number of runs to enumerate\n * @param {Date|string} [previous] - Date to start from\n * @returns {Date[]} - Next n run times\n */\n nextRuns(n: number, previous?: Date | string): Date[];\n /**\n * Return the original pattern, if there was one\n *\n * @returns {string|undefined} - Original pattern\n */\n getPattern(): string | undefined;\n /**\n * Indicates whether or not the cron job is scheduled and running, e.g. awaiting next trigger\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isRunning(): boolean;\n /**\n * Indicates whether or not the cron job is permanently stopped\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isStopped(): boolean;\n /**\n * Indicates whether or not the cron job is currently working\n * @public\n *\n * @returns {boolean} - Running or not\n */\n public isBusy(): boolean;\n /**\n * Return current/previous run start time\n * @public\n *\n * @returns {Date | null} - Previous run time\n */\n public currentRun(): Date | null;\n /**\n * Return previous run start time\n * @public\n *\n * @returns {Date | null} - Previous run time\n */\n public previousRun(): Date | null;\n /**\n * Returns number of milliseconds to next run\n * @public\n *\n * @param {CronDate|Date|string} [prev] - Starting date, defaults to now - minimum interval\n * @returns {number | null}\n */\n public msToNext(prev?: CronDate | Date | string): number | null;\n /**\n * Stop execution\n *\n * Running this will forcefully stop the job, and prevent furter exection. `.resume()` will not work after stopping.\n * It will also be removed from the scheduledJobs array if it were named.\n *\n * @public\n */\n public stop(): void;\n /**\n * Pause execution\n * @public\n *\n * @returns {boolean} - Wether pause was successful\n */\n public pause(): boolean;\n /**\n * Resume execution\n * @public\n *\n * @returns {boolean} - Wether resume was successful\n */\n public resume(): boolean;\n /**\n * Schedule a new job\n * @public\n *\n * @param {Function} func - Function to be run each iteration of pattern\n * @returns {Cron}\n */\n public schedule(func: Function): Cron;\n private _trigger;\n /**\n * Trigger a run manually\n * @public\n */\n public trigger(): Promise;\n private _checkTrigger;\n private _next;\n private _calculatePreviousRun;\n}\nnamespace Cron {\n export { Cron };\n export { scheduledJobs };\n}\n/**\n * An array containing all named cron jobs.\n *\n * @constant\n * @type {Cron[]}\n */\nconst scheduledJobs: Cron[];\n/**\n * Converts date to CronDate\n * @constructor\n *\n * @param {CronDate|Date|string} [d] - Input date, if using string representation ISO 8001 (2015-11-24T19:40:00) local timezone is expected\n * @param {string|number} [tz] - String representation of target timezone in Europe/Stockholm format, or a number representing offset in minutes.\n*/\ndeclare function CronDate(d?: CronDate | Date | string, tz?: string | number): void;\ndeclare class CronDate {\n /**\n * Converts date to CronDate\n * @constructor\n *\n * @param {CronDate|Date|string} [d] - Input date, if using string representation ISO 8001 (2015-11-24T19:40:00) local timezone is expected\n * @param {string|number} [tz] - String representation of target timezone in Europe/Stockholm format, or a number representing offset in minutes.\n */\n constructor(d?: CronDate | Date | string, tz?: string | number);\n /**\n * TimeZone\n * @type {string|number|undefined}\n */\n tz: string | number | undefined;\n private isNthWeekdayOfMonth;\n private fromDate;\n ms: number;\n second: number;\n minute: number;\n hour: number;\n day: number;\n month: number;\n year: number;\n private fromCronDate;\n private apply;\n private fromString;\n private findNext;\n private recurse;\n /**\n * Increment to next run time\n * @public\n *\n * @param {string} pattern - The pattern used to increment current state\n * @param {CronOptions} options - Cron options used for incrementing\n * @param {boolean} [hasPreviousRun] - If this run should adhere to minimum interval\n * @return {CronDate|null} - Returns itthis for chaining, or null if increment wasnt possible\n */\n public increment(pattern: string, options: CronOptions, hasPreviousRun?: boolean): CronDate | null;\n /**\n * Convert current state back to a javascript Date()\n * @public\n *\n * @param {boolean} internal - If this is an internal call\n * @returns {Date}\n */\n public getDate(internal: boolean): Date;\n /**\n * Convert current state back to a javascript Date() and return UTC milliseconds\n * @public\n *\n * @returns {Date}\n */\n public getTime(): Date;\n}\n{ Cron as default };\n","file://node_modules/@takaro/queues/package.json":"{\"name\":\"@takaro/queues\",\"types\":\"dist/main.d.ts\"}","file://node_modules/@takaro/queues/dist/QueueService.d.ts":"declare class QueuesService {\n private static instance;\n static getInstance(): QueuesService;\n disconnect(): Promise;\n private queuesMap;\n get queues(): {\n commands: {\n queue: TakaroQueue;\n };\n cronjobs: {\n queue: TakaroQueue;\n };\n hooks: {\n queue: TakaroQueue;\n };\n events: {\n queue: TakaroQueue;\n };\n connector: {\n queue: TakaroQueue;\n };\n itemsSync: {\n queue: TakaroQueue;\n };\n bansSync: {\n queue: TakaroQueue;\n };\n playerSync: {\n queue: TakaroQueue;\n };\n steamSync: {\n queue: TakaroQueue;\n };\n csmmImport: {\n queue: TakaroQueue;\n };\n kpi: {\n queue: TakaroQueue>;\n };\n system: {\n queue: TakaroQueue>;\n };\n };\n}\ndeclare const queueService: QueuesService;\n{};\n","file://node_modules/@takaro/queues/dist/TakaroQueue.d.ts":"declare class TakaroQueue> {\n name: string;\n bullQueue: Queue;\n constructor(name: string);\n /**\n * Generating a job ID like this effectively de-duplicates all jobs with the same data.\n * @see https://docs.bullmq.io/guide/jobs/job-ids\n * @param data\n * @returns\n */\n private getJobId;\n add(data: T, extra?: JobsOptions): Promise>;\n getRepeatableJobs(): Promise;\n removeRepeatableByKey(id: string): Promise;\n}\n","file://node_modules/@takaro/queues/dist/TakaroWorker.d.ts":"type WorkerOptionsWithoutConnectionOptions = Omit;\ndeclare abstract class TakaroWorker {\n log: import(\"winston\").Logger;\n bullWorker: Worker;\n constructor(name: string, concurrency: number | undefined, fn: Processor, extraBullOpts?: WorkerOptionsWithoutConnectionOptions);\n}\n{};\n","file://node_modules/@takaro/queues/dist/bullboard.d.ts":"declare function getBullBoard(): any;\n","file://node_modules/@takaro/queues/dist/config.d.ts":"interface IQueuesConfig extends IBaseConfig {\n queues: {\n commands: {\n name: string;\n concurrency: number;\n };\n cronjobs: {\n name: string;\n concurrency: number;\n };\n hooks: {\n name: string;\n concurrency: number;\n };\n events: {\n name: string;\n concurrency: number;\n };\n connector: {\n name: string;\n };\n itemsSync: {\n name: string;\n interval: number;\n };\n bansSync: {\n name: string;\n interval: number;\n };\n steamSync: {\n name: string;\n interval: number;\n };\n playerSync: {\n name: string;\n interval: number;\n concurrency: number;\n };\n kpi: {\n name: string;\n interval: number;\n concurrency: number;\n };\n csmmImport: {\n name: string;\n };\n system: {\n name: string;\n };\n };\n redis: {\n host: string;\n port: number;\n username: string;\n password: string;\n };\n}\ndeclare const queuesConfigSchema: {\n redis: {\n host: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n port: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n username: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n password: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n queues: {\n commands: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n cronjobs: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n hooks: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n events: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n connector: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n itemsSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n bansSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n steamSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n playerSync: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n kpi: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n interval: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n concurrency: {\n doc: string;\n format: NumberConstructor;\n default: number;\n env: string;\n };\n };\n csmmImport: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n system: {\n name: {\n doc: string;\n format: StringConstructor;\n default: string;\n env: string;\n };\n };\n };\n};\ndeclare const config: Config;\n","file://node_modules/@takaro/queues/dist/dataDefinitions.d.ts":"interface IParsedCommand {\n command: string;\n arguments: Record;\n [key: string]: string | Record;\n}\ninterface IBaseJobData {\n [key: string]: unknown;\n domainId: string;\n}\ninterface IJobData extends IBaseJobData {\n functionId: string;\n /**\n * The id of the item that triggered this job (cronjobId, commandId or hookId)\n */\n itemId: string;\n /**\n * The id of the gameserver that triggered this job\n */\n gameServerId: string;\n /**\n * The module installation object, including the configs\n */\n module: ModuleInstallationOutputDTO;\n}\ninterface IHookJobData extends IJobData {\n eventData: EventPayload;\n player?: PlayerOutputWithRolesDTO;\n pog?: PlayerOnGameserverOutputWithRolesDTO;\n}\ninterface ICommandJobData extends IJobData {\n player: PlayerOutputWithRolesDTO;\n pog: PlayerOnGameserverOutputWithRolesDTO;\n arguments: IParsedCommand['arguments'];\n chatMessage: EventChatMessage;\n trigger: string;\n}\ntype ICronJobData = IJobData;\ndeclare function isCommandData(data: IJobData): data is ICommandJobData;\ndeclare function isHookData(data: IJobData): data is IHookJobData;\ndeclare function isCronData(data: IJobData): data is ICronJobData;\ninterface IEventQueueData extends IBaseJobData {\n type: ValueOf;\n gameServerId: string;\n event: ValueOf<(typeof GameEventsMapping)[ValueOf]>;\n}\ninterface IConnectorQueueData extends IBaseJobData {\n gameServerId: string;\n operation: 'create' | 'update' | 'delete';\n}\ninterface IGameServerQueueData extends IBaseJobData {\n gameServerId?: string;\n}\ninterface ICSMMImportData extends IBaseJobData {\n csmmExport: Record;\n options: {\n currency: boolean;\n players: boolean;\n roles: boolean;\n shop: boolean;\n };\n}\n","file://node_modules/@takaro/queues/dist/main.d.ts":"{ queueService } from './QueueService.js';\n{ queuesConfigSchema, IQueuesConfig } from './config.js';\n* from './dataDefinitions.js';\n{ TakaroQueue } from './TakaroQueue.js';\n{ TakaroWorker } from './TakaroWorker.js';\n{ getBullBoard } from './bullboard.js';\n","file://node_modules/@takaro/queues/dist/util/redisConnectionOptions.d.ts":"declare function getRedisConnectionOptions(): {\n host: string;\n port: number;\n username: string;\n password: string;\n};\n","file://node_modules/filelist/package.json":"{\"name\":\"filelist\",\"types\":\"index.d.ts\"}","file://node_modules/filelist/index.d.ts":"// IncludeOptions definitions copied from minimatch (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/minimatch/index.d.ts)\ninterface IncludeOptions {\n /**\n * Dump a ton of stuff to stderr.\n *\n * @default false\n */\n debug?: boolean;\n\n /**\n * Do not expand {a,b} and {1..3} brace sets.\n *\n * @default false\n */\n nobrace?: boolean;\n\n /**\n * Disable ** matching against multiple folder names.\n *\n * @default false\n */\n noglobstar?: boolean;\n\n /**\n * Allow patterns to match filenames starting with a period,\n * even if the pattern does not explicitly have a period in that spot.\n *\n * @default false\n */\n dot?: boolean;\n\n /**\n * Disable \"extglob\" style patterns like +(a|b).\n *\n * @default false\n */\n noext?: boolean;\n\n /**\n * Perform a case-insensitive match.\n *\n * @default false\n */\n nocase?: boolean;\n\n /**\n * When a match is not found by minimatch.match,\n * return a list containing the pattern itself if this option is set.\n * Otherwise, an empty list is returned if there are no matches.\n *\n * @default false\n */\n nonull?: boolean;\n\n /**\n * If set, then patterns without slashes will be matched against\n * the basename of the path if it contains slashes.\n *\n * @default false\n */\n matchBase?: boolean;\n\n /**\n * Suppress the behavior of treating #\n * at the start of a pattern as a comment.\n *\n * @default false\n */\n nocomment?: boolean;\n\n /**\n * Suppress the behavior of treating a leading ! character as negation.\n *\n * @default false\n */\n nonegate?: boolean;\n\n /**\n * Returns from negate expressions the same as if they were not negated.\n * (Ie, true on a hit, false on a miss.)\n *\n * @default false\n */\n flipNegate?: boolean;\n}\n\nclass FileList {\n static clone(): FileList\n static verbose: boolean\n}\n\ninterface FileList extends Omit, \"length\"> {\n pendingAdd: string[]\n pending: boolean\n excludes: {\n pats: RegExp[],\n funcs: Function[],\n regex: null | RegExp\n }\n items: string[]\n toArray(): string[]\n include(...items: string[]): this\n include(...items: (IncludeOptions | string)[]): this\n exclude(...items: string[]): this\n shouldExclude(item: string): boolean\n resolve(): this\n clearInclusions(): this\n clearExclusions(): this\n length(): number\n}","file://node_modules/setprototypeof/package.json":"{\"name\":\"setprototypeof\",\"types\":\"index.d.ts\"}","file://node_modules/setprototypeof/index.d.ts":"declare function setPrototypeOf(o: any, proto: object | null): any;\n= setPrototypeOf;\n","file://node_modules/iconv-lite/package.json":"{\"name\":\"iconv-lite\",\"types\":\"./lib/index.d.ts\"}","file://node_modules/iconv-lite/./lib/index.d.ts":"/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n * REQUIREMENT: This definition is dependent on the @types/node definition.\n * Install with `npm install @types/node --save-dev`\n *--------------------------------------------------------------------------------------------*/\n\ndeclare module 'iconv-lite' {\n\texport function decode(buffer: Buffer, encoding: string, options?: Options): string;\n\n\texport function encode(content: string, encoding: string, options?: Options): Buffer;\n\n\texport function encodingExists(encoding: string): boolean;\n\n\texport function decodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;\n\n\texport function encodeStream(encoding: string, options?: Options): NodeJS.ReadWriteStream;\n}\n\ninterface Options {\n stripBOM?: boolean;\n addBOM?: boolean;\n defaultEncoding?: string;\n}\n","file://node_modules/side-channel/package.json":"{\"name\":\"side-channel\",\"types\":\"./index.d.ts\"}","file://node_modules/side-channel/index.d.ts":"declare namespace getSideChannel {\n\ttype Key = unknown;\n\ttype ListNode = {\n\t\tkey: Key;\n\t\tnext: ListNode;\n\t\tvalue: T;\n\t};\n\ttype RootNode = {\n\t\tkey: object;\n\t\tnext: null | ListNode;\n\t};\n\tfunction listGetNode(list: RootNode, key: ListNode['key']): ListNode | void;\n\tfunction listGet(objects: RootNode, key: ListNode['key']): T | void;\n\tfunction listSet(objects: RootNode, key: ListNode['key'], value: T): void;\n\tfunction listHas(objects: RootNode, key: ListNode['key']): boolean;\n\n\ttype Channel = {\n\t\tassert: (key: Key) => void;\n\t\thas: (key: Key) => boolean;\n\t\tget: (key: Key) => T;\n\t\tset: (key: Key, value: T) => void;\n\t}\n}\n\ndeclare function getSideChannel(): getSideChannel.Channel;\n\n= getSideChannel;\n","file://node_modules/es-define-property/package.json":"{\"name\":\"es-define-property\",\"types\":\"./index.d.ts\"}","file://node_modules/es-define-property/index.d.ts":"declare const defineProperty: false | typeof Object.defineProperty;\n\n= defineProperty;","file://node_modules/hasown/package.json":"{\"name\":\"hasown\",\"types\":\"index.d.ts\"}","file://node_modules/hasown/index.d.ts":"declare function hasOwn(o: O, p: K): o is O & Record;\n\n= hasOwn;\n","file://node_modules/define-data-property/package.json":"{\"name\":\"define-data-property\",\"types\":\"./index.d.ts\"}","file://node_modules/define-data-property/index.d.ts":"\ndeclare function defineDataProperty(\n obj: Record,\n property: keyof typeof obj,\n value: typeof obj[typeof property],\n nonEnumerable?: boolean | null,\n nonWritable?: boolean | null,\n nonConfigurable?: boolean | null,\n loose?: boolean\n): void;\n\n= defineDataProperty;","file://node_modules/safe-buffer/package.json":"{\"name\":\"safe-buffer\",\"types\":\"index.d.ts\"}","file://node_modules/safe-buffer/index.d.ts":"declare module \"safe-buffer\" {\n export class Buffer {\n length: number\n write(string: string, offset?: number, length?: number, encoding?: string): number;\n toString(encoding?: string, start?: number, end?: number): string;\n toJSON(): { type: 'Buffer', data: any[] };\n equals(otherBuffer: Buffer): boolean;\n compare(otherBuffer: Buffer, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number;\n copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;\n slice(start?: number, end?: number): Buffer;\n writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n readIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n readIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n readUInt8(offset: number, noAssert?: boolean): number;\n readUInt16LE(offset: number, noAssert?: boolean): number;\n readUInt16BE(offset: number, noAssert?: boolean): number;\n readUInt32LE(offset: number, noAssert?: boolean): number;\n readUInt32BE(offset: number, noAssert?: boolean): number;\n readInt8(offset: number, noAssert?: boolean): number;\n readInt16LE(offset: number, noAssert?: boolean): number;\n readInt16BE(offset: number, noAssert?: boolean): number;\n readInt32LE(offset: number, noAssert?: boolean): number;\n readInt32BE(offset: number, noAssert?: boolean): number;\n readFloatLE(offset: number, noAssert?: boolean): number;\n readFloatBE(offset: number, noAssert?: boolean): number;\n readDoubleLE(offset: number, noAssert?: boolean): number;\n readDoubleBE(offset: number, noAssert?: boolean): number;\n swap16(): Buffer;\n swap32(): Buffer;\n swap64(): Buffer;\n writeUInt8(value: number, offset: number, noAssert?: boolean): number;\n writeUInt16LE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt16BE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt32LE(value: number, offset: number, noAssert?: boolean): number;\n writeUInt32BE(value: number, offset: number, noAssert?: boolean): number;\n writeInt8(value: number, offset: number, noAssert?: boolean): number;\n writeInt16LE(value: number, offset: number, noAssert?: boolean): number;\n writeInt16BE(value: number, offset: number, noAssert?: boolean): number;\n writeInt32LE(value: number, offset: number, noAssert?: boolean): number;\n writeInt32BE(value: number, offset: number, noAssert?: boolean): number;\n writeFloatLE(value: number, offset: number, noAssert?: boolean): number;\n writeFloatBE(value: number, offset: number, noAssert?: boolean): number;\n writeDoubleLE(value: number, offset: number, noAssert?: boolean): number;\n writeDoubleBE(value: number, offset: number, noAssert?: boolean): number;\n fill(value: any, offset?: number, end?: number): this;\n indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;\n lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number;\n includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean;\n\n /**\n * Allocates a new buffer containing the given {str}.\n *\n * @param str String to store in buffer.\n * @param encoding encoding to use, optional. Default is 'utf8'\n */\n constructor (str: string, encoding?: string);\n /**\n * Allocates a new buffer of {size} octets.\n *\n * @param size count of octets to allocate.\n */\n constructor (size: number);\n /**\n * Allocates a new buffer containing the given {array} of octets.\n *\n * @param array The octets to store.\n */\n constructor (array: Uint8Array);\n /**\n * Produces a Buffer backed by the same allocated memory as\n * the given {ArrayBuffer}.\n *\n *\n * @param arrayBuffer The ArrayBuffer with which to share memory.\n */\n constructor (arrayBuffer: ArrayBuffer);\n /**\n * Allocates a new buffer containing the given {array} of octets.\n *\n * @param array The octets to store.\n */\n constructor (array: any[]);\n /**\n * Copies the passed {buffer} data onto a new {Buffer} instance.\n *\n * @param buffer The buffer to copy.\n */\n constructor (buffer: Buffer);\n prototype: Buffer;\n /**\n * Allocates a new Buffer using an {array} of octets.\n *\n * @param array\n */\n static from(array: any[]): Buffer;\n /**\n * When passed a reference to the .buffer property of a TypedArray instance,\n * the newly created Buffer will share the same allocated memory as the TypedArray.\n * The optional {byteOffset} and {length} arguments specify a memory range\n * within the {arrayBuffer} that will be shared by the Buffer.\n *\n * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer()\n * @param byteOffset\n * @param length\n */\n static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;\n /**\n * Copies the passed {buffer} data onto a new Buffer instance.\n *\n * @param buffer\n */\n static from(buffer: Buffer): Buffer;\n /**\n * Creates a new Buffer containing the given JavaScript string {str}.\n * If provided, the {encoding} parameter identifies the character encoding.\n * If not provided, {encoding} defaults to 'utf8'.\n *\n * @param str\n */\n static from(str: string, encoding?: string): Buffer;\n /**\n * Returns true if {obj} is a Buffer\n *\n * @param obj object to test.\n */\n static isBuffer(obj: any): obj is Buffer;\n /**\n * Returns true if {encoding} is a valid encoding argument.\n * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex'\n *\n * @param encoding string to test.\n */\n static isEncoding(encoding: string): boolean;\n /**\n * Gives the actual byte length of a string. encoding defaults to 'utf8'.\n * This is not the same as String.prototype.length since that returns the number of characters in a string.\n *\n * @param string string to test.\n * @param encoding encoding used to evaluate (defaults to 'utf8')\n */\n static byteLength(string: string, encoding?: string): number;\n /**\n * Returns a buffer which is the result of concatenating all the buffers in the list together.\n *\n * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer.\n * If the list has exactly one item, then the first item of the list is returned.\n * If the list has more than one item, then a new Buffer is created.\n *\n * @param list An array of Buffer objects to concatenate\n * @param totalLength Total length of the buffers when concatenated.\n * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly.\n */\n static concat(list: Buffer[], totalLength?: number): Buffer;\n /**\n * The same as buf1.compare(buf2).\n */\n static compare(buf1: Buffer, buf2: Buffer): number;\n /**\n * Allocates a new buffer of {size} octets.\n *\n * @param size count of octets to allocate.\n * @param fill if specified, buffer will be initialized by calling buf.fill(fill).\n * If parameter is omitted, buffer will be filled with zeros.\n * @param encoding encoding used for call to buf.fill while initalizing\n */\n static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer;\n /**\n * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents\n * of the newly created Buffer are unknown and may contain sensitive data.\n *\n * @param size count of octets to allocate\n */\n static allocUnsafe(size: number): Buffer;\n /**\n * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents\n * of the newly created Buffer are unknown and may contain sensitive data.\n *\n * @param size count of octets to allocate\n */\n static allocUnsafeSlow(size: number): Buffer;\n }\n}","file://node_modules/ipaddr.js/package.json":"{\"name\":\"ipaddr.js\",\"types\":\"./lib/ipaddr.js.d.ts\"}","file://node_modules/ipaddr.js/./lib/ipaddr.js.d.ts":"declare module \"ipaddr.js\" {\n type IPv4Range = 'unicast' | 'unspecified' | 'broadcast' | 'multicast' | 'linkLocal' | 'loopback' | 'carrierGradeNat' | 'private' | 'reserved';\n type IPv6Range = 'unicast' | 'unspecified' | 'linkLocal' | 'multicast' | 'loopback' | 'uniqueLocal' | 'ipv4Mapped' | 'rfc6145' | 'rfc6052' | '6to4' | 'teredo' | 'reserved';\n\n interface RangeList {\n [name: string]: [T, number] | [T, number][];\n }\n\n // Common methods/properties for IPv4 and IPv6 classes.\n class IP {\n prefixLengthFromSubnetMask(): number | null;\n toByteArray(): number[];\n toNormalizedString(): string;\n toString(): string;\n }\n\n namespace Address {\n export function isValid(addr: string): boolean;\n export function fromByteArray(bytes: number[]): IPv4 | IPv6;\n export function parse(addr: string): IPv4 | IPv6;\n export function parseCIDR(mask: string): [IPv4 | IPv6, number];\n export function process(addr: string): IPv4 | IPv6;\n export function subnetMatch(addr: IPv4, rangeList: RangeList, defaultName?: string): string;\n export function subnetMatch(addr: IPv6, rangeList: RangeList, defaultName?: string): string;\n\n export class IPv4 extends IP {\n static broadcastAddressFromCIDR(addr: string): IPv4;\n static isIPv4(addr: string): boolean;\n static isValidFourPartDecimal(addr: string): boolean;\n static isValid(addr: string): boolean;\n static networkAddressFromCIDR(addr: string): IPv4;\n static parse(addr: string): IPv4;\n static parseCIDR(addr: string): [IPv4, number];\n static subnetMaskFromPrefixLength(prefix: number): IPv4;\n constructor(octets: number[]);\n octets: number[]\n\n kind(): 'ipv4';\n match(addr: IPv4, bits: number): boolean;\n match(mask: [IPv4, number]): boolean;\n range(): IPv4Range;\n subnetMatch(rangeList: RangeList, defaultName?: string): string;\n toIPv4MappedAddress(): IPv6;\n }\n\n export class IPv6 extends IP {\n static broadcastAddressFromCIDR(addr: string): IPv6;\n static isIPv6(addr: string): boolean;\n static isValid(addr: string): boolean;\n static parse(addr: string): IPv6;\n static parseCIDR(addr: string): [IPv6, number];\n static subnetMaskFromPrefixLength(prefix: number): IPv6;\n constructor(parts: number[]);\n parts: number[]\n zoneId?: string\n\n isIPv4MappedAddress(): boolean;\n kind(): 'ipv6';\n match(addr: IPv6, bits: number): boolean;\n match(mask: [IPv6, number]): boolean;\n range(): IPv6Range;\n subnetMatch(rangeList: RangeList, defaultName?: string): string;\n toIPv4Address(): IPv4;\n }\n }\n\n export = Address;\n}\n"} diff --git a/packages/web-main/src/routes/_auth/_global/-roles/RoleCreateUpdateForm.tsx b/packages/web-main/src/routes/_auth/_global/-roles/RoleCreateUpdateForm.tsx index 50214d86e5..8c7f3819d6 100644 --- a/packages/web-main/src/routes/_auth/_global/-roles/RoleCreateUpdateForm.tsx +++ b/packages/web-main/src/routes/_auth/_global/-roles/RoleCreateUpdateForm.tsx @@ -88,7 +88,7 @@ export const RoleForm: FC = ({ const { control, handleSubmit, formState } = useForm({ mode: 'onChange', resolver: zodResolver(validationSchema), - defaultValues: initialData && { + values: initialData && { name: initialData.name, permissions: Object.values(permissions).reduce( (acc, permission) => ({ diff --git a/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx b/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx index 64713db142..cd63385a62 100644 --- a/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx +++ b/packages/web-main/src/routes/_auth/_global/user.$userId.role.assign.tsx @@ -78,7 +78,7 @@ function Component() {
- + { + // Logging into a disabled domain is going to error out + if (isDisabled) return; + if (isCurrentDomain === false) { mutate({ domainId: domain.id }); + } else { + navigate({ to: '/dashboard' }); } }; @@ -97,6 +103,7 @@ function DomainCard({ domain, isCurrentDomain }: DomainCardProps) {
{isCurrentDomain && } + {isDisabled && }

{domain.name} diff --git a/renovate.json b/renovate.json index dcfab05007..b14f454159 100644 --- a/renovate.json +++ b/renovate.json @@ -63,6 +63,7 @@ "prettier", "@types", "chai", + "sinon", "mocha" ], "automerge": true