Compare commits

..

No commits in common. "eed84fb151d9740f9b557299e166bc87b4e35b90" and "85d731825b252f171f16f8194ff469ac8df63d87" have entirely different histories.

32 changed files with 692 additions and 425 deletions

View File

@ -5,4 +5,4 @@ if [ ! -f conf/config.json ]; then
jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config.json.example > /app/conf/config.json jq --arg value "mongodb://openwfagent:spaceninjaserver@mongodb:27017/" '.mongodbUrl = $value' /app/config.json.example > /app/conf/config.json
fi fi
exec npm run start -- --configPath conf/config.json exec npm run start conf/config.json

538
package-lock.json generated
View File

@ -29,11 +29,11 @@
"@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0", "@typescript-eslint/parser": "^8.28.0",
"@typescript/native-preview": "^7.0.0-dev.20250523.1", "@typescript/native-preview": "^7.0.0-dev.20250523.1",
"chokidar": "^4.0.3",
"eslint": "^8", "eslint": "^8",
"eslint-plugin-prettier": "^5.2.5", "eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"tree-kill": "^1.2.2" "ts-node-dev": "^2.0.0",
"tsconfig-paths": "^4.2.0"
} }
}, },
"node_modules/@colors/colors": { "node_modules/@colors/colors": {
@ -45,6 +45,19 @@
"node": ">=0.1.90" "node": ">=0.1.90"
} }
}, },
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@dabh/diagnostics": { "node_modules/@dabh/diagnostics": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
@ -205,6 +218,34 @@
"dev": true, "dev": true,
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@mongodb-js/saslprep": { "node_modules/@mongodb-js/saslprep": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz",
@ -265,6 +306,34 @@
"url": "https://opencollective.com/pkgr" "url": "https://opencollective.com/pkgr"
} }
}, },
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true,
"license": "MIT"
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/body-parser": { "node_modules/@types/body-parser": {
"version": "1.19.5", "version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@ -370,6 +439,20 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/strip-json-comments": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
"integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/triple-beam": { "node_modules/@types/triple-beam": {
"version": "1.3.5", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
@ -777,6 +860,19 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/acorn-walk": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"acorn": "^8.11.0"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@ -820,6 +916,27 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -858,6 +975,19 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@ -910,6 +1040,13 @@
"node": ">=16.20.1" "node": ">=16.20.1"
} }
}, },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -976,19 +1113,41 @@
} }
}, },
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "4.0.3", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"readdirp": "^4.0.1" "anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
}, },
"engines": { "engines": {
"node": ">= 14.16.0" "node": ">= 8.10.0"
}, },
"funding": { "funding": {
"url": "https://paulmillr.com/funding/" "url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/chokidar/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
} }
}, },
"node_modules/color": { "node_modules/color": {
@ -1113,6 +1272,13 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -1161,6 +1327,16 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/doctrine": { "node_modules/doctrine": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@ -1188,6 +1364,16 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/dynamic-dedupe": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
"integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
}
},
"node_modules/ee-first": { "node_modules/ee-first": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -1711,6 +1897,21 @@
"dev": true, "dev": true,
"license": "ISC" "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==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -1983,6 +2184,35 @@
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -2091,6 +2321,19 @@
"integrity": "sha512-AhpYAAaZsPjU7smaBomDt1SOQshi9rEm6BlTbfVwsG1vNmeHKtEedJi62sHZzJTyKNtwzmNnrsd55kjwJ7054A==", "integrity": "sha512-AhpYAAaZsPjU7smaBomDt1SOQshi9rEm6BlTbfVwsG1vNmeHKtEedJi62sHZzJTyKNtwzmNnrsd55kjwJ7054A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/kareem": { "node_modules/kareem": {
"version": "2.6.3", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@ -2170,6 +2413,13 @@
"node": ">= 12.0.0" "node": ">= 12.0.0"
} }
}, },
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
"license": "ISC"
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -2267,6 +2517,29 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/moment": { "node_modules/moment": {
"version": "2.30.1", "version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@ -2449,6 +2722,16 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-hash": { "node_modules/object-hash": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
@ -2611,6 +2894,13 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "8.2.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
@ -2769,17 +3059,37 @@
} }
}, },
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "4.1.2", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": { "engines": {
"node": ">= 14.18.0" "node": ">=8.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"type": "individual", "url": "https://github.com/sponsors/ljharb"
"url": "https://paulmillr.com/funding/"
} }
}, },
"node_modules/resolve-from": { "node_modules/resolve-from": {
@ -3061,6 +3371,27 @@
"is-arrayish": "^0.3.1" "is-arrayish": "^0.3.1"
} }
}, },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/sparse-bitfield": { "node_modules/sparse-bitfield": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
@ -3110,6 +3441,16 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/strip-json-comments": { "node_modules/strip-json-comments": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@ -3136,6 +3477,19 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/synckit": { "node_modules/synckit": {
"version": "0.11.4", "version": "0.11.4",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.4.tgz",
@ -3232,6 +3586,137 @@
"typescript": ">=4.8.4" "typescript": ">=4.8.4"
} }
}, },
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/ts-node-dev": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-2.0.0.tgz",
"integrity": "sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.1",
"dynamic-dedupe": "^0.3.0",
"minimist": "^1.2.6",
"mkdirp": "^1.0.4",
"resolve": "^1.0.0",
"rimraf": "^2.6.1",
"source-map-support": "^0.5.12",
"tree-kill": "^1.2.2",
"ts-node": "^10.4.0",
"tsconfig": "^7.0.0"
},
"bin": {
"ts-node-dev": "lib/bin.js",
"tsnd": "lib/bin.js"
},
"engines": {
"node": ">=0.8.0"
},
"peerDependencies": {
"node-notifier": "*",
"typescript": "*"
},
"peerDependenciesMeta": {
"node-notifier": {
"optional": true
}
}
},
"node_modules/ts-node-dev/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/tsconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
"integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/strip-bom": "^3.0.0",
"@types/strip-json-comments": "0.0.30",
"strip-bom": "^3.0.0",
"strip-json-comments": "^2.0.0"
}
},
"node_modules/tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tsconfig/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@ -3323,6 +3808,13 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true,
"license": "MIT"
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -3471,6 +3963,26 @@
} }
} }
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/yocto-queue": { "node_modules/yocto-queue": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View File

