diff --git a/.gitignore b/.gitignore index 6ad5f79..3d65a9f 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,8 @@ dist .pnp.* # galvanic corrosion test builds -build/ \ No newline at end of file +build/ + +# galvanic corrosion local config +config.json +rooms.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a33692 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Galvanic Corrosion +delectable acids yum yum + +Rec Room server for communities. Fast runtime and easy setup. +Built for Rec Room version September 7th, 2018 (manifest 7763898423339170417) + +drawing \ No newline at end of file diff --git a/deno.json b/deno.json index 42d5491..ef3c2ab 100644 --- a/deno.json +++ b/deno.json @@ -1,12 +1,18 @@ { "tasks": { - "compile-win": "deno compile --target x86_64-pc-windows-msvc -o build/GalvanicCorrosion.exe --allow-env --allow-sys --allow-net --allow-read=. src/main.ts", - "compile-linux": "deno compile --target x86_64-unknown-linux-gnu -o build/GalvanicCorrosion --allow-env --allow-sys --allow-net --allow-read=. src/main.ts", - "cross-compile": "deno run compile-win && deno run compile-linux" + "compile-win": "deno compile --target x86_64-pc-windows-msvc -o build/GalvanicCorrosion.exe --allow-env --allow-sys --allow-net --allow-write --allow-read src/main.ts", + "compile-linux": "deno compile --target x86_64-unknown-linux-gnu -o build/GalvanicCorrosion --allow-env --allow-sys --allow-net --allow-write --allow-read src/main.ts", + "cross-compile": "deno run compile-win && deno run compile-linux", + "start": "deno run --allow-env --allow-sys --allow-net --allow-write --allow-read src/main.ts" }, "imports": { + "@oak/oak": "jsr:@oak/oak@^17.1.3", + "@proxnet/undead-logging": "jsr:@proxnet/undead-logging@^1.2.0", "@std/assert": "jsr:@std/assert@1", - "express": "npm:express@^4.21.1", - "log-like-a-zombie": "npm:log-like-a-zombie@1.4.0" + "@types/express": "npm:@types/express@^5.0.0", + "discord.js": "npm:discord.js@^14.16.3", + "ioredis": "npm:ioredis@^5.4.1", + "validator": "npm:validator@^13.12.0", + "why-is-node-running": "npm:why-is-node-running@^3.2.1" } } diff --git a/deno.lock b/deno.lock index 6be443f..cce1ab5 100644 --- a/deno.lock +++ b/deno.lock @@ -1,398 +1,366 @@ { "version": "4", "specifiers": { + "jsr:@oak/commons@1": "1.0.0", + "jsr:@oak/oak@^17.1.3": "17.1.3", + "jsr:@proxnet/undead-logging@^1.2.0": "1.2.0", "jsr:@std/assert@1": "1.0.7", + "jsr:@std/bytes@1": "1.0.4", + "jsr:@std/bytes@^1.0.2": "1.0.4", + "jsr:@std/crypto@1": "1.0.3", + "jsr:@std/encoding@1": "1.0.5", + "jsr:@std/encoding@^1.0.5": "1.0.5", + "jsr:@std/http@1": "1.0.10", "jsr:@std/internal@^1.0.5": "1.0.5", - "npm:express@^4.21.1": "4.21.1", - "npm:log-like-a-zombie@1.4.0": "1.4.0" + "jsr:@std/io@0.224": "0.224.9", + "jsr:@std/media-types@1": "1.1.0", + "jsr:@std/path@1": "1.0.8", + "npm:@types/express@5": "5.0.0", + "npm:@types/node@*": "22.5.4", + "npm:chalk@^5.3.0": "5.3.0", + "npm:discord.js@^14.16.3": "14.16.3", + "npm:ioredis@^5.4.1": "5.4.1", + "npm:path-to-regexp@6.2.1": "6.2.1", + "npm:validator@^13.12.0": "13.12.0", + "npm:why-is-node-running@^3.2.1": "3.2.1" }, "jsr": { + "@oak/commons@1.0.0": { + "integrity": "49805b55603c3627a9d6235c0655aa2b6222d3036b3a13ff0380c16368f607ac", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/bytes@1", + "jsr:@std/crypto", + "jsr:@std/encoding@1", + "jsr:@std/http", + "jsr:@std/media-types" + ] + }, + "@oak/oak@17.1.3": { + "integrity": "d89296c22db91681dd3a2a1e1fd14e258d0d5a9654de55637aee5b661c159f33", + "dependencies": [ + "jsr:@oak/commons", + "jsr:@std/assert", + "jsr:@std/bytes@1", + "jsr:@std/crypto", + "jsr:@std/http", + "jsr:@std/io", + "jsr:@std/media-types", + "jsr:@std/path", + "npm:path-to-regexp" + ] + }, + "@proxnet/undead-logging@1.2.0": { + "integrity": "59a4db428b5b848b7f51189b173b100ddabf7d86bb9de1a095e5d97b4a867e2c", + "dependencies": [ + "npm:chalk" + ] + }, "@std/assert@1.0.7": { "integrity": "64ce9fac879e0b9f3042a89b3c3f8ccfc9c984391af19e2087513a79d73e28c3", "dependencies": [ "jsr:@std/internal" ] }, + "@std/bytes@1.0.4": { + "integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc" + }, + "@std/crypto@1.0.3": { + "integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f" + }, + "@std/encoding@1.0.5": { + "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" + }, + "@std/http@1.0.10": { + "integrity": "4e32d11493ab04e3ef09f104f0cb9beb4228b1d4b47c5469573c2c294c0d3692", + "dependencies": [ + "jsr:@std/encoding@^1.0.5" + ] + }, "@std/internal@1.0.5": { "integrity": "54a546004f769c1ac9e025abd15a76b6671ddc9687e2313b67376125650dc7ba" + }, + "@std/io@0.224.9": { + "integrity": "4414664b6926f665102e73c969cfda06d2c4c59bd5d0c603fd4f1b1c840d6ee3", + "dependencies": [ + "jsr:@std/bytes@^1.0.2" + ] + }, + "@std/media-types@1.1.0": { + "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4" + }, + "@std/path@1.0.8": { + "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" } }, "npm": { - "accepts@1.3.8": { - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "@discordjs/builders@1.9.0": { + "integrity": "sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==", "dependencies": [ - "mime-types", - "negotiator" + "@discordjs/formatters", + "@discordjs/util", + "@sapphire/shapeshift", + "discord-api-types@0.37.97", + "fast-deep-equal", + "ts-mixer", + "tslib" ] }, - "array-flatten@1.1.1": { - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "@discordjs/collection@1.5.3": { + "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==" }, - "body-parser@1.20.3": { - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "@discordjs/collection@2.1.1": { + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==" + }, + "@discordjs/formatters@0.5.0": { + "integrity": "sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==", "dependencies": [ - "bytes", - "content-type", - "debug", - "depd", - "destroy", - "http-errors", - "iconv-lite", - "on-finished", - "qs", - "raw-body", - "type-is", - "unpipe" + "discord-api-types@0.37.97" ] }, - "bytes@3.1.2": { - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind@1.0.7": { - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "@discordjs/rest@2.4.0": { + "integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==", "dependencies": [ - "es-define-property", - "es-errors", - "function-bind", - "get-intrinsic", - "set-function-length" + "@discordjs/collection@2.1.1", + "@discordjs/util", + "@sapphire/async-queue", + "@sapphire/snowflake", + "@vladfrangu/async_event_emitter", + "discord-api-types@0.37.97", + "magic-bytes.js", + "tslib", + "undici" ] }, + "@discordjs/util@1.1.1": { + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==" + }, + "@discordjs/ws@1.1.1": { + "integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==", + "dependencies": [ + "@discordjs/collection@2.1.1", + "@discordjs/rest", + "@discordjs/util", + "@sapphire/async-queue", + "@types/ws", + "@vladfrangu/async_event_emitter", + "discord-api-types@0.37.83", + "tslib", + "ws" + ] + }, + "@ioredis/commands@1.2.0": { + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, + "@sapphire/async-queue@1.5.5": { + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==" + }, + "@sapphire/shapeshift@4.0.0": { + "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==", + "dependencies": [ + "fast-deep-equal", + "lodash" + ] + }, + "@sapphire/snowflake@3.5.3": { + "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==" + }, + "@types/body-parser@1.19.5": { + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": [ + "@types/connect", + "@types/node" + ] + }, + "@types/connect@3.4.38": { + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": [ + "@types/node" + ] + }, + "@types/express-serve-static-core@5.0.1": { + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dependencies": [ + "@types/node", + "@types/qs", + "@types/range-parser", + "@types/send" + ] + }, + "@types/express@5.0.0": { + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "dependencies": [ + "@types/body-parser", + "@types/express-serve-static-core", + "@types/qs", + "@types/serve-static" + ] + }, + "@types/http-errors@2.0.4": { + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "@types/mime@1.3.5": { + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "@types/node@22.5.4": { + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dependencies": [ + "undici-types" + ] + }, + "@types/qs@6.9.17": { + "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" + }, + "@types/range-parser@1.2.7": { + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "@types/send@0.17.4": { + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": [ + "@types/mime", + "@types/node" + ] + }, + "@types/serve-static@1.15.7": { + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": [ + "@types/http-errors", + "@types/node", + "@types/send" + ] + }, + "@types/ws@8.5.13": { + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "dependencies": [ + "@types/node" + ] + }, + "@vladfrangu/async_event_emitter@2.4.6": { + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==" + }, "chalk@5.3.0": { "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==" }, - "content-disposition@0.5.4": { - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "cluster-key-slot@1.1.2": { + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, + "debug@4.3.7": { + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": [ - "safe-buffer" + "ms" ] }, - "content-type@1.0.5": { - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + "denque@2.1.0": { + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" }, - "cookie-signature@1.0.6": { - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "discord-api-types@0.37.100": { + "integrity": "sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==" }, - "cookie@0.7.1": { - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" + "discord-api-types@0.37.83": { + "integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==" }, - "debug@2.6.9": { - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "discord-api-types@0.37.97": { + "integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==" + }, + "discord.js@14.16.3": { + "integrity": "sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==", "dependencies": [ - "ms@2.0.0" + "@discordjs/builders", + "@discordjs/collection@1.5.3", + "@discordjs/formatters", + "@discordjs/rest", + "@discordjs/util", + "@discordjs/ws", + "@sapphire/snowflake", + "discord-api-types@0.37.100", + "fast-deep-equal", + "lodash.snakecase", + "tslib", + "undici" ] }, - "define-data-property@1.1.4": { - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "fast-deep-equal@3.1.3": { + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "ioredis@5.4.1": { + "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", "dependencies": [ - "es-define-property", - "es-errors", - "gopd" - ] - }, - "depd@2.0.0": { - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy@1.2.0": { - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "ee-first@1.1.1": { - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "encodeurl@1.0.2": { - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "encodeurl@2.0.0": { - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" - }, - "es-define-property@1.0.0": { - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": [ - "get-intrinsic" - ] - }, - "es-errors@1.3.0": { - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "escape-html@1.0.3": { - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "etag@1.8.1": { - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "express@4.21.1": { - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", - "dependencies": [ - "accepts", - "array-flatten", - "body-parser", - "content-disposition", - "content-type", - "cookie", - "cookie-signature", + "@ioredis/commands", + "cluster-key-slot", "debug", - "depd", - "encodeurl@2.0.0", - "escape-html", - "etag", - "finalhandler", - "fresh", - "http-errors", - "merge-descriptors", - "methods", - "on-finished", - "parseurl", - "path-to-regexp", - "proxy-addr", - "qs", - "range-parser", - "safe-buffer", - "send", - "serve-static", - "setprototypeof", - "statuses", - "type-is", - "utils-merge", - "vary" + "denque", + "lodash.defaults", + "lodash.isarguments", + "redis-errors", + "redis-parser", + "standard-as-callback" ] }, - "finalhandler@1.3.1": { - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dependencies": [ - "debug", - "encodeurl@2.0.0", - "escape-html", - "on-finished", - "parseurl", - "statuses", - "unpipe" - ] + "lodash.defaults@4.2.0": { + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" }, - "forwarded@0.2.0": { - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "lodash.isarguments@3.1.0": { + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" }, - "fresh@0.5.2": { - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "lodash.snakecase@4.1.1": { + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + "lodash@4.17.21": { + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "get-intrinsic@1.2.4": { - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": [ - "es-errors", - "function-bind", - "has-proto", - "has-symbols", - "hasown" - ] - }, - "gopd@1.0.1": { - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": [ - "get-intrinsic" - ] - }, - "has-property-descriptors@1.0.2": { - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": [ - "es-define-property" - ] - }, - "has-proto@1.0.3": { - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" - }, - "has-symbols@1.0.3": { - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": [ - "function-bind" - ] - }, - "http-errors@2.0.0": { - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": [ - "depd", - "inherits", - "setprototypeof", - "statuses", - "toidentifier" - ] - }, - "iconv-lite@0.4.24": { - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": [ - "safer-buffer" - ] - }, - "inherits@2.0.4": { - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js@1.9.1": { - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "log-like-a-zombie@1.4.0": { - "integrity": "sha512-XZD8eDAhKhAKHcd6aGVwNiOXMo9L/TQLY0Fg+cAKa/OhG1vNcJTsJLf4ew33N5UgNIul2Plonrw+vPXjuMFG7Q==", - "dependencies": [ - "chalk" - ] - }, - "media-typer@0.3.0": { - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors@1.0.3": { - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" - }, - "methods@1.1.2": { - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": [ - "mime-db" - ] - }, - "mime@1.6.0": { - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "ms@2.0.0": { - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "magic-bytes.js@1.10.0": { + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" }, "ms@2.1.3": { "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "negotiator@0.6.3": { - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "path-to-regexp@6.2.1": { + "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" }, - "object-inspect@1.13.3": { - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==" + "redis-errors@1.2.0": { + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" }, - "on-finished@2.4.1": { - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "redis-parser@3.0.0": { + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", "dependencies": [ - "ee-first" + "redis-errors" ] }, - "parseurl@1.3.3": { - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + "standard-as-callback@2.1.0": { + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" }, - "path-to-regexp@0.1.10": { - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "ts-mixer@6.0.4": { + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" }, - "proxy-addr@2.0.7": { - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": [ - "forwarded", - "ipaddr.js" - ] + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "qs@6.13.0": { - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": [ - "side-channel" - ] + "undici-types@6.19.8": { + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, - "range-parser@1.2.1": { - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "undici@6.19.8": { + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==" }, - "raw-body@2.5.2": { - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": [ - "bytes", - "http-errors", - "iconv-lite", - "unpipe" - ] + "validator@13.12.0": { + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==" }, - "safe-buffer@5.2.1": { - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "why-is-node-running@3.2.1": { + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==" }, - "safer-buffer@2.1.2": { - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send@0.19.0": { - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dependencies": [ - "debug", - "depd", - "destroy", - "encodeurl@1.0.2", - "escape-html", - "etag", - "fresh", - "http-errors", - "mime", - "ms@2.1.3", - "on-finished", - "range-parser", - "statuses" - ] - }, - "serve-static@1.16.2": { - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dependencies": [ - "encodeurl@2.0.0", - "escape-html", - "parseurl", - "send" - ] - }, - "set-function-length@1.2.2": { - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": [ - "define-data-property", - "es-errors", - "function-bind", - "get-intrinsic", - "gopd", - "has-property-descriptors" - ] - }, - "setprototypeof@1.2.0": { - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "side-channel@1.0.6": { - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": [ - "call-bind", - "es-errors", - "get-intrinsic", - "object-inspect" - ] - }, - "statuses@2.0.1": { - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "toidentifier@1.0.1": { - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "type-is@1.6.18": { - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": [ - "media-typer", - "mime-types" - ] - }, - "unpipe@1.0.0": { - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "utils-merge@1.0.1": { - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "vary@1.1.2": { - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "ws@8.18.0": { + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==" } }, "workspace": { "dependencies": [ + "jsr:@oak/oak@^17.1.3", + "jsr:@proxnet/undead-logging@^1.2.0", "jsr:@std/assert@1", - "npm:express@^4.21.1", - "npm:log-like-a-zombie@1.4.0" + "npm:@types/express@5", + "npm:discord.js@^14.16.3", + "npm:ioredis@^5.4.1", + "npm:validator@^13.12.0", + "npm:why-is-node-running@^3.2.1" ] } } diff --git a/galv4.jpg b/galv4.jpg new file mode 100644 index 0000000..807281e Binary files /dev/null and b/galv4.jpg differ diff --git a/src/apiutils.ts b/src/apiutils.ts new file mode 100644 index 0000000..de603b2 --- /dev/null +++ b/src/apiutils.ts @@ -0,0 +1,88 @@ +import { Context, Next } from "@oak/oak"; +import Logging from "@proxnet/undead-logging"; + +const log = new Logging('APIUtils'); + +export function generateRandomString(length: number) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let randomString = ''; + + for (let i = 0; i < length; i++) { + const randomIndex = Math.floor(Math.random() * characters.length); + randomString += characters.charAt(randomIndex); + } + + return randomString; +} + +const instanceId = generateRandomString(128); + +export function checkQueryTypes(typeDef: T) { + return (ctx: Context, nxt: Next) => { + for (const key in typeDef) { + if (typeof Object.fromEntries(ctx.request.url.searchParams)[key] !== typeof (typeDef)[key]) { + ctx.response.status = 400; + setContentType(ctx, 'application/json'); + ctx.response.body = JSON.stringify(genericResponseFormat(true, "One or more query parameters were invalid or not found.")); + return; + } + } + nxt(); + }; +} +export function checkBodyTypes(typeDef: T) { + return async (ctx: Context, nxt: Next) => { + for (const key in typeDef) { + if (typeof (await ctx.request.body.json())[key] !== typeof (typeDef)[key]) { + log.e(`Body check for key '${key}' failed.`); + ctx.response.status = 400; + setContentType(ctx, 'application/json'); + ctx.response.body = JSON.stringify(genericResponseFormat(true, "One or more body values were invalid or not found.")); + return; + } + } + nxt(); + }; +} + +export function genericResponseFormat(failure: boolean, msg: string | null = null, data = null) { + return { failed: failure, instance: instanceId, message: msg, data: data }; +} +export function genericResponse(failure: boolean, msg: string | null = null, data = null) { + return (ctx: Context) => { + setContentType(ctx, 'application/json'); + ctx.response.body = JSON.stringify({ failed: failure, instance: instanceId, message: msg, data: data }); + }; +} +type RecNetResponse = { + Success: boolean, + Message: string +}; +export function RecNetResponse(success: boolean, message: string) { + const msg: RecNetResponse = { Success: success, Message: message }; + return (ctx: Context) => { + setContentType(ctx, 'application/json'); + ctx.response.body = JSON.stringify(msg); + } +} + +export async function logBody(ctx: Context, nxt: Next) { + nxt(); + log.d(`Request body: ${JSON.stringify(await ctx.request.body.text())}`); +} + +export function emptyArrayResponse(ctx: Context) { + setContentType(ctx, 'application/json'); + ctx.response.body = JSON.stringify([]); +} + +export function setJSONBody(ctx: Context, obj: object) { + ctx.response.type = 'json'; + ctx.response.body = JSON.stringify(obj); +} + +export function setContentType(ctx: Context, type: string) { + ctx.response.headers.set('Content-Type', type); +} + +export * as APIUtils from "./apiutils.ts" \ No newline at end of file diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..3d07aeb --- /dev/null +++ b/src/config.ts @@ -0,0 +1,110 @@ +import Logging from "@proxnet/undead-logging"; +import * as fs from "node:fs"; +import process from "node:process"; + +const log = new Logging("Config"); + +type RedisConfiguration = { + host: string, + port: number, + username: string, + password: string, + db: number +} + +type WebConfiguration = { + port: number, + host: string +} + +type PublicConfiguration = { + serverName: string, + owner: string, + motd: string +} + +type LoggingConfiguration = { + debug: boolean, + network: boolean +} + +type DiscordConfiguration = { + token: string, + clientId: string, + guildId: string +} + +type GalvanicConfiguration = { + redis: RedisConfiguration, + web: WebConfiguration, + public: PublicConfiguration, + logging: LoggingConfiguration, + discord: DiscordConfiguration +} + +const defaultConfig: GalvanicConfiguration = { + redis: { + host: "127.0.0.1", + port: 6379, + username: "", + password: "", + db: 0 + }, + web: { + port: 3000, + host: "127.0.0.1" + }, + public: { + serverName: "Galvanic Corrosion", + owner: "John Doe", + motd: "The narwhal bacons at midnight" + }, + logging: { + debug: false, + network: false + }, + discord: { + token: "replace-me", + guildId: "replace-me", + clientId: "replace-me" + } +} + +/** The current configuration. Read and parsed only during startup. */ +let config: GalvanicConfiguration | undefined; +try { + if (!configurationExists()) generateDefaultConfig(); + config = JSON.parse(fs.readFileSync('./config.json').toString()); +} catch (err) { + log.e(`Could not get config: ${err}`); + process.exit(1); +} + +/** + * Looks for a certain file in the current directory that shouldn't exist on the first run. + * Returns `false` when GC has ran at least once + */ +export function firstRun() { + if (!fs.existsSync('./firstrun')) return true; + else { + fs.writeFile('./firstrun', "", () => {}); + return false; + } +} + +/** Does the configuration file exist on the disk? */ +export function configurationExists() { + return fs.existsSync('./config.json'); +} + +/** Place the default configuration in the current directory. */ +export function generateDefaultConfig() { + fs.writeFileSync('./config.json', JSON.stringify(defaultConfig, undefined, ' ')); +} + +/** Get current server configuration */ +export function getConfig() { + return config; +} + +export * as Config from './config.ts'; \ No newline at end of file diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..b3a9c66 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,47 @@ +import { Redis } from "ioredis"; +import * as Config from "./config.ts"; +import Logging from "@proxnet/undead-logging"; +import process from "node:process"; + +const log = new Logging("RedisDB"); + +const config = Config.getConfig(); +if (typeof config == 'undefined') { + log.e(`Cannot start: Redis configuration failed`); + process.exit(1); +} + +let shuttingDown = false; +Deno.addSignalListener('SIGINT', () => { + if (shuttingDown) return; + shuttingDown = true; + log.n('Disconnecting from Redis'); + if (typeof Database !== 'undefined') Database.quit(); +}); + +export let Database: Redis | undefined; +export function connectToRedis() { + Database = new Redis({ + port: config?.redis.port, + host: config?.redis.host, + username: config?.redis.username == "" ? undefined : config?.redis.username, + password: config?.redis.password == "" ? undefined : config?.redis.password, + db: config?.redis.db + }); + log.i(`Connected to Redis`); +} + +export function buildKey(...args: string[]) { + return args.join(':'); +} +export const KeyGroups = { + Accounts: { + Ids: "account-ids", + Usernames: "account-usernames", + DisplayNames: "account-displaynames", + XP: "account-scores", + Developers: "account-developers", + ProfileImages: "account-images" + } +} +export * as Redis from "./db.ts"; \ No newline at end of file diff --git a/src/discord.ts b/src/discord.ts new file mode 100644 index 0000000..91bfe7b --- /dev/null +++ b/src/discord.ts @@ -0,0 +1,34 @@ +import * as discord from "discord.js"; +import { Config } from "./config.ts"; +import Logging from "@proxnet/undead-logging"; +import process from "node:process"; + +const log = new Logging("Discord"); + +const config = Config.getConfig(); +if (typeof config == 'undefined') { + log.e(`Cannot start: Discord configuration is unavailable`); + process.exit(1); +} + +export const client = new discord.Client({ intents: [discord.GatewayIntentBits.Guilds, discord.GatewayIntentBits.GuildPresences] }); + +client.once(discord.Events.ClientReady, client => { + log.i(`Logged in to Discord as "${client.user.tag}"`); + client.user?.setActivity(config?.public.motd, { type: discord.ActivityType.Custom }); +}); + +let shuttingDown = false; +Deno.addSignalListener('SIGINT', () => { + if (shuttingDown) return; + shuttingDown = true; + log.n('Disconnecting from Discord'); + client.destroy(); +}); + +export function login() { + log.i(`Creating Discord connection..`); + client.login(config?.discord.token); +} + +export * as Discord from "./discord.ts"; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 92918d9..579067d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,22 +1,52 @@ -import Logging from "log-like-a-zombie"; -import express from "express"; +import Logging from "@proxnet/undead-logging"; +import * as Config from "./config.ts"; +import { Application, Router } from "@oak/oak"; +import { Redis } from "./db.ts"; +import { Discord } from "./discord.ts"; +import { APIUtils } from "./apiutils.ts"; const log = new Logging("Main"); -const port = 3000; -const address = "127.0.0.1"; +log.i(`Starting Galvanic Corrosion..`); -log.i(`Starting HTTP server on http://${address}:${port}`); +const config = Config.getConfig(); -const app = express(); -app.disable('etag'); -app.disable('x-powered-by'); +if (typeof config == 'undefined') { + log.e('Cannot start: Configuration is undefined'); + Deno.exit(1); +} -app.use((rq: express.Request, rs: express.Response) => { - log.n(`${rq.ip} ${rq.method} ${rq.originalUrl}`); - rs.sendStatus(200); -}); +const port = config.web.port; +const host = config.web.host; -app.listen(port, address, () => { - log.i(`Listening on http://${address}:${port}`); +log.i(`Starting HTTP server on http://${host}:${port}`); + +const abortController = new AbortController(); +const app = new Application(); + +app.use(new Router().all('/', APIUtils.genericResponse(false, `${config?.public.serverName} - ${config?.public.motd}`)).routes()); + +try { + log.i(`Connecting to Redis..`); + Redis.connectToRedis(); +} catch (err) { + log.e(`Cannot start: Redis could not be initialized. ${err}`); + Deno.exit(1); +} + +try { + app.listen({port: port, hostname: host, signal: abortController.signal }); +} catch (err) { + log.e(`Cannot start: Network could not be initalized. ${err}`); + Deno.exit(1); +} + +Discord.login(); + +let shuttingDown = false; +Deno.addSignalListener('SIGINT', () => { + if (shuttingDown) return; + shuttingDown = true; + log.n(`Shutting down`); + abortController.abort(); }); \ No newline at end of file