diff --git a/.vscode/settings.json b/.vscode/settings.json index 0856db23e..7637b9ec7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,36 +9,36 @@ "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit", - } + "source.fixAll.biome": "explicit" + } }, "[typescriptreact]": { "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit", - } + "source.fixAll.biome": "explicit" + } }, "[javascript]": { "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit", - } + "source.fixAll.biome": "explicit" + } }, "[json]": { "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit", - } + "source.fixAll.biome": "explicit" + } }, "[jsonc]": { "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "source.fixAll.biome": "explicit", - } + "source.fixAll.biome": "explicit" + } }, "files.associations": { "Justfile-*": "makefile" diff --git a/Justfile-test b/Justfile-test index 0fd394b08..55afb5a05 100644 --- a/Justfile-test +++ b/Justfile-test @@ -19,6 +19,7 @@ fix: fmt just gen bun run --filter '*' lint-fix + .scripts/ts_autoinherit.js # Automatically fix any issues we can in the codebase and then # make a commit for them diff --git a/bun.lock b/bun.lock index 927626cb3..37ebfe1d3 100644 --- a/bun.lock +++ b/bun.lock @@ -95,6 +95,7 @@ "mei-logger": "catalog:", "monk": "catalog:", "node-fetch": "catalog:", + "pino": "^10.3.1", "prudence": "catalog:", "seq-logging": "catalog:", "tachi-common": "workspace:*", @@ -400,6 +401,7 @@ "node-html-parser": "catalog:", "nodemailer": "catalog:", "pg": "catalog:", + "pino": "^10.3.1", "prudence": "catalog:", "rate-limit-redis": "catalog:", "redis": "catalog:", @@ -1200,6 +1202,8 @@ "@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.3.1", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw=="], + "@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="], @@ -1636,6 +1640,8 @@ "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], + "auto-bind": ["auto-bind@4.0.0", "", {}, "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ=="], "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], @@ -2782,6 +2788,8 @@ "oblivious-set": ["oblivious-set@1.0.0", "", {}, "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="], + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], "on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="], @@ -2874,6 +2882,12 @@ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="], + + "pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="], + + "pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="], + "pirates": ["pirates@3.0.2", "", { "dependencies": { "node-modules-regexp": "^1.0.0" } }, "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q=="], "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], @@ -2908,6 +2922,8 @@ "process-on-spawn": ["process-on-spawn@1.1.0", "", { "dependencies": { "fromentries": "^1.2.0" } }, "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q=="], + "process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="], + "prom-client": ["prom-client@15.1.3", "", { "dependencies": { "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" } }, "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g=="], "promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="], @@ -2940,6 +2956,8 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], + "ramda": ["ramda@0.32.0", "", {}, "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ=="], "random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="], @@ -2994,6 +3012,8 @@ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], + "rechoir": ["rechoir@0.8.0", "", { "dependencies": { "resolve": "^1.20.0" } }, "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ=="], "redeyed": ["redeyed@2.1.1", "", { "dependencies": { "esprima": "~4.0.0" } }, "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ=="], @@ -3122,6 +3142,8 @@ "snappy": ["snappy@7.3.3", "", { "optionalDependencies": { "@napi-rs/snappy-android-arm-eabi": "7.3.3", "@napi-rs/snappy-android-arm64": "7.3.3", "@napi-rs/snappy-darwin-arm64": "7.3.3", "@napi-rs/snappy-darwin-x64": "7.3.3", "@napi-rs/snappy-freebsd-x64": "7.3.3", "@napi-rs/snappy-linux-arm-gnueabihf": "7.3.3", "@napi-rs/snappy-linux-arm64-gnu": "7.3.3", "@napi-rs/snappy-linux-arm64-musl": "7.3.3", "@napi-rs/snappy-linux-ppc64-gnu": "7.3.3", "@napi-rs/snappy-linux-riscv64-gnu": "7.3.3", "@napi-rs/snappy-linux-s390x-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-musl": "7.3.3", "@napi-rs/snappy-openharmony-arm64": "7.3.3", "@napi-rs/snappy-wasm32-wasi": "7.3.3", "@napi-rs/snappy-win32-arm64-msvc": "7.3.3", "@napi-rs/snappy-win32-ia32-msvc": "7.3.3", "@napi-rs/snappy-win32-x64-msvc": "7.3.3" } }, "sha512-UDJVCunvgblRpfTOjo/uT7pQzfrTsSICJ4yVS4aq7SsGBaUSpJwaVP15nF//jqinSLpN7boe/BqbUmtWMTQ5MQ=="], + "sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="], + "source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -3258,6 +3280,8 @@ "text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="], + "thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="], + "tildify": ["tildify@2.0.0", "", {}, "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw=="], "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], diff --git a/dev/setup.fish b/dev/setup.fish index 40a80d841..99c3e9a67 100755 --- a/dev/setup.fish +++ b/dev/setup.fish @@ -25,6 +25,6 @@ funcsave _tide_item_tachi > /dev/null set -U tide_tachi_color cc527a set -U tide_tachi_bg_color 131313 -set -U tide_right_prompt_items status cmd_duration context jobs direnv bun node python rustc java php pulumi ruby go gcloud kubectl distrobox toolbox terraform aws nix_shell crystal elixir zig time tachi +set -U tide_right_prompt_items status cmd_duration context jobs direnv bun node python rustc java php pulumi ruby go gcloud kubectl distrobox toolbox terraform aws nix_shell crystal elixir zig tachi time # rr \ No newline at end of file diff --git a/package.json b/package.json index bc480c0f3..5fd8d62d9 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,10 @@ }, "catalog": { "@typescript/native-preview": "7.0.0-dev.20260309.1", - "@types/node": "18.11.18", + "@types/node": "*", "@types/pg": "^8.11.11", "ts-node": "^8.5.2", - "typescript": "^3.7.2", + "typescript": "^4.3.4", "kysely": "^0.27.5", "pg": "^8.13.3", "@types/history": "4.7.11", @@ -203,8 +203,8 @@ "which": "^2.0.2", "@types/eslint__js": "8.42.3", "@eslint/js": "^9.20.0", - "@typescript-eslint/eslint-plugin": "^8.23.0", - "@typescript-eslint/parser": "^8.23.0", + "@typescript-eslint/eslint-plugin": "^4.27.0", + "@typescript-eslint/parser": "^4.27.0", "@typescript-eslint/utils": "^8.24.1", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.7.0", @@ -218,7 +218,16 @@ "typescript-eslint": "^8.24.1", "vitest": "~3.2.0", "@vitest/coverage-v8": "~3.2.0", - "@vitest/coverage-istanbul": "~3.2.0" + "@vitest/coverage-istanbul": "~3.2.0", + "pino": "^10.3.1", + "@types/jest": "^26.0.23", + "eslint": "^7.29.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-jest": "^24.3.6", + "jest": "^27.0.4", + "standard": "^16.0.3", + "ts-jest": "^27.0.3", + "tsd": "^0.17.0" }, "overrides": { "@types/mongodb": "3.6.20", diff --git a/typescript/bot/eslint.config.mjs b/typescript/bot/eslint.config.mjs index eeb2f6c18..8d952ed78 100644 --- a/typescript/bot/eslint.config.mjs +++ b/typescript/bot/eslint.config.mjs @@ -8,8 +8,5 @@ export default [ }, { files: ["**/*.{ts,tsx}"], - rules: { - "@typescript-eslint/no-unsafe-assignment": "warn", - }, }, ]; diff --git a/typescript/bot/package.json b/typescript/bot/package.json index 0354c7bcf..14fc3705f 100644 --- a/typescript/bot/package.json +++ b/typescript/bot/package.json @@ -38,9 +38,10 @@ "json5": "catalog:", "lodash": "catalog:", "luxon": "catalog:", - "mei-logger": "catalog:", + "mei-log": "catalog:", "monk": "catalog:", "node-fetch": "catalog:", + "pino": "catalog:", "prudence": "catalog:", "seq-logging": "catalog:", "tachi-common": "workspace:*", diff --git a/typescript/bot/src/config.ts b/typescript/bot/src/config.ts index d2d4335f8..4120c4ba9 100644 --- a/typescript/bot/src/config.ts +++ b/typescript/bot/src/config.ts @@ -14,9 +14,9 @@ config(); // Reads the bots config file from $pwd/conf.json5. // Validates it using prudence. -// the real logger tries to bind to discord, and is dependent on the options +// the real log tries to bind to discord, and is dependent on the options // below. -const logger = console; +const log = console; export interface BotConfig { TACHI_SERVER_LOCATION: string; @@ -43,10 +43,10 @@ function ParseBotConfig(fileLoc = "conf.json5"): BotConfig { try { const contents = fs.readFileSync(fileLoc, "utf-8"); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + data = JSON5.parse(contents); } catch (err) { - logger.error("Failed to find/parse a valid conf.json5 file. Cannot boot.", { err }); + log.error({ err }, "Failed to find/parse a valid conf.json5 file. Cannot boot."); throw err; } @@ -93,7 +93,7 @@ function ParseBotConfig(fileLoc = "conf.json5"): BotConfig { }); if (err) { - logger.error(FormatPrError(err, "Invalid conf.json5 file. Cannot safely boot.")); + log.error(FormatPrError(err, "Invalid conf.json5 file. Cannot safely boot.")); throw err; } @@ -105,8 +105,6 @@ export interface ProcessEnvironment { nodeEnv: "dev" | "production" | "staging" | "test"; mongoUrl: string; port: integer; - seqUrl?: string; - seqApiKey?: string; } function ParseEnvVars() { @@ -123,15 +121,13 @@ function ParseEnvVars() { PORT: (self) => p.isPositiveInteger(Number(self)) === true || "Should be a string representing a whole integer port.", - SEQ_URL: "*string", - SEQ_API_KEY: "*string", }, {}, { allowExcessKeys: true }, ); if (err) { - logger.error(FormatPrError(err, "Invalid environment. Cannot safely boot.")); + log.error(FormatPrError(err, "Invalid environment. Cannot safely boot.")); throw err; } @@ -140,8 +136,6 @@ function ParseEnvVars() { nodeEnv: process.env.NODE_ENV, mongoUrl: process.env.MONGO_URL, port: Number(process.env.PORT), - seqUrl: process.env.SEQ_URL, - seqApiKey: process.env.SEQ_API_KEY, } as ProcessEnvironment; } @@ -154,13 +148,13 @@ async function GetServerConfig() { // this fetch is complete, and it saves us having to do a singleton pattern or worse. // This *should* be solved with top-level-await, but good luck actually getting // typescript to output the right stuff here. - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const res = await fetch(`${BotConfig.TACHI_SERVER_LOCATION}/api/v1/config`).then((res) => res.json(), ); if (!res.success) { - logger.error( + log.error( `Failed to fetch server info from ${BotConfig.TACHI_SERVER_LOCATION}. Can't run.`, ); process.exit(1); @@ -177,7 +171,7 @@ export const ProcessEnv = ParseEnvVars(); // This warns people if their parent server supports games that they aren't acknowledging. for (const game of ServerConfig.GAMES) { if (!Object.prototype.hasOwnProperty.call(BotConfig.DISCORD.GAME_CHANNELS, game)) { - logger.warn( + log.warn( `${ServerConfig.NAME} declares support for ${game}, but no channel is mapped to it in your conf.json5.`, ); } diff --git a/typescript/bot/src/data/data.ts b/typescript/bot/src/data/data.ts deleted file mode 100644 index 213627c5e..000000000 --- a/typescript/bot/src/data/data.ts +++ /dev/null @@ -1,17 +0,0 @@ -export enum LoggerLayers { - apiRequests = "apiRequests", - botConfigSetup = "botConfigSetup", - buildChartEmbed = "buildChartEmbed", - buildProfileEmbed = "buildProfileEmbed", - chartSearch = "chartSearch", - client = "client", - database = "database", - databaseQuery = "databaseQuery", - profile = "profile", - selectInteractionHandler = "selectInteractionHandler", - server = "server", - serverAuth = "serverAuth", - slashCommands = "slashCommands", - tachiAuth = "tachiAuth", - tachiFetch = "tachiFetch", -} diff --git a/typescript/bot/src/database/mongo.ts b/typescript/bot/src/database/mongo.ts index 68ea52253..f6b20fc48 100644 --- a/typescript/bot/src/database/mongo.ts +++ b/typescript/bot/src/database/mongo.ts @@ -1,23 +1,20 @@ +import { log } from "#utils/log"; import monk from "monk"; import type { DiscordUserMapDocument } from "./documents"; import { ProcessEnv } from "../config"; -import { LoggerLayers } from "../data/data"; -import { CreateLayeredLogger } from "../utils/logger"; -const logger = CreateLayeredLogger(LoggerLayers.database); - -logger.info(`Connecting to ${ProcessEnv.mongoUrl}...`); +log.info(`Connecting to ${ProcessEnv.mongoUrl}...`); const monkDB = monk(ProcessEnv.mongoUrl); monkDB .then(() => { - logger.info(`Database connection successful.`); + log.info(`Database connection successful.`); }) .catch((err) => { - logger.crit(err); + log.fatal(err); process.exit(1); }); @@ -26,16 +23,16 @@ const db = { }; export async function SetIndexes(hardReset = false) { - logger.info(`Recieved request to set indexes.`); + log.info(`Recieved request to set indexes.`); if (hardReset) { - logger.warn(`Hard resetting indexes!`); + log.warn(`Hard resetting indexes!`); await db.discordUserMap.dropIndexes(); } await db.discordUserMap.createIndex({ discordID: 1 }, { unique: true }); - logger.info(`Indexes have been set.`); + log.info(`Indexes have been set.`); } export default db; diff --git a/typescript/bot/src/database/queries.ts b/typescript/bot/src/database/queries.ts index 02b35d330..f092cfcab 100644 --- a/typescript/bot/src/database/queries.ts +++ b/typescript/bot/src/database/queries.ts @@ -1,23 +1,22 @@ import type { integer } from "tachi-common"; +import { log } from "#utils/log.js"; + import type { DiscordUserMapDocument } from "./documents"; -import { LoggerLayers } from "../data/data"; -import { CreateLayeredLogger } from "../utils/logger"; import db from "./mongo"; -const logger = CreateLayeredLogger(LoggerLayers.databaseQuery); export function GetUserAndTokenForDiscordID( discordID: string, ): Promise { - logger.verbose(`Fetching linked user & token with DiscordID: ${discordID}.`); + log.debug(`Fetching linked user & token with DiscordID: ${discordID}.`); return db.discordUserMap.findOne({ discordID }); } export async function GetUserIDForDiscordID(discordID: string): Promise { - logger.verbose(`Fetching linked user & token with DiscordID: ${discordID}.`); + log.debug(`Fetching linked user & token with DiscordID: ${discordID}.`); const user = await db.discordUserMap.findOne({ discordID }, { projection: { userID: 1 } }); diff --git a/typescript/bot/src/interactionHandlers/handleIsCommand.ts b/typescript/bot/src/interactionHandlers/handleIsCommand.ts index 3ca7e86be..fd544d7fb 100644 --- a/typescript/bot/src/interactionHandlers/handleIsCommand.ts +++ b/typescript/bot/src/interactionHandlers/handleIsCommand.ts @@ -1,12 +1,9 @@ +import { log } from "#utils/log"; import { type CommandInteraction, MessageEmbed } from "discord.js"; import type { DiscordUserMapDocument } from "../database/documents"; -import { LoggerLayers } from "../data/data"; import { SLASH_COMMANDS } from "../slashCommands/commands"; -import { CreateLayeredLogger } from "../utils/logger"; - -const logger = CreateLayeredLogger(LoggerLayers.slashCommands); /** * Handles incoming command requests by resolving the interaction to the command @@ -29,7 +26,7 @@ export async function handleIsCommand( await interaction.deferReply(); - logger.verbose(`Running ${command.info.name} interaction.`); + log.debug(`Running ${command.info.name} interaction.`); try { const response = await command.exec(interaction, requestingUser); @@ -43,13 +40,13 @@ export async function handleIsCommand( }); } } catch (err) { - logger.error(`An error occured while executing a command.`, { command, err }); + log.error({ command, err }, `An error occured while executing a command.`); void interaction.editReply( `An error has occured while executing this command (${err}). This has been reported.`, ); } } catch (e) { - logger.error("Failed to handle isCommand interaction", { error: e }); + log.error({ error: e }, "Failed to handle isCommand interaction"); } } diff --git a/typescript/bot/src/main.ts b/typescript/bot/src/main.ts index eac2613d8..bf8e76e11 100644 --- a/typescript/bot/src/main.ts +++ b/typescript/bot/src/main.ts @@ -1,17 +1,16 @@ +import { log } from "#utils/log.js"; import { GetLimboChannel } from "#utils/misc"; import { Client, type CommandInteraction, Intents, type SelectMenuInteraction } from "discord.js"; import { BotConfig, ProcessEnv, ServerConfig } from "./config"; -import { LoggerLayers } from "./data/data"; import { GetUserAndTokenForDiscordID } from "./database/queries"; import { handleIsCommand } from "./interactionHandlers/handleIsCommand"; import { app } from "./server/server"; import { RegisterSlashCommands } from "./slashCommands/register"; -import { CreateLayeredLogger } from "./utils/logger"; import { VERSION_PRETTY } from "./version"; -// hack: DiscordJS's endpoints sometimes return bigints that end up in our logger. -// when our logger tries to format that content, JSON.stringify fails. +// hack: DiscordJS's endpoints sometimes return bigints that end up in our log. +// when our log tries to format that content, JSON.stringify fails. // // I personally cannot believe that the spec now made JSON.stringify fallible in such // a common case. It's kind of absurdly ridiculous. But hey ho; monkey patch our way @@ -21,8 +20,6 @@ BigInt.prototype.toJSON = function toJSON() { return this.toString(); }; -const logger = CreateLayeredLogger(LoggerLayers.client); - export const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.GUILD_MESSAGES], }); @@ -53,11 +50,11 @@ client.on("interactionCreate", async (interaction) => { if (interaction.isCommand()) { await handleIsCommand(interaction, requestingUser); } - } catch (e) { + } catch (err) { await interaction.channel?.send( "We failed to handle this request. Are your DMs shut to non-friends?", ); - logger.error("Failed to run interaction.", { interaction, e }); + log.error({ interaction, err }, "Failed to run interaction."); } }); @@ -79,25 +76,25 @@ We've sent you a DM with instructions on how to link your account.`, void (async () => { try { - logger.info(`Booting Tachi Bot ${VERSION_PRETTY}.`); + log.info(`Booting Tachi Bot ${VERSION_PRETTY}.`); // Login to discord. await client.login(BotConfig.DISCORD.TOKEN); - logger.info(`Logged in successfully to ${client.guilds.cache.size} guilds.`); + log.info(`Logged in successfully to ${client.guilds.cache.size} guilds.`); // Mount our express server. app.listen(ProcessEnv.port); - logger.info( + log.info( `Invite URL: https://discord.com/api/oauth2/authorize?client_id=${ client.application!.id }&permissions=8&scope=applications.commands%20bot`, ); await RegisterSlashCommands(client); - } catch (err) { - logger.crit("Failed to properly boot.", err); + } catch (error) { + log.fatal({ error }, "Failed to properly boot."); process.exit(1); } })(); @@ -105,7 +102,5 @@ void (async () => { // taken from https://nodejs.org/api/process.html#process_event_unhandledrejection // to avoid future deprecation. process.on("unhandledRejection", (reason, promise) => { - // @ts-expect-error reason is an error, and the logger can handle errors - // it just refuses. - logger.error(reason, { promise }); + log.error({ promise }, reason as string); }); diff --git a/typescript/bot/src/server/middleware.ts b/typescript/bot/src/server/middleware.ts index 3e86c19ab..e52a38a20 100644 --- a/typescript/bot/src/server/middleware.ts +++ b/typescript/bot/src/server/middleware.ts @@ -1,10 +1,9 @@ import type { RequestHandler } from "express"; +import { log } from "#utils/log"; + import { BotConfig } from "../config"; -import { LoggerLayers } from "../data/data"; -import { CreateLayeredLogger } from "../utils/logger"; -const logger = CreateLayeredLogger(LoggerLayers.serverAuth); /** * Middleware that checks that a webhook request has Authorization set to @@ -17,7 +16,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => { const auth = req.header("Authorization"); if (!auth) { - logger.info(`Received unauthed request from ${req.ip}.`); + log.info(`Received unauthed request from ${req.ip}.`); return res.status(401).json({ success: false, description: "No authorization provided.", @@ -27,7 +26,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => { const [type, value] = auth.split(" ", 2); if (type !== "Bearer") { - logger.info(`Received invalid auth type request from ${req.ip}, got auth type ${type}.`); + log.info(`Received invalid auth type request from ${req.ip}, got auth type ${type}.`); return res.status(400).json({ success: false, description: "Invalid authorization type. Expected Bearer.", @@ -35,7 +34,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => { } if (value !== BotConfig.OAUTH.CLIENT_SECRET) { - logger.warn( + log.warn( `Recieved invalid auth value from ${req.ip}. Has the client secret been changed?`, ); return res.status(403).json({ @@ -44,7 +43,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => { }); } - logger.debug("Webhook authorisation successful."); + log.debug("Webhook authorisation successful."); next(); }; diff --git a/typescript/bot/src/server/server.ts b/typescript/bot/src/server/server.ts index f0d650cc3..11aad45a8 100644 --- a/typescript/bot/src/server/server.ts +++ b/typescript/bot/src/server/server.ts @@ -1,15 +1,13 @@ import type { APITokenDocument, UserDocument, WebhookEvents } from "tachi-common"; -import { client } from "#main"; +import { log } from "#utils/log.js"; import { HandleQuestAchievedV1 } from "#webhookHandlers/questAchieved"; import express, { type Express } from "express"; import path from "path"; import { BotConfig, ProcessEnv } from "../config"; -import { LoggerLayers } from "../data/data"; import db from "../database/mongo"; import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "../utils/fetchTachi"; -import { CreateLayeredLogger } from "../utils/logger"; import { VERSION_PRETTY } from "../version"; import { HandleClassUpdateV1 } from "../webhookHandlers/classUpdate"; import { HandleGoalAchievedV1 } from "../webhookHandlers/goalsAchieved"; @@ -17,7 +15,6 @@ import { ValidateWebhookRequest } from "./middleware"; export const app: Express = express(); -const logger = CreateLayeredLogger(LoggerLayers.server); app.use(express.json()); @@ -33,7 +30,7 @@ app.set("query parser", "simple"); * * @name GET / */ -app.get("/", (req, res) => +app.get("/", (_req, res) => res.status(200).json({ success: true, description: "Bot is online!", @@ -76,7 +73,7 @@ app.get("/oauth/callback", async (req, res) => { ); if (!tokenRes.success) { - logger.error( + log.error( `Failed to convert code ${req.query.code} to a token. ${tokenRes.description} Cannot auth.`, ); return res.status(401).json({ @@ -91,7 +88,8 @@ app.get("/oauth/callback", async (req, res) => { const whoamiRes = await TachiServerV1Get("/users/me", apiToken); if (!whoamiRes.success) { - logger.severe("Failed to request user with token we just got?", { discordID }); + log.error({ discordID } + , "Failed to request user with token we just got?"); return res .status(500) .send( @@ -101,12 +99,12 @@ app.get("/oauth/callback", async (req, res) => { const user = whoamiRes.body; - logger.info(`Saving user-discord-link for ${user.username} (id: ${user.id}).`); + log.info(`Saving user-discord-link for ${user.username} (id: ${user.id}).`); const existingLink = await db.discordUserMap.findOne({ userID: user.id }); if (existingLink) { - logger.info(`Updating user-discord-link for ${user.username} (id: ${user.id})`); + log.info(`Updating user-discord-link for ${user.username} (id: ${user.id})`); await db.discordUserMap.update( { @@ -163,7 +161,7 @@ app.post("/webhook", ValidateWebhookRequest, async (req, res) => { // to define new webhooks, and the bot might not // However, tachi-(server/common) may recieve an update // According to the types, this should never happen. - logger.warn( + log.warn( `Received unknown webhook event ${ (webhookEvent as WebhookEvents).type }. Have we got support for this?`, @@ -183,7 +181,7 @@ app.post("/webhook", ValidateWebhookRequest, async (req, res) => { * * @name ALL * */ -app.all("*", (req, res) => +app.all("*", (_req, res) => res.status(404).json({ success: false, description: "Nothing found here.", @@ -210,16 +208,14 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne const expErr: ExpressJSONErr = err as ExpressJSONErr; if (expErr.status === 400 && "body" in expErr) { - logger.info(`Error in parsing JSON in request body from ${req.url}`, { - url: req.originalUrl, - }); + log.info({ url: req.originalUrl, err: err }, `Error in parsing JSON in request body from ${req.url}`); return res.status(400).send({ success: false, description: err.message }); } // else, this isn't a JSON parsing error } - logger.error(err, req.route); + log.error({ err, route: req.route }, "Fatal error propagated to server root?"); return res.status(500).json({ success: false, @@ -229,4 +225,4 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne app.use(MainExpressErrorHandler); -logger.info(`Starting express server on port ${ProcessEnv.port}.`); +log.info(`Starting express server on port ${ProcessEnv.port}.`); diff --git a/typescript/bot/src/slashCommands/register.ts b/typescript/bot/src/slashCommands/register.ts index cf3aebfd0..cfd799985 100644 --- a/typescript/bot/src/slashCommands/register.ts +++ b/typescript/bot/src/slashCommands/register.ts @@ -1,15 +1,12 @@ import type { Client } from "discord.js"; +import { log } from "#utils/log"; import { REST } from "@discordjs/rest"; import { Routes } from "discord-api-types/v9"; import { BotConfig, ProcessEnv } from "../config"; -import { LoggerLayers } from "../data/data"; -import { CreateLayeredLogger } from "../utils/logger"; import { SLASH_COMMANDS } from "./commands"; -const logger = CreateLayeredLogger(LoggerLayers.slashCommands); - const rest = new REST({ version: "9", }).setToken(BotConfig.DISCORD.TOKEN); @@ -23,18 +20,18 @@ export async function RegisterSlashCommands(client: Client): Promise { const commandsArray = [...SLASH_COMMANDS.values()]; // always unregister guild slash commands, just in case. - logger.info(`Unregistering guild slash commands.`); + log.info(`Unregistering guild slash commands.`); await UnregisterAllCommands(client); if (ProcessEnv.nodeEnv === "production") { - logger.info(`Updating global commands.`); + log.info(`Updating global commands.`); await rest.put(Routes.applicationCommands(client.application!.id), { body: commandsArray.map((command) => command.info), }); } else { - logger.info("Registering guild slash commands."); + log.info("Registering guild slash commands."); await rest.put( Routes.applicationGuildCommands( @@ -47,9 +44,9 @@ export async function RegisterSlashCommands(client: Client): Promise { ); } - logger.info("Successfully registered guild slash commands."); + log.info("Successfully registered guild slash commands."); } catch (err) { - logger.error("Failed to register guild slash commands.", err); + log.error({ err }, "Failed to register guild slash commands."); throw err; } } @@ -59,7 +56,7 @@ export async function RegisterSlashCommands(client: Client): Promise { */ async function UnregisterAllCommands(client: Client): Promise { try { - logger.info("Tidying old guild slash commands."); + log.info("Tidying old guild slash commands."); const guilds = client.guilds.cache; // discord.js doesn't use arrays because those aren't cool anymore @@ -77,9 +74,9 @@ async function UnregisterAllCommands(client: Client): Promise { // parallelise waiting for these to be deleted. await Promise.all(promises); - logger.info(`Successfully tidied ${promises.length} old guild slash commands.`); + log.info(`Successfully tidied ${promises.length} old guild slash commands.`); } catch (err) { - logger.error("Failed to tidy old guild slash commands.", err); + log.error({ err }, "Failed to tidy old guild slash commands."); throw err; } diff --git a/typescript/bot/src/utils/apiRequests.ts b/typescript/bot/src/utils/apiRequests.ts index aa4fd109d..86b09dd43 100644 --- a/typescript/bot/src/utils/apiRequests.ts +++ b/typescript/bot/src/utils/apiRequests.ts @@ -13,16 +13,13 @@ import type { } from "tachi-common"; import { BotConfig } from "#config"; +import { log } from "#utils/log"; import type { ImportDeferred, ImportPollStatus, UGPTStats } from "./returnTypes"; -import { LoggerLayers } from "../data/data"; import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "./fetchTachi"; -import { CreateLayeredLogger } from "./logger"; import { Sleep } from "./misc"; -const logger = CreateLayeredLogger(LoggerLayers.apiRequests); - export async function GetUserInfo(userID: string | integer) { const res = await TachiServerV1Get(`/users/${userID}`, null); @@ -116,7 +113,7 @@ export async function PerformScoreImport( if (!initRes.success) { if (initRes.statusCode >= 500) { - logger.error(`Failed to perform score import on ${url}.`, { body }); + log.error({ body }, `Failed to perform score import on ${url}.`); throw new Error(`Failed to perform import on ${url}.`); } else { return initRes.description; @@ -172,7 +169,7 @@ Please go to ${BotConfig.TACHI_SERVER_LOCATION}/u/me/integrations/services to un } } - logger.error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`, { body }); + log.error({ body }, `Unexpected status code ${initRes.statusCode} returned from ${url}.`); throw new Error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`); } diff --git a/typescript/bot/src/utils/fetchTachi.ts b/typescript/bot/src/utils/fetchTachi.ts index 55fd4dd18..c6da2dd32 100644 --- a/typescript/bot/src/utils/fetchTachi.ts +++ b/typescript/bot/src/utils/fetchTachi.ts @@ -1,14 +1,11 @@ import type { integer, SuccessfulAPIResponse, UnsuccessfulAPIResponse } from "tachi-common"; +import { log } from "#utils/log"; import fetch from "node-fetch"; import { URLSearchParams } from "url"; import { BotConfig } from "../config"; -import { LoggerLayers } from "../data/data"; import { VERSION_STR } from "../version"; -import { CreateLayeredLogger } from "./logger"; - -const logger = CreateLayeredLogger(LoggerLayers.tachiFetch); export type APIResponse = (({ body: null } & UnsuccessfulAPIResponse) | SuccessfulAPIResponse) & { statusCode: integer; @@ -41,9 +38,9 @@ export async function TachiServerV1Request( body: unknown = {}, ): Promise> { const realUrl = PrependTachiUrl(url, "1"); - const loggerUrl = `${method} ${realUrl}`; + const logUrl = `${method} ${realUrl}`; - logger.debug(`Making a request to ${loggerUrl}.`); + log.debug(`Making a request to ${logUrl}.`); try { const res = await fetch(realUrl, { @@ -60,11 +57,11 @@ export async function TachiServerV1Request( const contents = { ...json, statusCode: res.status }; - LogRequestResult(loggerUrl, contents); + LogRequestResult(logUrl, contents); return contents; } catch (err) { - logger.error(`Failed while requesting ${method} ${realUrl}.`, { err }); + log.error({ err }, `Failed while requesting ${method} ${realUrl}.`); // Throw the error upwards for it to be caught be a higher handler. throw err; @@ -94,7 +91,7 @@ export async function TachiServerV1Get( const realUrl = `${PrependTachiUrl(url, "1")}?${urlParams.toString()}`; - logger.verbose(`GET ${realUrl}`); + log.debug(`GET ${realUrl}`); const res = await fetch(realUrl, { method: RequestTypes.GET, @@ -111,7 +108,7 @@ export async function TachiServerV1Get( return contents; } catch (err) { - logger.error(`Failed while requesting GET ${url}.\n\n${err}\n`); + log.error(`Failed while requesting GET ${url}.\n\n${err}\n`); throw err; } @@ -133,12 +130,12 @@ export function PrependTachiUrl(url: string, version: "1" = "1"): string { * Logs the result of a request. * Logs at WARN level if was unsuccessful, DEBUG otherwise. */ -function LogRequestResult(loggerUrl: string, res: APIResponse): void { +function LogRequestResult(logUrl: string, res: APIResponse): void { if (!res.success) { - logger.warn( - `Request ${loggerUrl} was unsuccessful: ${res.description} (${res.statusCode})`, + log.warn( + `Request ${logUrl} was unsuccessful: ${res.description} (${res.statusCode})`, ); } else { - logger.debug(`Request ${loggerUrl} was successful: ${res.description} (${res.statusCode})`); + log.debug(`Request ${logUrl} was successful: ${res.description} (${res.statusCode})`); } } diff --git a/typescript/bot/src/utils/log.ts b/typescript/bot/src/utils/log.ts new file mode 100644 index 000000000..8905ebe32 --- /dev/null +++ b/typescript/bot/src/utils/log.ts @@ -0,0 +1,17 @@ +import pino from "pino"; + +// Use JSON log lines when LOG_JSON=1 (dev/prod); otherwise pretty format for localdev. +// Deliberately process.env here instead of importing ENV to avoid a circular dep. +const useJson = process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true"; + +export const log = pino({ + level: process.env.LOG_LEVEL ?? "info", + ...(useJson + ? {} + : { + transport: { + options: { colorize: true }, + target: "pino-pretty", + }, + }), +}); diff --git a/typescript/bot/src/utils/logger.ts b/typescript/bot/src/utils/logger.ts deleted file mode 100644 index 4e33dfd3f..000000000 --- a/typescript/bot/src/utils/logger.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { SeqLogLevel } from "seq-logging"; - -import { ProcessEnv } from "#config"; -import { Transport as SeqTransport } from "@valuabletouch/winston-seq"; -import { CreateLogger, type MeiLogger } from "mei-logger"; -import { transports } from "winston"; - -import type { LoggerLayers } from "../data/data"; - -const tports: Array = [new transports.Console({})]; - -if (ProcessEnv.seqUrl) { - // Turns winston log levels into seq format. - const levelMap: Record = { - crit: "Fatal", - severe: "Error", - error: "Error", - warn: "Warning", - info: "Information", - - // Note that Seq interprets these in reverse, - // however, it's easier to read this code if I just - // use the same levels, instead of the right ones. - verbose: "Verbose", - debug: "Debug", - }; - - tports.push( - new SeqTransport({ - apiKey: ProcessEnv.seqApiKey, - serverUrl: ProcessEnv.seqUrl, - onError: (err) => { - console.error(`Failed to send seq message: ${err.message}.`); - }, - levelMapper(level = "") { - return levelMap[level] ?? "Information"; - }, - }), - ); -} - -const logger = CreateLogger(`tachi-bot`, undefined, tports); - -export default logger; - -export function CreateLayeredLogger(layerName: LoggerLayers) { - const lg = logger.child({ - context: [layerName], - }); - - lg.defaultMeta = { ...(lg.defaultMeta ?? {}), context: [layerName] }; - - return lg as MeiLogger; -} diff --git a/typescript/bot/src/webhookHandlers/classUpdate.ts b/typescript/bot/src/webhookHandlers/classUpdate.ts index 2d585321f..7cb90a316 100644 --- a/typescript/bot/src/webhookHandlers/classUpdate.ts +++ b/typescript/bot/src/webhookHandlers/classUpdate.ts @@ -14,7 +14,7 @@ import { client } from "../main"; import { GetUGPTStats, GetUserInfo } from "../utils/apiRequests"; import { CreateEmbed } from "../utils/embeds"; import { PrependTachiUrl } from "../utils/fetchTachi"; -import logger from "../utils/logger"; +import { log } from "#utils/log" import { FormatClass, GetGameChannel } from "../utils/misc"; export async function HandleClassUpdateV1( @@ -29,12 +29,12 @@ export async function HandleClassUpdateV1( } catch (e) { const err = e as Error; - logger.error(`ClassUpdate handler failed: ${err.message}`); + log.error(`ClassUpdate handler failed: ${err.message}`); return 500; } if (!ShouldRenderUpdate(game, playtype, event.set, event.new)) { - logger.info( + log.info( `Not rendering class update ${event.set}: ${event.old} -> ${event.new} (not relevant).`, ); return 204; @@ -49,7 +49,7 @@ export async function HandleClassUpdateV1( // Do not render if the user hasn't hit the score cap. if (totalScores < minimumNecessaryScores) { - logger.info( + log.info( `Not rendering class update ${event.set}: ${event.old} -> ${event.new} (not enough scores).`, ); return 204; @@ -91,7 +91,7 @@ function ShouldRenderUpdate( const classSpec = config.classes[classSet]; if (classSpec === undefined) { - logger.error(`Invalid class ${classSet} for ${game} ${playtype}`); + log.error(`Invalid class ${classSet} for ${game} ${playtype}`); return false; } @@ -105,12 +105,12 @@ function ShouldRenderUpdate( const minimumId = ids.indexOf(classSpec.minimumRelevantValue); if (currentId < 0) { - logger.error(`Invalid classValue ${classValue} for ${game} ${playtype}`); + log.error(`Invalid classValue ${classValue} for ${game} ${playtype}`); return false; } if (minimumId < 0) { - logger.error(`Invalid minimum classValue ${classValue} for ${game} ${playtype}`); + log.error(`Invalid minimum classValue ${classValue} for ${game} ${playtype}`); return false; } @@ -126,7 +126,7 @@ function GetMinimumScores( const classSpec = config.classes[classSet]; if (classSpec === undefined) { - logger.error(`Invalid class ${classSet} for ${game} ${playtype}`); + log.error(`Invalid class ${classSet} for ${game} ${playtype}`); return null; } diff --git a/typescript/bot/src/webhookHandlers/goalsAchieved.ts b/typescript/bot/src/webhookHandlers/goalsAchieved.ts index b50d44702..1eae8132c 100644 --- a/typescript/bot/src/webhookHandlers/goalsAchieved.ts +++ b/typescript/bot/src/webhookHandlers/goalsAchieved.ts @@ -1,5 +1,6 @@ import { BotConfig } from "#config"; import { PrependTachiUrl } from "#utils/fetchTachi"; +import { log } from "#utils/log" import { GetGameGroupConfig, type GoalDocument, @@ -10,7 +11,6 @@ import { import { client } from "../main"; import { GetGoalWithID, GetUserInfo } from "../utils/apiRequests"; import { CreateEmbed } from "../utils/embeds"; -import logger from "../utils/logger"; import { GetGameChannel, Pluralise } from "../utils/misc"; export async function HandleGoalAchievedV1( @@ -25,7 +25,7 @@ export async function HandleGoalAchievedV1( } catch (e) { const err = e as Error; - logger.error(`ClassUpdate handler failed: ${err.message}`); + log.error(`ClassUpdate handler failed: ${err.message}`); return 500; } diff --git a/typescript/bot/src/webhookHandlers/questAchieved.ts b/typescript/bot/src/webhookHandlers/questAchieved.ts index 2cc74d555..aa339dec6 100644 --- a/typescript/bot/src/webhookHandlers/questAchieved.ts +++ b/typescript/bot/src/webhookHandlers/questAchieved.ts @@ -1,11 +1,11 @@ import { BotConfig } from "#config"; import { PrependTachiUrl } from "#utils/fetchTachi"; +import { log } from "#utils/log"; import { GetGameGroupConfig, type integer, type WebhookEventQuestAchievedV1 } from "tachi-common"; import { client } from "../main"; import { GetQuestWithID, GetUserInfo } from "../utils/apiRequests"; import { CreateEmbed } from "../utils/embeds"; -import logger from "../utils/logger"; import { GetGameChannel } from "../utils/misc"; export async function HandleQuestAchievedV1( @@ -20,7 +20,7 @@ export async function HandleQuestAchievedV1( } catch (e) { const err = e as Error; - logger.error(`ClassUpdate handler failed: ${err.message}`); + log.error({ err }, "ClassUpdate handler failed."); return 500; } diff --git a/typescript/scripts/tsconfig.json b/typescript/scripts/tsconfig.json new file mode 100644 index 000000000..42a8526b6 --- /dev/null +++ b/typescript/scripts/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "Preserve", + "moduleResolution": "bundler", + "noImplicitAny": false, + "strictNullChecks": true, + "allowJs": true, + "strict": false + }, + "exclude": ["../seeds-scripts/node_modules"] +} diff --git a/typescript/server/package.json b/typescript/server/package.json index 8bbd41e33..2abda64cd 100644 --- a/typescript/server/package.json +++ b/typescript/server/package.json @@ -95,6 +95,7 @@ "node-html-parser": "catalog:", "nodemailer": "catalog:", "pg": "catalog:", + "pino": "catalog:", "prudence": "catalog:", "rate-limit-redis": "catalog:", "redis": "catalog:", @@ -107,10 +108,10 @@ "ts-node": "catalog:", "typescript": "catalog:", "uuidv7": "catalog:", - "zod": "catalog:", "winston": "catalog:", "winston-loki": "catalog:", - "winston-transport": "catalog:" + "winston-transport": "catalog:", + "zod": "catalog:" }, "nyc": { "reporter": [ diff --git a/typescript/server/src/lib/handlers/sigterm.ts b/typescript/server/src/lib/handlers/sigterm.ts index b605ae985..146571172 100644 --- a/typescript/server/src/lib/handlers/sigterm.ts +++ b/typescript/server/src/lib/handlers/sigterm.ts @@ -23,7 +23,7 @@ async function CloseEverythingElse() { logger.info("Closing Mongo Database.", { shutdownInfo: true }); await monkDB.close(); - logger.info("Closing database...", {shutdownInfo: true}); + logger.info("Closing database...", { shutdownInfo: true }); await ClosePgConnection(); logger.info("Closing Redis Connection.", { shutdownInfo: true }); diff --git a/typescript/server/src/lib/logger/logger.ts b/typescript/server/src/lib/logger/logger.ts index 9f05a32e5..d42058a93 100644 --- a/typescript/server/src/lib/logger/logger.ts +++ b/typescript/server/src/lib/logger/logger.ts @@ -120,7 +120,6 @@ const tports: Array = [ }), ]; - export const rootLogger = winston.createLogger({ levels: { // entire process termination is necessary diff --git a/typescript/server/src/lib/setup/config.ts b/typescript/server/src/lib/setup/config.ts index 95df1848e..39d15e1b0 100644 --- a/typescript/server/src/lib/setup/config.ts +++ b/typescript/server/src/lib/setup/config.ts @@ -1,5 +1,5 @@ import JSON5 from "json5"; -import { allSupportedGameGroups, type GameGroup , type ImportTypes } from "tachi-common"; +import { allSupportedGameGroups, type GameGroup, type ImportTypes } from "tachi-common"; import { allImportTypes } from "tachi-common/constants/import-types"; import { z } from "zod"; @@ -198,7 +198,6 @@ if (!["crit", "debug", "error", "info", "severe", "verbose", "warn"].includes(lo process.exit(1); } - const POSTGRES_URL = process.env.POSTGRES_URL ?? ""; if (!POSTGRES_URL) { diff --git a/typescript/server/src/server/server.ts b/typescript/server/src/server/server.ts index 4aa682086..d6b867db7 100644 --- a/typescript/server/src/server/server.ts +++ b/typescript/server/src/server/server.ts @@ -51,10 +51,7 @@ const userSessionMiddleware = expressSession({ ServerConfig.ENABLE_SERVER_HTTPS, // Very important. Without this, we're vulnerable to CSRF! - sameSite: - Env.NODE_ENV === "production" || Env.NODE_ENV === "staging" - ? "strict" - : "none", + sameSite: Env.NODE_ENV === "production" || Env.NODE_ENV === "staging" ? "strict" : "none", }, });