@ -5,10 +5,8 @@
"main": "index.ts", "main": "index.ts",
"scripts": { "scripts": {
"start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js", "start": "node --enable-source-maps --import ./build/src/pathman.js build/src/index.js",
"dev": "ts-node-dev --openssl-legacy-provider -r tsconfig-paths/register src/index.ts ",
"build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui", "build": "tsc --incremental --sourceMap && ncp static/webui build/static/webui",
"build:dev": "tsc --incremental --sourceMap",
"build-and-start": "npm run build && npm run start",
"dev": "node scripts/dev.js",
"verify": "tsgo --noEmit", "verify": "tsgo --noEmit",
"lint": "eslint --ext .ts .", "lint": "eslint --ext .ts .",
"lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .", "lint:ci": "eslint --ext .ts --rule \"prettier/prettier: off\" .",
@ -38,10 +36,10 @@
"@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0", "@typescript-eslint/parser": "^8.28.0",
"@typescript/native-preview": "^7.0.0-dev.20250523.1", "@typescript/native-preview": "^7.0.0-dev.20250523.1",
"chokidar": "^4.0.3",
"eslint": "^8", "eslint": "^8",
"eslint-plugin-prettier": "^5.2.5", "eslint-plugin-prettier": "^5.2.5",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"tree-kill": "^1.2.2" "ts-node-dev": "^2.0.0",
"tsconfig-paths": "^4.2.0"
} }
} }

View File

@ -1,55 +0,0 @@
/* eslint-disable */
const { spawn } = require("child_process");
const chokidar = require("chokidar");
const kill = require("tree-kill");
let secret = "";
for (let i = 0; i != 10; ++i) {
secret += String.fromCharCode(Math.floor(Math.random() * 26) + 0x41);
}
const args = [...process.argv].splice(2);
args.push("--dev");
args.push("--secret");
args.push(secret);
let buildproc, runproc;
function run(changedFile) {
if (changedFile) {
console.log(`Change to ${changedFile} detected`);
}
if (buildproc) {
kill(buildproc.pid);
buildproc = undefined;
}
if (runproc) {
kill(runproc.pid);
runproc = undefined;
}
const thisbuildproc = spawn("npm", ["run", "build:dev"], { stdio: "inherit", shell: true });
buildproc = thisbuildproc;
buildproc.on("exit", code => {
if (buildproc !== thisbuildproc) {
return;
}
buildproc = undefined;
if (code === 0) {
runproc = spawn("npm", ["run", "start", "--", ...args], { stdio: "inherit", shell: true });
runproc.on("exit", () => {
runproc = undefined;
});
}
});
}
run();
chokidar.watch("src").on("change", run);
chokidar.watch("static/fixed_responses").on("change", run);
chokidar.watch("static/webui").on("change", async () => {
try {
await fetch("http://localhost/custom/webuiFileChangeDetected?secret=" + secret);
} catch (e) {}
});

View File

@ -1,7 +1,6 @@
// Based on https://onlyg.it/OpenWF/Translations/src/branch/main/update.php // Based on https://onlyg.it/OpenWF/Translations/src/branch/main/update.php
// Converted via ChatGPT-4o // Converted via ChatGPT-4o
/* eslint-disable */
const fs = require("fs"); const fs = require("fs");
function extractStrings(content) { function extractStrings(content) {

View File

@ -4,16 +4,16 @@ import { config } from "@/src/services/configService";
import { buildConfig } from "@/src/services/buildConfigService"; import { buildConfig } from "@/src/services/buildConfigService";
import { Account } from "@/src/models/loginModel"; import { Account } from "@/src/models/loginModel";
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "@/src/services/loginService"; import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes"; import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { version_compare } from "@/src/helpers/inventoryHelpers"; import { version_compare } from "@/src/helpers/inventoryHelpers";
import { sendWsBroadcastTo } from "@/src/services/webService";
export const loginController: RequestHandler = async (request, response) => { export const loginController: RequestHandler = async (request, response) => {
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
const account = await Account.findOne({ email: loginRequest.email }); const account = await Account.findOne({ email: loginRequest.email });
const nonce = Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
const buildLabel: string = const buildLabel: string =
typeof request.query.buildLabel == "string" typeof request.query.buildLabel == "string"
@ -42,14 +42,26 @@ export const loginController: RequestHandler = async (request, response) => {
loginRequest.ClientType == "webui-register") loginRequest.ClientType == "webui-register")
) { ) {
try { try {
const name = await getUsernameFromEmail(loginRequest.email); const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
let name = nameFromEmail || loginRequest.email.substring(1) || "SpaceNinja";
if (await isNameTaken(name)) {
let suffix = 0;
do {
++suffix;
name = nameFromEmail + suffix;
} while (await isNameTaken(name));
}
const newAccount = await createAccount({ const newAccount = await createAccount({
email: loginRequest.email, email: loginRequest.email,
password: loginRequest.password, password: loginRequest.password,
DisplayName: name, DisplayName: name,
CountryCode: loginRequest.lang?.toUpperCase() ?? "EN", CountryCode: loginRequest.lang?.toUpperCase() ?? "EN",
ClientType: loginRequest.ClientType, ClientType: loginRequest.ClientType == "webui-register" ? "webui" : loginRequest.ClientType,
Nonce: createNonce(), CrossPlatformAllowed: true,
ForceLogoutVersion: 0,
ConsentNeeded: false,
TrackedSettings: [],
Nonce: nonce,
BuildLabel: buildLabel, BuildLabel: buildLabel,
LastLogin: new Date() LastLogin: new Date()
}); });
@ -68,11 +80,22 @@ export const loginController: RequestHandler = async (request, response) => {
return; return;
} }
if (loginRequest.ClientType == "webui-register") {
response.status(400).json({ error: "account already exists" });
return;
}
if (!isCorrectPassword(loginRequest.password, account.password)) { if (!isCorrectPassword(loginRequest.password, account.password)) {
response.status(400).json({ error: "incorrect login data" }); response.status(400).json({ error: "incorrect login data" });
return; return;
} }
if (loginRequest.ClientType == "webui") {
if (!account.Nonce) {
account.ClientType = "webui";
account.Nonce = nonce;
}
} else {
if (account.Nonce && account.ClientType != "webui" && !account.Dropped && !loginRequest.kick) { if (account.Nonce && account.ClientType != "webui" && !account.Dropped && !loginRequest.kick) {
// U17 seems to handle "nonce still set" like a login failure. // U17 seems to handle "nonce still set" like a login failure.
if (version_compare(buildLabel, "2015.12.05.18.07") >= 0) { if (version_compare(buildLabel, "2015.12.05.18.07") >= 0) {
@ -82,15 +105,13 @@ export const loginController: RequestHandler = async (request, response) => {
} }
account.ClientType = loginRequest.ClientType; account.ClientType = loginRequest.ClientType;
account.Nonce = createNonce(); account.Nonce = nonce;
account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN"; account.CountryCode = loginRequest.lang?.toUpperCase() ?? "EN";
account.BuildLabel = buildLabel; account.BuildLabel = buildLabel;
account.LastLogin = new Date(); account.LastLogin = new Date();
}
await account.save(); await account.save();
// Tell WebUI its nonce has been invalidated
sendWsBroadcastTo(account._id.toString(), { logged_out: true });
response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel)); response.json(createLoginResponse(myAddress, myUrlBase, account.toJSON(), buildLabel));
}; };

View File

@ -1,6 +1,5 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { Account } from "@/src/models/loginModel"; import { Account } from "@/src/models/loginModel";
import { sendWsBroadcastTo } from "@/src/services/webService";
export const logoutController: RequestHandler = async (req, res) => { export const logoutController: RequestHandler = async (req, res) => {
if (!req.query.accountId) { if (!req.query.accountId) {
@ -11,7 +10,7 @@ export const logoutController: RequestHandler = async (req, res) => {
throw new Error("Request is missing nonce parameter"); throw new Error("Request is missing nonce parameter");
} }
const stat = await Account.updateOne( await Account.updateOne(
{ {
_id: req.query.accountId, _id: req.query.accountId,
Nonce: nonce Nonce: nonce
@ -20,10 +19,6 @@ export const logoutController: RequestHandler = async (req, res) => {
Nonce: 0 Nonce: 0
} }
); );
if (stat.modifiedCount) {
// Tell WebUI its nonce has been invalidated
sendWsBroadcastTo(req.query.accountId as string, { logged_out: true });
}
res.writeHead(200, { res.writeHead(200, {
"Content-Type": "text/html", "Content-Type": "text/html",

View File

@ -1,10 +0,0 @@
import { args } from "@/src/helpers/commandLineArguments";
import { sendWsBroadcast } from "@/src/services/webService";
import { RequestHandler } from "express";
export const webuiFileChangeDetectedController: RequestHandler = (req, res) => {
if (args.dev && args.secret && req.query.secret == args.secret) {
sendWsBroadcast({ reload: true });
}
res.end();
};

View File

@ -1,23 +0,0 @@
interface IArguments {
configPath?: string;
dev?: boolean;
secret?: string;
}
export const args: IArguments = {};
for (let i = 2; i < process.argv.length; ) {
switch (process.argv[i++]) {
case "--configPath":
args.configPath = process.argv[i++];
break;
case "--dev":
args.dev = true;
break;
case "--secret":
args.secret = process.argv[i++];
break;
}
}

View File

@ -1,4 +1,5 @@
import path from "path"; import path from "path";
export const rootDir = path.join(__dirname, "../.."); export const rootDir = path.join(__dirname, "../..");
export const repoDir = path.basename(rootDir) != "build" ? rootDir : path.join(rootDir, ".."); export const isDev = path.basename(rootDir) != "build";
export const repoDir = isDev ? rootDir : path.join(rootDir, "..");

View File

@ -11,13 +11,13 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
email: { type: String, required: true, unique: true }, email: { type: String, required: true, unique: true },
password: { type: String, required: true }, password: { type: String, required: true },
DisplayName: { type: String, required: true, unique: true }, DisplayName: { type: String, required: true, unique: true },
CountryCode: { type: String, default: "" }, CountryCode: { type: String, required: true },
ClientType: { type: String }, ClientType: { type: String },
CrossPlatformAllowed: { type: Boolean, default: true }, CrossPlatformAllowed: { type: Boolean, required: true },
ForceLogoutVersion: { type: Number, default: 0 }, ForceLogoutVersion: { type: Number, required: true },
AmazonAuthToken: { type: String }, AmazonAuthToken: { type: String },
AmazonRefreshToken: { type: String }, AmazonRefreshToken: { type: String },
ConsentNeeded: { type: Boolean, default: false }, ConsentNeeded: { type: Boolean, required: true },
TrackedSettings: { type: [String], default: [] }, TrackedSettings: { type: [String], default: [] },
Nonce: { type: Number, default: 0 }, Nonce: { type: Number, default: 0 },
BuildLabel: String, BuildLabel: String,

View File

@ -284,7 +284,6 @@ apiRouter.post("/inventorySlots.php", inventorySlotsController);
apiRouter.post("/joinSession.php", joinSessionController); apiRouter.post("/joinSession.php", joinSessionController);
apiRouter.post("/login.php", loginController); apiRouter.post("/login.php", loginController);
apiRouter.post("/loginRewardsSelection.php", loginRewardsSelectionController); apiRouter.post("/loginRewardsSelection.php", loginRewardsSelectionController);
apiRouter.post("/logout.php", logoutController); // from ~U16, don't know when they changed it to GET
apiRouter.post("/maturePet.php", maturePetController); apiRouter.post("/maturePet.php", maturePetController);
apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController); apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController);
apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController); apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController);

View File

@ -11,7 +11,6 @@ import { renameAccountController } from "@/src/controllers/custom/renameAccountC
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController"; import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController"; import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController"; import { addMissingMaxRankModsController } from "@/src/controllers/custom/addMissingMaxRankModsController";
import { webuiFileChangeDetectedController } from "@/src/controllers/custom/webuiFileChangeDetectedController";
import { createAccountController } from "@/src/controllers/custom/createAccountController"; import { createAccountController } from "@/src/controllers/custom/createAccountController";
import { createMessageController } from "@/src/controllers/custom/createMessageController"; import { createMessageController } from "@/src/controllers/custom/createMessageController";
@ -21,10 +20,10 @@ import { addXpController } from "@/src/controllers/custom/addXpController";
import { importController } from "@/src/controllers/custom/importController"; import { importController } from "@/src/controllers/custom/importController";
import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController"; import { manageQuestsController } from "@/src/controllers/custom/manageQuestsController";
import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController"; import { setEvolutionProgressController } from "@/src/controllers/custom/setEvolutionProgressController";
import { setBoosterController } from "@/src/controllers/custom/setBoosterController";
import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController"; import { getConfigDataController } from "@/src/controllers/custom/getConfigDataController";
import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController"; import { updateConfigDataController } from "@/src/controllers/custom/updateConfigDataController";
import { setBoosterController } from "../controllers/custom/setBoosterController";
const customRouter = express.Router(); const customRouter = express.Router();
@ -39,7 +38,6 @@ customRouter.get("/renameAccount", renameAccountController);
customRouter.get("/ircDropped", ircDroppedController); customRouter.get("/ircDropped", ircDroppedController);
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController); customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);
customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController); customRouter.get("/addMissingMaxRankMods", addMissingMaxRankModsController);
customRouter.get("/webuiFileChangeDetected", webuiFileChangeDetectedController);
customRouter.post("/createAccount", createAccountController); customRouter.post("/createAccount", createAccountController);
customRouter.post("/createMessage", createMessageController); customRouter.post("/createMessage", createMessageController);

View File

@ -1,9 +1,6 @@
import express from "express"; import express from "express";
import path from "path"; import path from "path";
import { repoDir, rootDir } from "@/src/helpers/pathHelper"; import { repoDir, rootDir } from "@/src/helpers/pathHelper";
import { args } from "@/src/helpers/commandLineArguments";
const baseDir = args.dev ? repoDir : rootDir;
const webuiRouter = express.Router(); const webuiRouter = express.Router();
@ -22,29 +19,29 @@ webuiRouter.use("/webui", (req, res, next) => {
// Serve virtual routes // Serve virtual routes
webuiRouter.get("/webui/inventory", (_req, res) => { webuiRouter.get("/webui/inventory", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get(/webui\/powersuit\/(.+)/, (_req, res) => { webuiRouter.get(/webui\/powersuit\/(.+)/, (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/mods", (_req, res) => { webuiRouter.get("/webui/mods", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/settings", (_req, res) => { webuiRouter.get("/webui/settings", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/quests", (_req, res) => { webuiRouter.get("/webui/quests", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/cheats", (_req, res) => { webuiRouter.get("/webui/cheats", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
webuiRouter.get("/webui/import", (_req, res) => { webuiRouter.get("/webui/import", (_req, res) => {
res.sendFile(path.join(baseDir, "static/webui/index.html")); res.sendFile(path.join(rootDir, "static/webui/index.html"));
}); });
// Serve static files // Serve static files
webuiRouter.use("/webui", express.static(path.join(baseDir, "static/webui"))); webuiRouter.use("/webui", express.static(path.join(rootDir, "static/webui")));
// Serve favicon // Serve favicon
webuiRouter.get("/favicon.ico", (_req, res) => { webuiRouter.get("/favicon.ico", (_req, res) => {
@ -61,7 +58,7 @@ webuiRouter.get("/webui/riven-tool/RivenParser.js", (_req, res) => {
// Serve translations // Serve translations
webuiRouter.get("/translations/:file", (req, res) => { webuiRouter.get("/translations/:file", (req, res) => {
res.sendFile(path.join(baseDir, `static/webui/translations/${req.params.file}`)); res.sendFile(path.join(rootDir, `static/webui/translations/${req.params.file}`));
}); });
export { webuiRouter }; export { webuiRouter };

View File

@ -1,7 +1,6 @@
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import { repoDir } from "@/src/helpers/pathHelper"; import { repoDir } from "@/src/helpers/pathHelper";
import { args } from "@/src/helpers/commandLineArguments";
export interface IConfig { export interface IConfig {
mongodbUrl: string; mongodbUrl: string;
@ -80,7 +79,7 @@ export interface IConfig {
}; };
} }
export const configPath = path.join(repoDir, args.configPath ?? "config.json"); export const configPath = path.join(repoDir, process.argv[2] ?? "config.json");
export const config: IConfig = { export const config: IConfig = {
mongodbUrl: "mongodb://127.0.0.1:27017/openWF", mongodbUrl: "mongodb://127.0.0.1:27017/openWF",

View File

@ -1825,15 +1825,12 @@ export const addChallenges = (
return affiliationMods; return affiliationMods;
}; };
export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes, Tier }: IMission): void => { export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag, Completes }: IMission): void => {
const { Missions } = inventory; const { Missions } = inventory;
const itemIndex = Missions.findIndex(item => item.Tag === Tag); const itemIndex = Missions.findIndex(item => item.Tag === Tag);
if (itemIndex !== -1) { if (itemIndex !== -1) {
Missions[itemIndex].Completes += Completes; Missions[itemIndex].Completes += Completes;
if (Tier) {
Missions[itemIndex].Tier = Tier;
}
} else { } else {
Missions.push({ Tag, Completes }); Missions.push({ Tag, Completes });
} }

View File

@ -18,23 +18,6 @@ export const isNameTaken = async (name: string): Promise<boolean> => {
return !!(await Account.findOne({ DisplayName: name })); return !!(await Account.findOne({ DisplayName: name }));
}; };
export const createNonce = (): number => {
return Math.round(Math.random() * Number.MAX_SAFE_INTEGER);
};
export const getUsernameFromEmail = async (email: string): Promise<string> => {
const nameFromEmail = email.substring(0, email.indexOf("@"));
let name = nameFromEmail || email.substring(1) || "SpaceNinja";
if (await isNameTaken(name)) {
let suffix = 0;
do {
++suffix;
name = nameFromEmail + suffix;
} while (await isNameTaken(name));
}
return nameFromEmail;
};
export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => { export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
const account = new Account(accountData); const account = new Account(accountData);
try { try {

View File

@ -1,5 +1,5 @@
import { unixTimesInMs } from "@/src/constants/timeConstants"; import { unixTimesInMs } from "@/src/constants/timeConstants";
import { args } from "@/src/helpers/commandLineArguments"; import { isDev } from "@/src/helpers/pathHelper";
import { catBreadHash } from "@/src/helpers/stringHelpers"; import { catBreadHash } from "@/src/helpers/stringHelpers";
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
import { mixSeeds, SRng } from "@/src/services/rngService"; import { mixSeeds, SRng } from "@/src/services/rngService";
@ -379,7 +379,7 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani
return cacheEntry; return cacheEntry;
}; };
if (args.dev) { if (isDev) {
if ( if (
getCycleDuration(ExportVendors["/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest"]) != getCycleDuration(ExportVendors["/Lotus/Types/Game/VendorManifests/Hubs/TeshinHardModeVendorManifest"]) !=
unixTimesInMs.week unixTimesInMs.week

View File

@ -6,10 +6,6 @@ import { logger } from "../utils/logger";
import { app } from "../app"; import { app } from "../app";
import { AddressInfo } from "node:net"; import { AddressInfo } from "node:net";
import ws from "ws"; import ws from "ws";
import { Account } from "../models/loginModel";
import { createAccount, createNonce, getUsernameFromEmail, isCorrectPassword } from "./loginService";
import { IDatabaseAccountJson } from "../types/loginTypes";
import { HydratedDocument } from "mongoose";
let httpServer: http.Server | undefined; let httpServer: http.Server | undefined;
let httpsServer: https.Server | undefined; let httpsServer: https.Server | undefined;
@ -29,7 +25,7 @@ export const startWebServer = (): void => {
httpServer = http.createServer(app); httpServer = http.createServer(app);
httpServer.listen(httpPort, () => { httpServer.listen(httpPort, () => {
wsServer = new ws.Server({ server: httpServer }); wsServer = new ws.Server({ server: httpServer });
wsServer.on("connection", wsOnConnect); //wsServer.on("connection", wsOnConnect);
logger.info("HTTP server started on port " + httpPort); logger.info("HTTP server started on port " + httpPort);
@ -37,7 +33,7 @@ export const startWebServer = (): void => {
httpsServer = https.createServer(tlsOptions, app); httpsServer = https.createServer(tlsOptions, app);
httpsServer.listen(httpsPort, () => { httpsServer.listen(httpsPort, () => {
wssServer = new ws.Server({ server: httpsServer }); wssServer = new ws.Server({ server: httpsServer });
wssServer.on("connection", wsOnConnect); //wssServer.on("connection", wsOnConnect);
logger.info("HTTPS server started on port " + httpsPort); logger.info("HTTPS server started on port " + httpsPort);
@ -96,102 +92,11 @@ export const stopWebServer = async (): Promise<void> => {
await Promise.all(promises); await Promise.all(promises);
}; };
interface IWsCustomData extends ws { /*const wsOnConnect = (ws: ws, _req: http.IncomingMessage): void => {
accountId?: string; ws.on("message", console.log);
} };*/
interface IWsMsgFromClient { export const sendWsBroadcast = <T>(data: T): void => {
auth?: {
email: string;
password: string;
isRegister: boolean;
};
logout?: boolean;
}
interface IWsMsgToClient {
reload?: boolean;
ports?: {
http: number | undefined;
https: number | undefined;
};
config_reloaded?: boolean;
auth_succ?: {
id: string;
DisplayName: string;
Nonce: number;
};
auth_fail?: {
isRegister: boolean;
};
logged_out?: boolean;
}
const wsOnConnect = (ws: ws, _req: http.IncomingMessage): void => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
ws.on("message", async msg => {
const data = JSON.parse(String(msg)) as IWsMsgFromClient;
if (data.auth) {
let account: IDatabaseAccountJson | null = await Account.findOne({ email: data.auth.email });
if (account) {
if (isCorrectPassword(data.auth.password, account.password)) {
if (!account.Nonce) {
account.ClientType = "webui";
account.Nonce = createNonce();
await (account as HydratedDocument<IDatabaseAccountJson>).save();
}
} else {
account = null;
}
} else if (data.auth.isRegister) {
const name = await getUsernameFromEmail(data.auth.email);
account = await createAccount({
email: data.auth.email,
password: data.auth.password,
ClientType: "webui",
LastLogin: new Date(),
DisplayName: name,
Nonce: createNonce()
});
}
if (account) {
(ws as IWsCustomData).accountId = account.id;
ws.send(
JSON.stringify({
auth_succ: {
id: account.id,
DisplayName: account.DisplayName,
Nonce: account.Nonce
}
} satisfies IWsMsgToClient)
);
} else {
ws.send(
JSON.stringify({
auth_fail: {
isRegister: data.auth.isRegister
}
} satisfies IWsMsgToClient)
);
}
}
if (data.logout) {
const accountId = (ws as IWsCustomData).accountId;
(ws as IWsCustomData).accountId = undefined;
await Account.updateOne(
{
_id: accountId,
ClientType: "webui"
},
{
Nonce: 0
}
);
}
});
};
export const sendWsBroadcast = (data: IWsMsgToClient): void => {
const msg = JSON.stringify(data); const msg = JSON.stringify(data);
if (wsServer) { if (wsServer) {
for (const client of wsServer.clients) { for (const client of wsServer.clients) {
@ -204,21 +109,3 @@ export const sendWsBroadcast = (data: IWsMsgToClient): void => {
} }
} }
}; };
export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => {
const msg = JSON.stringify(data);
if (wsServer) {
for (const client of wsServer.clients) {
if ((client as IWsCustomData).accountId == accountId) {
client.send(msg);
}
}
}
if (wssServer) {
for (const client of wssServer.clients) {
if ((client as IWsCustomData).accountId == accountId) {
client.send(msg);
}
}
}
};

View File

@ -2,7 +2,7 @@ import { Types } from "mongoose";
export interface IAccountAndLoginResponseCommons { export interface IAccountAndLoginResponseCommons {
DisplayName: string; DisplayName: string;
CountryCode?: string; CountryCode: string;
ClientType?: string; ClientType?: string;
CrossPlatformAllowed?: boolean; CrossPlatformAllowed?: boolean;
ForceLogoutVersion?: number; ForceLogoutVersion?: number;

View File

@ -37,7 +37,7 @@
<li class="nav-item dropdown user-dropdown"> <li class="nav-item dropdown user-dropdown">
<button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button> <button class="nav-link dropdown-toggle displayname" data-bs-toggle="dropdown" aria-expanded="false"></button>
<ul class="dropdown-menu dropdown-menu-end"> <ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="/webui/" onclick="doLogout();" data-loc="navbar_logout"></a></li> <li><a class="dropdown-item" href="/webui/" onclick="logout();" data-loc="navbar_logout"></a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();renameAccount();" data-loc="navbar_renameAccount"></a></li> <li><a class="dropdown-item" href="#" onclick="event.preventDefault();renameAccount();" data-loc="navbar_renameAccount"></a></li>
<li><a class="dropdown-item" href="#" onclick="event.preventDefault();deleteAccount();" data-loc="navbar_deleteAccount"></a></li> <li><a class="dropdown-item" href="#" onclick="event.preventDefault();deleteAccount();" data-loc="navbar_deleteAccount"></a></li>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,46 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
let auth_pending = false,
did_initial_auth = false,
ws_is_open = false;
const sendAuth = isRegister => {
if (ws_is_open && localStorage.getItem("email") && localStorage.getItem("password")) {
auth_pending = true;
window.ws.send(
JSON.stringify({
auth: {
email: localStorage.getItem("email"),
password: wp.encSync(localStorage.getItem("password")),
isRegister
}
})
);
}
};
function openWebSocket() { function openWebSocket() {
window.ws = new WebSocket("/custom/ws"); window.ws = new WebSocket("/custom/ws");
window.ws.onopen = () => {
ws_is_open = true;
sendAuth(false);
};
window.ws.onmessage = e => { window.ws.onmessage = e => {
const msg = JSON.parse(e.data); const msg = JSON.parse(e.data);
if ("reload" in msg) {
setTimeout(() => {
getWebSocket().then(() => {
location.reload();
});
}, 100);
}
if ("ports" in msg) { if ("ports" in msg) {
location.port = location.protocol == "https:" ? msg.ports.https : msg.ports.http; location.port = location.protocol == "https:" ? msg.ports.https : msg.ports.http;
} }
@ -50,9 +11,31 @@ function openWebSocket() {
single.loadRoute("/webui/cheats"); single.loadRoute("/webui/cheats");
} }
} }
if ("auth_succ" in msg) { };
auth_pending = false; window.ws.onclose = function () {
const data = msg.auth_succ; setTimeout(openWebSocket, 3000);
};
}
openWebSocket();
let loginOrRegisterPending = false;
window.registerSubmit = false;
function doLogin() {
if (loginOrRegisterPending) {
return;
}
loginOrRegisterPending = true;
localStorage.setItem("email", $("#email").val());
localStorage.setItem("password", $("#password").val());
loginFromLocalStorage();
registerSubmit = false;
}
function loginFromLocalStorage() {
const isRegister = registerSubmit;
doLoginRequest(
data => {
if (single.getCurrentPath() == "/webui/") { if (single.getCurrentPath() == "/webui/") {
single.loadRoute("/webui/inventory"); single.loadRoute("/webui/inventory");
} }
@ -62,74 +45,54 @@ function openWebSocket() {
if (window.dict) { if (window.dict) {
updateLocElements(); updateLocElements();
} }
if (!did_initial_auth) {
did_initial_auth = true;
updateInventory(); updateInventory();
} },
} () => {
if ("auth_fail" in msg) {
auth_pending = false;
logout(); logout();
if (single.getCurrentPath() == "/webui/") { alert(loc(isRegister ? "code_regFail" : "code_loginFail"));
alert(loc(msg.auth_fail.isRegister ? "code_regFail" : "code_loginFail"));
} else {
single.loadRoute("/webui/");
} }
);
} }
if ("logged_out" in msg) {
sendAuth();
}
};
window.ws.onclose = function () {
ws_is_open = false;
setTimeout(openWebSocket, 3000);
};
}
openWebSocket();
function getWebSocket() { function doLoginRequest(succ_cb, fail_cb) {
return new Promise(resolve => { const req = $.post({
let interval; url: "/api/login.php",
interval = setInterval(() => { contentType: "text/plain",
if (ws_is_open) { data: JSON.stringify({
clearInterval(interval); email: localStorage.getItem("email").toLowerCase(),
resolve(window.ws); password: wp.encSync(localStorage.getItem("password"), "hex"),
} time: parseInt(new Date() / 1000),
}, 10); s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind
lang: "en",
date: 1501230947855458660, // ???
ClientType: registerSubmit ? "webui-register" : "webui",
PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data
})
});
req.done(succ_cb);
req.fail(fail_cb);
req.always(() => {
loginOrRegisterPending = false;
}); });
}
window.registerSubmit = false;
function doLogin() {
if (auth_pending) {
return;
}
localStorage.setItem("email", $("#email").val());
localStorage.setItem("password", $("#password").val());
sendAuth(registerSubmit);
window.registerSubmit = false;
} }
function revalidateAuthz(succ_cb) { function revalidateAuthz(succ_cb) {
getWebSocket().then(() => { return doLoginRequest(
// We have a websocket connection, so authz should be good. data => {
window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce;
succ_cb(); succ_cb();
}); },
() => {
logout();
alert(loc("code_nonValidAuthz"));
single.loadRoute("/webui/"); // Show login screen
}
);
} }
function logout() { function logout() {
localStorage.removeItem("email"); localStorage.removeItem("email");
localStorage.removeItem("password"); localStorage.removeItem("password");
did_initial_auth = false;
}
function doLogout() {
logout();
if (ws_is_open) {
// Unsubscribe from notifications about nonce invalidation
window.ws.send(JSON.stringify({ logout: true }));
}
} }
function renameAccount() { function renameAccount() {
@ -155,6 +118,10 @@ function deleteAccount() {
} }
} }
if (localStorage.getItem("email") && localStorage.getItem("password")) {
loginFromLocalStorage();
}
single.on("route_load", function (event) { single.on("route_load", function (event) {
if (event.route.paths[0] != "/webui/") { if (event.route.paths[0] != "/webui/") {
// Authorised route? // Authorised route?
@ -1730,9 +1697,7 @@ function doAcquireRiven() {
if (typeof fingerprint !== "object") { if (typeof fingerprint !== "object") {
fingerprint = JSON.parse(fingerprint); fingerprint = JSON.parse(fingerprint);
} }
} catch (e) { } catch (e) {}
/* empty */
}
if ( if (
typeof fingerprint !== "object" || typeof fingerprint !== "object" ||
!("compat" in fingerprint) || !("compat" in fingerprint) ||
@ -1985,7 +1950,7 @@ function doAddAllMods() {
const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1"); const req = $.get("/api/inventory.php?" + window.authz + "&xpBasedLevelCapDisabled=1");
req.done(data => { req.done(data => {
for (const modOwned of data.RawUpgrades) { for (const modOwned of data.RawUpgrades) {
if ((modOwned.ItemCount ?? 1) > 0) { if (modOwned.ItemCount ?? 1 > 0) {
modsAll.delete(modOwned.ItemType); modsAll.delete(modOwned.ItemType);
} }
} }

View File

@ -5,6 +5,7 @@ dict = {
general_bulkActions: `Massenaktionen`, general_bulkActions: `Massenaktionen`,
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`, code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`, code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
code_nonValidAuthz: `Deine Anmeldedaten sind nicht mehr gültig.`,
code_changeNameConfirm: `In welchen Namen möchtest du deinen Account umbenennen?`, code_changeNameConfirm: `In welchen Namen möchtest du deinen Account umbenennen?`,
code_deleteAccountConfirm: `Bist du sicher, dass du deinen Account |DISPLAYNAME| (|EMAIL|) löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.`, code_deleteAccountConfirm: `Bist du sicher, dass du deinen Account |DISPLAYNAME| (|EMAIL|) löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.`,
code_archgun: `Arch-Gewehr`, code_archgun: `Arch-Gewehr`,

View File

@ -4,6 +4,7 @@ dict = {
general_bulkActions: `Bulk Actions`, general_bulkActions: `Bulk Actions`,
code_loginFail: `Login failed. Double-check the email and password.`, code_loginFail: `Login failed. Double-check the email and password.`,
code_regFail: `Registration failed. Account already exists?`, code_regFail: `Registration failed. Account already exists?`,
code_nonValidAuthz: `Your credentials are no longer valid.`,
code_changeNameConfirm: `What would you like to change your account name to?`, code_changeNameConfirm: `What would you like to change your account name to?`,
code_deleteAccountConfirm: `Are you sure you want to delete your account |DISPLAYNAME| (|EMAIL|)? This action cannot be undone.`, code_deleteAccountConfirm: `Are you sure you want to delete your account |DISPLAYNAME| (|EMAIL|)? This action cannot be undone.`,
code_archgun: `Archgun`, code_archgun: `Archgun`,

View File

@ -5,6 +5,7 @@ dict = {
general_bulkActions: `Acciones masivas`, general_bulkActions: `Acciones masivas`,
code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`, code_loginFail: `Error al iniciar sesión. Verifica el correo electrónico y la contraseña.`,
code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`, code_regFail: `Error al registrar la cuenta. ¿Ya existe una cuenta con este correo?`,
code_nonValidAuthz: `Tus credenciales no son válidas.`,
code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`, code_changeNameConfirm: `¿Qué nombre te gustaría ponerle a tu cuenta?`,
code_deleteAccountConfirm: `¿Estás seguro de que deseas eliminar tu cuenta |DISPLAYNAME| (|EMAIL|)? Esta acción es permanente.`, code_deleteAccountConfirm: `¿Estás seguro de que deseas eliminar tu cuenta |DISPLAYNAME| (|EMAIL|)? Esta acción es permanente.`,
code_archgun: `Archcañón`, code_archgun: `Archcañón`,
@ -100,7 +101,7 @@ dict = {
inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`, inventory_bulkRankUpSentinels: `Maximizar rango de todos los centinelas`,
inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`, inventory_bulkRankUpSentinelWeapons: `Maximizar rango de todas las armas de centinela`,
inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`, inventory_bulkRankUpEvolutionProgress: `Maximizar todo el progreso de evolución Incarnon`,
inventory_maxPlexus: `Rango máximo de Plexus`, inventory_maxPlexus: `[UNTRANSLATED] Max Rank Plexus`,
quests_list: `Misiones`, quests_list: `Misiones`,
quests_completeAll: `Completar todas las misiones`, quests_completeAll: `Completar todas las misiones`,
@ -135,10 +136,10 @@ dict = {
cheats_infiniteRegalAya: `Aya Real infinita`, cheats_infiniteRegalAya: `Aya Real infinita`,
cheats_infiniteHelminthMaterials: `Materiales Helminto infinitos`, cheats_infiniteHelminthMaterials: `Materiales Helminto infinitos`,
cheats_claimingBlueprintRefundsIngredients: `Reclamar ingredientes devueltos por planos`, cheats_claimingBlueprintRefundsIngredients: `Reclamar ingredientes devueltos por planos`,
cheats_dontSubtractPurchaseCreditCost: `No restar costo en créditos de la compra`, cheats_dontSubtractPurchaseCreditCost: `[UNTRANSLATED] Don't Subtract Purchase Credit Cost`,
cheats_dontSubtractPurchasePlatinumCost: `No restar costo en platino de la compra`, cheats_dontSubtractPurchasePlatinumCost: `[UNTRANSLATED] Don't Subtract Purchase Platinum Cost`,
cheats_dontSubtractPurchaseItemCost: `No restar costo de ítem en la compra`, cheats_dontSubtractPurchaseItemCost: `[UNTRANSLATED] Don't Subtract Purchase Item Cost`,
cheats_dontSubtractPurchaseStandingCost: `No restar costo en reputación de la compra`, cheats_dontSubtractPurchaseStandingCost: `[UNTRANSLATED] Don't Subtract Purchase Standing Cost`,
cheats_dontSubtractVoidTraces: `No descontar vestigios del Vacío`, cheats_dontSubtractVoidTraces: `No descontar vestigios del Vacío`,
cheats_dontSubtractConsumables: `No restar consumibles`, cheats_dontSubtractConsumables: `No restar consumibles`,
cheats_unlockAllShipFeatures: `Desbloquear todas las funciones de nave`, cheats_unlockAllShipFeatures: `Desbloquear todas las funciones de nave`,
@ -159,7 +160,7 @@ dict = {
cheats_noDeathMarks: `Sin marcas de muerte`, cheats_noDeathMarks: `Sin marcas de muerte`,
cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`, cheats_noKimCooldowns: `Sin tiempo de espera para conversaciones KIM`,
cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`, cheats_syndicateMissionsRepeatable: `Misiones de sindicato rejugables`,
cheats_unlockAllProfitTakerStages: `Deslobquea todas las etapas del Roba-ganancias`, cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`, cheats_instantFinishRivenChallenge: `Terminar desafío de agrietado inmediatamente`,
cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`, cheats_instantResourceExtractorDrones: `Drones de extracción de recursos instantáneos`,
cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`, cheats_noResourceExtractorDronesDamage: `Sin daño a los drones extractores de recursos`,
@ -170,7 +171,7 @@ dict = {
cheats_noDojoResearchCosts: `Sin costo de investigación del dojo`, cheats_noDojoResearchCosts: `Sin costo de investigación del dojo`,
cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`, cheats_noDojoResearchTime: `Sin tiempo de investigación del dojo`,
cheats_fastClanAscension: `Ascenso rápido del clan`, cheats_fastClanAscension: `Ascenso rápido del clan`,
cheats_missionsCanGiveAllRelics: `Las misiones pueden otorgar todas las reliquias`, cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`, cheats_spoofMasteryRank: `Rango de maestría simulado (-1 para desactivar)`,
cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`, cheats_nightwaveStandingMultiplier: `Multiplicador de Reputación de Onda Nocturna`,
cheats_save: `Guardar`, cheats_save: `Guardar`,

View File

@ -5,6 +5,7 @@ dict = {
general_bulkActions: `Action groupée`, general_bulkActions: `Action groupée`,
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`, code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`, code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
code_nonValidAuthz: `Informations de connexion invalides`,
code_changeNameConfirm: `Nouveau nom du compte :`, code_changeNameConfirm: `Nouveau nom du compte :`,
code_deleteAccountConfirm: `Supprimer |DISPLAYNAME| (|EMAIL|) ? Cette action est irreversible.`, code_deleteAccountConfirm: `Supprimer |DISPLAYNAME| (|EMAIL|) ? Cette action est irreversible.`,
code_archgun: `Archgun`, code_archgun: `Archgun`,

View File

@ -5,6 +5,7 @@ dict = {
general_bulkActions: `Массовые действия`, general_bulkActions: `Массовые действия`,
code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`, code_loginFail: `[UNTRANSLATED] Login failed. Double-check the email and password.`,
code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`, code_regFail: `[UNTRANSLATED] Registration failed. Account already exists?`,
code_nonValidAuthz: `Ваши данные больше не действительны.`,
code_changeNameConfirm: `Какое имя вы хотите установить для своей учетной записи?`, code_changeNameConfirm: `Какое имя вы хотите установить для своей учетной записи?`,
code_deleteAccountConfirm: `Вы уверены, что хотите удалить аккаунт |DISPLAYNAME| (|EMAIL|)? Это действие нельзя отменить.`, code_deleteAccountConfirm: `Вы уверены, что хотите удалить аккаунт |DISPLAYNAME| (|EMAIL|)? Это действие нельзя отменить.`,
code_archgun: `Арч-Пушка`, code_archgun: `Арч-Пушка`,

View File

@ -5,6 +5,7 @@ dict = {
general_bulkActions: `批量操作`, general_bulkActions: `批量操作`,
code_loginFail: `登录失败。请检查邮箱和密码。`, code_loginFail: `登录失败。请检查邮箱和密码。`,
code_regFail: `注册失败。账号已存在。`, code_regFail: `注册失败。账号已存在。`,
code_nonValidAuthz: `您的登录凭证已失效。`,
code_changeNameConfirm: `您想将账户名称更改为什么?`, code_changeNameConfirm: `您想将账户名称更改为什么?`,
code_deleteAccountConfirm: `确定要删除账户 |DISPLAYNAME| (|EMAIL|) 吗?此操作不可撤销。`, code_deleteAccountConfirm: `确定要删除账户 |DISPLAYNAME| (|EMAIL|) 吗?此操作不可撤销。`,
code_archgun: `空战`, code_archgun: `空战`,
@ -100,7 +101,7 @@ dict = {
inventory_bulkRankUpSentinels: `所有守护升满级`, inventory_bulkRankUpSentinels: `所有守护升满级`,
inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`, inventory_bulkRankUpSentinelWeapons: `所有守护武器升满级`,
inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`, inventory_bulkRankUpEvolutionProgress: `所有灵化之源最大等级`,
inventory_maxPlexus: `最大深控等级`, inventory_maxPlexus: `[UNTRANSLATED] Max Rank Plexus`,
quests_list: `任务`, quests_list: `任务`,
quests_completeAll: `完成所有任务`, quests_completeAll: `完成所有任务`,
@ -135,10 +136,10 @@ dict = {
cheats_infiniteRegalAya: `无限御品阿耶`, cheats_infiniteRegalAya: `无限御品阿耶`,
cheats_infiniteHelminthMaterials: `无限Helminth材料`, cheats_infiniteHelminthMaterials: `无限Helminth材料`,
cheats_claimingBlueprintRefundsIngredients: `取消蓝图制造时返还材料`, cheats_claimingBlueprintRefundsIngredients: `取消蓝图制造时返还材料`,
cheats_dontSubtractPurchaseCreditCost: `不减少现金花费`, cheats_dontSubtractPurchaseCreditCost: `[UNTRANSLATED] Don't Subtract Purchase Credit Cost`,
cheats_dontSubtractPurchasePlatinumCost: `不减少白金花费`, cheats_dontSubtractPurchasePlatinumCost: `[UNTRANSLATED] Don't Subtract Purchase Platinum Cost`,
cheats_dontSubtractPurchaseItemCost: `不减少物品花费`, cheats_dontSubtractPurchaseItemCost: `[UNTRANSLATED] Don't Subtract Purchase Item Cost`,
cheats_dontSubtractPurchaseStandingCost: `不减少声望花费`, cheats_dontSubtractPurchaseStandingCost: `[UNTRANSLATED] Don't Subtract Purchase Standing Cost`,
cheats_dontSubtractVoidTraces: `虚空光体无消耗`, cheats_dontSubtractVoidTraces: `虚空光体无消耗`,
cheats_dontSubtractConsumables: `消耗物品使用时无损耗`, cheats_dontSubtractConsumables: `消耗物品使用时无损耗`,
cheats_unlockAllShipFeatures: `解锁所有飞船功能`, cheats_unlockAllShipFeatures: `解锁所有飞船功能`,
@ -159,7 +160,7 @@ dict = {
cheats_noDeathMarks: `无死亡标记(不会被 Stalker/Grustrag 三霸/Zanuka 猎人等标记)`, cheats_noDeathMarks: `无死亡标记(不会被 Stalker/Grustrag 三霸/Zanuka 猎人等标记)`,
cheats_noKimCooldowns: `无 KIM 冷却时间`, cheats_noKimCooldowns: `无 KIM 冷却时间`,
cheats_syndicateMissionsRepeatable: `集团任务可重复`, cheats_syndicateMissionsRepeatable: `集团任务可重复`,
cheats_unlockAllProfitTakerStages: `解锁利润收割者圆蛛所有阶段`, cheats_unlockAllProfitTakerStages: `[UNTRANSLATED] Unlock All Profit Taker Stages`,
cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`, cheats_instantFinishRivenChallenge: `立即完成裂罅挑战`,
cheats_instantResourceExtractorDrones: `即时资源采集无人机`, cheats_instantResourceExtractorDrones: `即时资源采集无人机`,
cheats_noResourceExtractorDronesDamage: `资源提取器不会损毁`, cheats_noResourceExtractorDronesDamage: `资源提取器不会损毁`,
@ -170,10 +171,10 @@ dict = {
cheats_noDojoResearchCosts: `无视道场研究消耗`, cheats_noDojoResearchCosts: `无视道场研究消耗`,
cheats_noDojoResearchTime: `无视道场研究时间`, cheats_noDojoResearchTime: `无视道场研究时间`,
cheats_fastClanAscension: `快速升级氏族`, cheats_fastClanAscension: `快速升级氏族`,
cheats_missionsCanGiveAllRelics: `任务可获取所有遗物`, cheats_missionsCanGiveAllRelics: `[UNTRANSLATED] Missions Can Give All Relics`,
cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`, cheats_spoofMasteryRank: `伪造精通段位(-1为禁用)`,
cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`, cheats_nightwaveStandingMultiplier: `午夜电波声望倍率`,
cheats_save: `保存`, cheats_save: `[UNTRANSLATED] Save`,
cheats_account: `账户`, cheats_account: `账户`,
cheats_unlockAllFocusSchools: `解锁所有专精学派`, cheats_unlockAllFocusSchools: `解锁所有专精学派`,
cheats_helminthUnlockAll: `完全升级Helminth`, cheats_helminthUnlockAll: `完全升级Helminth`,
@ -191,7 +192,7 @@ dict = {
upgrade_WarframeAbilityDuration: `+|VAL|% 技能持续时间`, upgrade_WarframeAbilityDuration: `+|VAL|% 技能持续时间`,
upgrade_WarframeAbilityStrength: `+|VAL|% 技能强度`, upgrade_WarframeAbilityStrength: `+|VAL|% 技能强度`,
upgrade_WarframeArmourMax: `+|VAL| 护甲`, upgrade_WarframeArmourMax: `+|VAL| 护甲`,
upgrade_WarframeBlastProc: `+|VAL| 护盾在击杀时附带爆炸伤害`, upgrade_WarframeBlastProc: `[UNTRANSLATED] +|VAL| Shields on kill with Blast Damage`,
upgrade_WarframeCastingSpeed: `+|VAL|% 施放速度`, upgrade_WarframeCastingSpeed: `+|VAL|% 施放速度`,
upgrade_WarframeCorrosiveDamageBoost: `对受腐蚀状态影响的敌人 +|VAL|% 技能伤害`, upgrade_WarframeCorrosiveDamageBoost: `对受腐蚀状态影响的敌人 +|VAL|% 技能伤害`,
upgrade_WarframeCorrosiveStack: `腐蚀状态最大堆叠数 +|VAL|`, upgrade_WarframeCorrosiveStack: `腐蚀状态最大堆叠数 +|VAL|`,