Compare commits

...
57 Commits
Author SHA1 Message Date
Lncvrt a433f57a58 Add authorization header to versions (not in use yet) 2026-10-02 10:47:59 -07:00
Lncvrt c205c1bdaf Update dependencies 2026-10-02 10:45:17 -07:00
Lncvrt ffe3d99a9f Switch to headers for mods manifest & version manifest 2026-10-02 10:43:27 -07:00
Lncvrt de189bb7b7 Update CDN link 2026-08-10 10:52:20 -07:00
Lncvrt c80a8465a7 Update dependencies 2026-04-19 16:38:49 -07:00
Lncvrt b0dfab3b6a Update to new format 2026-04-19 16:27:05 -07:00
Lncvrt 4a12643098 Stupid issue im having 2026-04-19 15:21:08 -07:00
Lncvrt b35f0da493 Remove legacy launcher tables 2026-04-19 15:04:12 -07:00
Lncvrt 626fc4e6df bro 2026-04-03 16:28:21 -07:00
Lncvrt 11c6017429 Should fix linux 2026-04-03 14:02:19 -07:00
Lncvrt f9b0098375 Fix 2026-04-03 12:53:41 -07:00
Lncvrt 4069648732 Fully switch to new launcher & updater 2026-04-03 12:43:27 -07:00
Lncvrt d7befe30ca Add v2 launcher updater endpoint 2026-04-02 17:14:24 -07:00
Lncvrt 5aa3ed5820 ok ig 2026-03-28 18:08:25 -07:00
Lncvrt ba492caca4 oops 2026-03-28 18:07:32 -07:00
Lncvrt d7da88fc90 Add support for github host 2026-03-28 17:57:11 -07:00
Lncvrt 1b78f36a8a 26.1 fix 2026-03-27 23:51:14 -07:00
Lncvrt c19f2f533a A little late 2026-03-27 22:21:26 -07:00
Lncvrt 48bb6bef76 oops 2026-03-24 20:00:11 -07:00
Lncvrt 67034897da Count downloads for mods & fix game download count 2026-03-24 18:32:45 -07:00
Lncvrt fbb44e42ad oops 2026-03-24 18:14:43 -07:00
Lncvrt f3e9077dd3 better actually 2026-03-24 15:40:31 -07:00
Lncvrt 736d2fe9a6 stupid 2026-03-24 15:39:20 -07:00
Lncvrt bab60740d2 Add creators to the response 2026-03-24 15:34:01 -07:00
Lncvrt dffd9aa3c5 Fix cors issues 2026-03-24 15:15:49 -07:00
Lncvrt 61c1dba9df oop 2026-03-24 14:53:16 -07:00
Lncvrt aa296ab53d Add a mod manifest endpoint 2026-03-24 14:48:40 -07:00
Lncvrt 3d8153069d Update dependencies 2026-03-24 14:08:37 -07:00
Lncvrt b2aa8d6e8e Fix content type header 2026-03-24 12:11:25 -07:00
Lncvrt f1a9df30bc Revert "yep"
This reverts commit ae40455c5a.
2026-03-23 15:14:42 -07:00
Lncvrt 36865fd1df Update dependencies, update bun start command & update readme 2026-03-15 22:01:39 -07:00
Lncvrt 37266c1dd0 Revert "If user isn't in chatroom menu, don't send chatroom events"
This reverts commit c65974ff43.
2026-03-15 21:58:33 -07:00
Lncvrt 46f8c45f53 Built in modding support wip 2026-03-13 17:08:53 -07:00
Lncvrt 1d4f274f58 wtf 2026-03-13 15:48:41 -07:00
Lncvrt c65974ff43 If user isn't in chatroom menu, don't send chatroom events 2026-03-13 15:47:34 -07:00
Lncvrt ae40455c5a yep 2026-03-13 12:56:19 -07:00
Lncvrt ba51412592 Allow x64 macos apps to be ran on arm64 (rosetta2) 2026-03-12 18:22:04 -07:00
Lncvrt 5ba9b364d0 wtf 2026-03-12 07:35:08 -07:00
Lncvrt 414f8f6cec Use one DB Host/Port 2026-03-07 10:46:27 -07:00
Lncvrt ad2b6a0bbb Don't remember working on this but sure 2026-03-06 12:33:42 -07:00
Lncvrt 53c5642b4e Fix issues with joining 2026-03-05 12:31:09 -07:00
Lncvrt 3653c671f2 Add server support for pausing and unpausing 2026-03-05 11:44:17 -07:00
Lncvrt da0a38eec3 Early version of multiplayer 2026-03-05 11:36:17 -07:00
Lncvrt 9600a8ea6b Fixes 2026-02-28 12:25:11 -07:00
Lncvrt a186243b26 Signature signing 2026-02-28 11:57:53 -07:00
Lncvrt 5b40359cb8 Add additional archs 2026-02-27 12:49:23 -07:00
Lncvrt 7a427cb730 Fix latest endpoint 2026-02-27 12:46:40 -07:00
Lncvrt a2daf2bbfc Update this 2026-02-27 12:39:15 -07:00
Lncvrt b8a3de36a6 Fix wine support 2026-02-27 11:16:07 -07:00
Lncvrt 5c9b4f9910 Forgot to provide executable lmfao 2026-02-26 23:13:16 -07:00
Lncvrt fcdee21ab5 Finish reworking launcher manifest stuff 2026-02-26 22:47:15 -07:00
Lncvrt 02bad685da Don't format JSON response 2026-02-26 14:52:45 -07:00
Lncvrt 29a7903d6c Disable this until I am done with changes 2026-02-26 14:21:40 -07:00
Lncvrt 9caea847ec Provide if the user is an admin or not 2026-02-24 19:35:13 -07:00
Lncvrt 2c199339e0 Merge pull request 'Added back the allowed versions list' (#1) from Puppet/api:puppet-patch-1 into main
Reviewed-on: #1
2026-02-24 21:45:17 +00:00
Puppet 8ecfbf5f39 Made it get the last element in the allowed versions list for the latest ver
Before it was hardcoded
2026-02-24 21:44:04 +00:00
Puppet 9c54a4583b Added back the allowed versions list
If I ever need to fully block a ver I'll just make a blocked versions list
2026-02-24 21:39:49 +00:00
30 changed files with 1255 additions and 670 deletions

No files matched your search

+4 -5
View File
@@ -1,11 +1,10 @@
GAMES_DB_HOST= DB_HOST=
GAMES_DB_PORT= DB_PORT=
GAMES_DB_USER= GAMES_DB_USER=
GAMES_DB_PASS= GAMES_DB_PASS=
GAMES_DB_NAME= GAMES_DB_NAME=
BERRYDASH_DB_HOST=
BERRYDASH_DB_PORT=
BERRYDASH_DB_USER= BERRYDASH_DB_USER=
BERRYDASH_DB_PASS= BERRYDASH_DB_PASS=
BERRYDASH_DB_NAME= BERRYDASH_DB_NAME=
TURNSTILE_SECRET_KEY= TURNSTILE_SECRET_KEY=
PRIVATE_SIGNING_KEY=
+6 -2
View File
@@ -4,6 +4,10 @@ The server code for Lncvrt Games!
You **must** have Bun installed, install it [here](https://bun.com/) You **must** have Bun installed, install it [here](https://bun.com/)
## Running ## Building
Run `bun start`, if you are modifying code, run `bun dev`. Run `bun run build` to build. Then run `bun run start` to start the server.
## Development
Run `bun run dev`. That will start a server that will auto reload on file change (note: not as performant as running bun run build, if you need that)
+77 -76
View File
@@ -1,89 +1,92 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 2,
"configVersion": 1, "configVersion": 1,
"workspaces": { "workspaces": {
"": { "": {
"name": "lncvrt-games-api", "name": "lncvrt-games-api",
"dependencies": { "dependencies": {
"@elysiajs/cors": "1.4.1", "@elysiajs/cors": "1.4.2",
"@elysiajs/swagger": "1.3.1", "@elysiajs/swagger": "1.3.1",
"axios": "1.13.5", "axios": "1.20.0",
"bcryptjs": "3.0.3", "bcryptjs": "3.0.3",
"crypto": "1.0.1", "dotenv": "18.0.5",
"dotenv": "17.3.1", "drizzle-orm": "0.45.3",
"drizzle-orm": "0.45.1", "elysia": "1.4.30",
"elysia": "1.4.25", "image-size": "2.0.4",
"image-size": "2.0.2", "mysql2": "3.24.5",
"mysql2": "3.17.2", "nodemailer": "10.0.13",
"nodemailer": "8.0.1", "sharp": "0.35.5",
"sharp": "0.34.5", "validator": "13.15.35",
"validator": "13.15.26",
}, },
"devDependencies": { "devDependencies": {
"@types/nodemailer": "7.0.10", "@types/nodemailer": "8.0.2",
"@types/validator": "13.15.10", "@types/validator": "13.15.10",
"bun-types": "1.3.9", "bun-types": "1.4.2",
}, },
}, },
}, },
"packages": { "packages": {
"@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="], "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="],
"@elysiajs/cors": ["@elysiajs/cors@1.4.1", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-lQfad+F3r4mNwsxRKbXyJB8Jg43oAOXjRwn7sKUL6bcOW3KjUqUimTS+woNpO97efpzjtDE0tEjGk9DTw8lqTQ=="], "@elysiajs/cors": ["@elysiajs/cors@1.4.2", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-FTCcbH35brTLigF1W7BYySRZomgI/dBEMK9BgK9RP9Nez7zmpGh4koL/Yr1BFv8nYz7CfhRvcM8d/c+XnwMaVQ=="],
"@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-LcbLHa0zE6FJKWPWKsIC/f+62wbDv3aXydqcNPVPyqNcaUgwvCajIi+5kHEU6GO3oXUCpzKaMsb3gsjt8sLzFQ=="], "@elysiajs/swagger": ["@elysiajs/swagger@1.3.1", "", { "dependencies": { "@scalar/themes": "^0.9.52", "@scalar/types": "^0.0.12", "openapi-types": "^12.1.3", "pathe": "^1.1.2" }, "peerDependencies": { "elysia": ">= 1.3.0" } }, "sha512-LcbLHa0zE6FJKWPWKsIC/f+62wbDv3aXydqcNPVPyqNcaUgwvCajIi+5kHEU6GO3oXUCpzKaMsb3gsjt8sLzFQ=="],
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.3.4" }, "os": "darwin", "cpu": "x64" }, "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], "@img/sharp-freebsd-wasm32": ["@img/sharp-freebsd-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "os": "freebsd" }, "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.3.4", "", { "os": "linux", "cpu": "arm" }, "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A=="],
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.3.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.3.4", "", { "os": "linux", "cpu": "none" }, "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.3.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.3.4" }, "os": "linux", "cpu": "arm" }, "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ=="],
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.3.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.3.4" }, "os": "linux", "cpu": "none" }, "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.3.4" }, "os": "linux", "cpu": "s390x" }, "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" }, "os": "linux", "cpu": "arm64" }, "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.3.4" }, "os": "linux", "cpu": "x64" }, "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], "@img/sharp-wasm32": ["@img/sharp-wasm32@0.35.5", "", { "dependencies": { "@emnapi/runtime": "^1.11.3" } }, "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], "@img/sharp-webcontainers-wasm32": ["@img/sharp-webcontainers-wasm32@0.35.5", "", { "dependencies": { "@img/sharp-wasm32": "0.35.5" }, "cpu": "none" }, "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.35.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.35.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.35.5", "", { "os": "win32", "cpu": "x64" }, "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg=="],
"@scalar/openapi-types": ["@scalar/openapi-types@0.1.1", "", {}, "sha512-NMy3QNk6ytcCoPUGJH0t4NNr36OWXgZhA3ormr3TvhX1NDgoF95wFyodGVH8xiHeUyn2/FxtETm8UBLbB5xEmg=="], "@scalar/openapi-types": ["@scalar/openapi-types@0.1.1", "", {}, "sha512-NMy3QNk6ytcCoPUGJH0t4NNr36OWXgZhA3ormr3TvhX1NDgoF95wFyodGVH8xiHeUyn2/FxtETm8UBLbB5xEmg=="],
@@ -91,29 +94,31 @@
"@scalar/types": ["@scalar/types@0.0.12", "", { "dependencies": { "@scalar/openapi-types": "0.1.1", "@unhead/schema": "^1.9.5" } }, "sha512-XYZ36lSEx87i4gDqopQlGCOkdIITHHEvgkuJFrXFATQs9zHARop0PN0g4RZYWj+ZpCUclOcaOjbCt8JGe22mnQ=="], "@scalar/types": ["@scalar/types@0.0.12", "", { "dependencies": { "@scalar/openapi-types": "0.1.1", "@unhead/schema": "^1.9.5" } }, "sha512-XYZ36lSEx87i4gDqopQlGCOkdIITHHEvgkuJFrXFATQs9zHARop0PN0g4RZYWj+ZpCUclOcaOjbCt8JGe22mnQ=="],
"@sinclair/typebox": ["@sinclair/typebox@0.34.48", "", {}, "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA=="], "@sinclair/typebox": ["@sinclair/typebox@0.34.52", "", {}, "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw=="],
"@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="],
"@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="], "@types/node": ["@types/node@26.6.4", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg=="],
"@types/nodemailer": ["@types/nodemailer@7.0.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-tP+9WggTFN22Zxh0XFyst7239H0qwiRCogsk7v9aQS79sYAJY+WEbTHbNYcxUMaalHKmsNpxmoTe35hBEMMd6g=="], "@types/nodemailer": ["@types/nodemailer@8.0.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-c7M5ox8p0nEOfbJ2E9Qcmt8/QCu/VzsiAhzBiZbvky2PhKZDHpKB3sQHBM5MEZNkCfBOaS6S9//AGPRTNB7IaA=="],
"@types/validator": ["@types/validator@13.15.10", "", {}, "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA=="], "@types/validator": ["@types/validator@13.15.10", "", {}, "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA=="],
"@unhead/schema": ["@unhead/schema@1.11.20", "", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA=="], "@unhead/schema": ["@unhead/schema@1.11.20", "", { "dependencies": { "hookable": "^5.5.3", "zhead": "^2.2.4" } }, "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA=="],
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="], "axios": ["axios@1.20.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg=="],
"bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="], "bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="],
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], "bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@@ -121,29 +126,25 @@
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"crypto": ["crypto@1.0.1", "", {}, "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], "dotenv": ["dotenv@18.0.5", "", { "bin": { "dotenv": "dist/index.cjs" } }, "sha512-aBrGvt6KhjxbEnatqMWOMidftwsrCiCvqh1yNIGB9QyF0J+VcWkKDMsvYOEm7ZS82oiV+BT1sTOtIlAmUlTpwQ=="],
"drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="], "drizzle-orm": ["drizzle-orm@0.45.3", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@netlify/db": ">=0.4.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "prisma": "*", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@netlify/db", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "prisma", "sql.js", "sqlite3"] }, "sha512-CAloER21cDdcgZ1OmjrZX82EeRNsdP+y0onX/eyoYDaYzki/adrAa91lzM5jWh7zZPK4qUbw/6LGv9J+A+uizA=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"elysia": ["elysia@1.4.25", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-liKjavH99Gpzrv9cDil6uYWmPuqESfPFV1FIaFSd3iNqo3y7e29sN43VxFIK8tWWnyi6eDAmi2SZk8hNAMQMyg=="], "elysia": ["elysia@1.4.30", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-S2qqV0CbM4faB1hQQ5IYoeYnAUinjHlzRduxaBc4OoNqh0GjOc8k6tt3hv5ozWcG6Svr6hugw6JL4zNke4jwfA=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
@@ -151,11 +152,11 @@
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
"file-type": ["file-type@21.3.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA=="], "file-type": ["file-type@22.1.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.5", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0" } }, "sha512-sEB0oStmeLGtpmSAD88RDNuLsvhv4iC3u0lE25CUvfCULvj49xoQ3l3K525Vn+ZzsZ6DOLajQCV6RmhwwhqiJg=="],
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "follow-redirects": ["follow-redirects@1.16.1", "", { "peerDependencies": { "debug": "*" }, "optionalPeers": ["debug"] }, "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
@@ -171,21 +172,23 @@
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
"iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="],
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="], "image-size": ["image-size@2.0.4", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-QRUkFFsRV/6fuESxb9Vkq+a0LkSrgKXuc2NEqfikiXxxN/G3tjWt5EVUlMaImRBZRZK/jRBEbYvpPYZL8t08Zw=="],
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
"lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], "lru.min": ["lru.min@1.1.5", "", {}, "sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
@@ -197,31 +200,29 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mysql2": ["mysql2@3.17.2", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.2", "long": "^5.3.2", "lru.min": "^1.1.3", "named-placeholders": "^1.1.6", "seq-queue": "^0.0.5", "sql-escaper": "^1.3.3" } }, "sha512-/tFCtdqk5V5Aowpnzshryxuxp63ti4I7kcp3yqAKgWmhYXEXs8+F/IbQ6JTMzQPYc+ElnnhmMD2SqUYLtRVcTQ=="], "mysql2": ["mysql2@3.24.5", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "generate-function": "^2.3.1", "iconv-lite": "^0.7.3", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.5.1" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-X6Ujsr2QSkkLpkQGjxzpKRAPn9nu4axpR63ntBzquFVEvPOArgbUQ1sJjFKI7hnaYtiVZxa17Z7q18KSubW0IQ=="],
"named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="],
"nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="],
"nodemailer": ["nodemailer@8.0.1", "", {}, "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg=="], "nodemailer": ["nodemailer@10.0.13", "", {}, "sha512-SzG86OlvcW/NNhUFC6uROMwRTL4n7MswfQqC/T8mhkmnY1YVa23zUEMYi4ijSeXSl9GLz9ZeTJDUatEDuY5FeQ=="],
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
"pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="],
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], "sql-escaper": ["sql-escaper@1.5.2", "", {}, "sha512-6CKD38c31SENivxOADeMNLdukOnUxUcflKtzVWzace7Riv1v7cAEym5Cx9Q7gYZ3ezIJI7ZpqecQq8cqNeSSFg=="],
"sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="], "strtok3": ["strtok3@10.3.5", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA=="],
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
"token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="],
@@ -229,11 +230,11 @@
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], "uint8array-extras": ["uint8array-extras@1.6.0", "", {}, "sha512-8iAasVS4wUx0gPLjH8Xtz2PeDzTSiy8QiLGu2DT3X5GU+egFEQhpnbtkehbAwjvDcxIERmCYcysiODFmE3Ud0Q=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="],
"validator": ["validator@13.15.26", "", {}, "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA=="], "validator": ["validator@13.15.35", "", {}, "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw=="],
"zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="], "zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="],
+3 -3
View File
@@ -3,7 +3,7 @@
-- https://www.phpmyadmin.net/ -- https://www.phpmyadmin.net/
-- --
-- Host: localhost -- Host: localhost
-- Generation Time: Feb 21, 2026 at 09:07 PM -- Generation Time: Mar 05, 2026 at 06:35 PM
-- Server version: 11.8.6-MariaDB-log -- Server version: 11.8.6-MariaDB-log
-- PHP Version: 8.5.3 -- PHP Version: 8.5.3
@@ -119,7 +119,7 @@ CREATE TABLE `userposts` (
-- --
ALTER TABLE `chatroom_reports` ALTER TABLE `chatroom_reports`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD KEY `chatId` (`chatId`); ADD KEY `chatroom_reports?chatId` (`chatId`);
-- --
-- Indexes for table `chats` -- Indexes for table `chats`
@@ -199,7 +199,7 @@ ALTER TABLE `userposts`
-- Constraints for table `chatroom_reports` -- Constraints for table `chatroom_reports`
-- --
ALTER TABLE `chatroom_reports` ALTER TABLE `chatroom_reports`
ADD CONSTRAINT `chatId` FOREIGN KEY (`chatId`) REFERENCES `chats` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; ADD CONSTRAINT `chatroom_reports?chatId` FOREIGN KEY (`chatId`) REFERENCES `chats` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT; COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+89 -24
View File
@@ -3,9 +3,9 @@
-- https://www.phpmyadmin.net/ -- https://www.phpmyadmin.net/
-- --
-- Host: localhost -- Host: localhost
-- Generation Time: Feb 22, 2026 at 03:13 PM -- Generation Time: Apr 19, 2026 at 10:03 PM
-- Server version: 11.8.6-MariaDB-log -- Server version: 10.11.14-MariaDB-0ubuntu0.24.04.1
-- PHP Version: 8.5.3 -- PHP Version: 8.3.30
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION; START TRANSACTION;
@@ -23,6 +23,21 @@ SET time_zone = "+00:00";
-- -------------------------------------------------------- -- --------------------------------------------------------
--
-- Table structure for table `files`
--
CREATE TABLE `files` (
`id` bigint(20) NOT NULL,
`path` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`hash` char(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`size` bigint(20) NOT NULL,
`host` int(1) NOT NULL DEFAULT 0,
`mimeType` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
-- --------------------------------------------------------
-- --
-- Table structure for table `games` -- Table structure for table `games`
-- --
@@ -43,13 +58,11 @@ CREATE TABLE `games` (
-- --
CREATE TABLE `launcherupdates` ( CREATE TABLE `launcherupdates` (
`id` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `id` bigint(20) NOT NULL,
`releaseDate` bigint(20) NOT NULL, `version` varchar(32) NOT NULL,
`downloadUrls` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `notes` text NOT NULL,
`platforms` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `release_date` timestamp NOT NULL,
`hidden` tinyint(1) NOT NULL DEFAULT 1, `download_data` text NOT NULL
`place` bigint(20) NOT NULL DEFAULT 0,
`sha512sums` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '[]'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED;
-- -------------------------------------------------------- -- --------------------------------------------------------
@@ -61,33 +74,48 @@ CREATE TABLE `launcherupdates` (
CREATE TABLE `launcherversionmanifest` ( CREATE TABLE `launcherversionmanifest` (
`id` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `id` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`displayName` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `displayName` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`versionName` text NOT NULL,
`downloads` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '{}',
`executables` text NOT NULL DEFAULT '[]',
`modSupportDownloads` text NOT NULL DEFAULT '{}',
`releaseDate` bigint(20) NOT NULL, `releaseDate` bigint(20) NOT NULL,
`downloadUrlVersion` text NOT NULL,
`downloadUrls` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`platforms` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`executables` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`hidden` tinyint(1) NOT NULL DEFAULT 1, `hidden` tinyint(1) NOT NULL DEFAULT 1,
`game` bigint(20) NOT NULL DEFAULT 0, `game` bigint(20) NOT NULL DEFAULT 0,
`place` bigint(20) NOT NULL DEFAULT 0, `place` bigint(20) NOT NULL DEFAULT 0,
`sha512sums` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '[]',
`sizes` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '[]',
`changelog` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, `changelog` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`category` int(11) NOT NULL DEFAULT -1, `category` int(11) NOT NULL DEFAULT -1,
`lastRevision` bigint(20) NOT NULL DEFAULT 0, `lastRevision` bigint(20) NOT NULL DEFAULT 0,
`downloads` bigint(20) NOT NULL DEFAULT 0 `downloadCount` bigint(20) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED;
-- -------------------------------------------------------- -- --------------------------------------------------------
-- --
-- Table structure for table `loaderupdates` -- Table structure for table `mods`
-- --
CREATE TABLE `loaderupdates` ( CREATE TABLE `mods` (
`id` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `id` bigint(20) NOT NULL,
`name` text NOT NULL,
`creators` text NOT NULL DEFAULT '[]',
`description` text DEFAULT NULL,
`downloadCount` bigint(20) NOT NULL DEFAULT 0,
`hidden` tinyint(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED;
-- --------------------------------------------------------
--
-- Table structure for table `modversions`
--
CREATE TABLE `modversions` (
`id` text NOT NULL,
`downloads` text NOT NULL DEFAULT '{}',
`gameVersion` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`releaseDate` bigint(20) NOT NULL, `releaseDate` bigint(20) NOT NULL,
`hidden` tinyint(1) NOT NULL DEFAULT 1, `changelog` text DEFAULT NULL,
`place` bigint(20) NOT NULL DEFAULT 0 `hidden` tinyint(1) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPRESSED;
-- -------------------------------------------------------- -- --------------------------------------------------------
@@ -142,6 +170,12 @@ CREATE TABLE `verifycodes` (
-- Indexes for dumped tables -- Indexes for dumped tables
-- --
--
-- Indexes for table `files`
--
ALTER TABLE `files`
ADD PRIMARY KEY (`id`);
-- --
-- Indexes for table `games` -- Indexes for table `games`
-- --
@@ -162,11 +196,18 @@ ALTER TABLE `launcherversionmanifest`
ADD KEY `fk_game` (`game`); ADD KEY `fk_game` (`game`);
-- --
-- Indexes for table `loaderupdates` -- Indexes for table `mods`
-- --
ALTER TABLE `loaderupdates` ALTER TABLE `mods`
ADD PRIMARY KEY (`id`); ADD PRIMARY KEY (`id`);
--
-- Indexes for table `modversions`
--
ALTER TABLE `modversions`
ADD PRIMARY KEY (`id`(255)),
ADD KEY `fk_gameVersion` (`gameVersion`);
-- --
-- Indexes for table `resetcodes` -- Indexes for table `resetcodes`
-- --
@@ -189,12 +230,30 @@ ALTER TABLE `verifycodes`
-- AUTO_INCREMENT for dumped tables -- AUTO_INCREMENT for dumped tables
-- --
--
-- AUTO_INCREMENT for table `files`
--
ALTER TABLE `files`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT;
-- --
-- AUTO_INCREMENT for table `games` -- AUTO_INCREMENT for table `games`
-- --
ALTER TABLE `games` ALTER TABLE `games`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT; MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `launcherupdates`
--
ALTER TABLE `launcherupdates`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `mods`
--
ALTER TABLE `mods`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT;
-- --
-- AUTO_INCREMENT for table `resetcodes` -- AUTO_INCREMENT for table `resetcodes`
-- --
@@ -222,6 +281,12 @@ ALTER TABLE `verifycodes`
-- --
ALTER TABLE `launcherversionmanifest` ALTER TABLE `launcherversionmanifest`
ADD CONSTRAINT `fk_category` FOREIGN KEY (`game`) REFERENCES `games` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; ADD CONSTRAINT `fk_category` FOREIGN KEY (`game`) REFERENCES `games` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints for table `modversions`
--
ALTER TABLE `modversions`
ADD CONSTRAINT `fk_gameVersion` FOREIGN KEY (`gameVersion`) REFERENCES `launcherversionmanifest` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT; COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+14 -15
View File
@@ -2,29 +2,28 @@
"name": "lncvrt-games-api", "name": "lncvrt-games-api",
"version": "0.1.0", "version": "0.1.0",
"scripts": { "scripts": {
"start": "bun run src/index.ts", "start": "bun run dist/index.js",
"dev": "bun run --watch src/index.ts", "dev": "bun run --watch src/index.ts",
"build": "rm -rf dist; bun build src/index.ts --target=bun --outdir=dist" "build": "rm -rf dist; bun build src/index.ts --target=bun --outdir=dist"
}, },
"dependencies": { "dependencies": {
"@elysiajs/cors": "1.4.1", "@elysiajs/cors": "1.4.2",
"@elysiajs/swagger": "1.3.1", "@elysiajs/swagger": "1.3.1",
"axios": "1.13.5", "axios": "1.20.0",
"bcryptjs": "3.0.3", "bcryptjs": "3.0.3",
"crypto": "1.0.1", "dotenv": "18.0.5",
"dotenv": "17.3.1", "drizzle-orm": "0.45.3",
"drizzle-orm": "0.45.1", "elysia": "1.4.30",
"elysia": "1.4.25", "image-size": "2.0.4",
"image-size": "2.0.2", "mysql2": "3.24.5",
"mysql2": "3.17.2", "nodemailer": "10.0.13",
"nodemailer": "8.0.1", "sharp": "0.35.5",
"sharp": "0.34.5", "validator": "13.15.35"
"validator": "13.15.26"
}, },
"devDependencies": { "devDependencies": {
"@types/nodemailer": "7.0.10", "@types/nodemailer": "8.0.2",
"@types/validator": "13.15.10", "@types/validator": "13.15.10",
"bun-types": "1.3.9" "bun-types": "1.4.2"
}, },
"module": "src/index.js" "module": "src/index.js"
} }
+483 -146
View File
@@ -1,4 +1,4 @@
import { Elysia, t } from 'elysia' import { Elysia, redirect, t } from 'elysia'
import { cors } from '@elysiajs/cors' import { cors } from '@elysiajs/cors'
import { ElysiaWS } from 'elysia/dist/ws' import { ElysiaWS } from 'elysia/dist/ws'
import { getDatabaseConnection, jsonResponse } from './lib/util' import { getDatabaseConnection, jsonResponse } from './lib/util'
@@ -7,16 +7,18 @@ import swagger from '@elysiajs/swagger'
import { berryDashChats, berryDashUserData, users } from './lib/tables' import { berryDashChats, berryDashUserData, users } from './lib/tables'
import { and, desc, eq } from 'drizzle-orm' import { and, desc, eq } from 'drizzle-orm'
import { checkAuthorization } from './lib/auth' import { checkAuthorization } from './lib/auth'
import { randomBytes } from 'node:crypto'
import { handler as getVerifyCodeHandler } from './routes/get-verify-code' import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
import { handler as canLoadClientHandler } from './routes/can-load-client' import { handler as canLoadClientHandler } from './routes/can-load-client'
import { handler as launcherVersionsHandler } from './routes/launcher/versions' import { handler as launcherModsGetHandler } from './routes/launcher/mods/get'
import { handler as launcherLatestHandler } from './routes/launcher/latest' import { handler as launcherVersionsHandler } from './routes/launcher/versions/get'
import { handler as launcherDownloadHandler } from './routes/launcher/download' import { handler as launcherLatestHandler } from './routes/launcher/latest/get'
import { handler as launcherLoaderLatestHandler } from './routes/launcher/loader/latest' import { handler as launcherUpdateHandler } from './routes/launcher/update/get'
import { handler as launcherLoaderUpdateDataHandler } from './routes/launcher/loader/update-data' import { handler as launcherDownloadHandler } from './routes/launcher/download/get'
import { handler as launcherLoaderLatestHandler } from './routes/launcher/loader/latest/get'
import { handler as accountLoginPostHandler } from './routes/account/login/post' import { handler as accountLoginPostHandler } from './routes/account/login/post'
import { handler as accountRegisterPostHandler } from './routes/account/register/post' import { handler as accountRegisterPostHandler } from './routes/account/register/post'
@@ -80,7 +82,8 @@ const app = new Elysia({
.use( .use(
cors({ cors({
origin: '*', origin: '*',
methods: ['POST', 'GET'] methods: ['POST', 'GET'],
exposeHeaders: '*'
}) })
) )
.use( .use(
@@ -106,19 +109,384 @@ interface ClientInfoData {
ClientPlatform: string ClientPlatform: string
} }
interface MultiplayerClient {
Id: string
PosX: number
PosY: number
Flipped: boolean
Paused: boolean
FlaggedReason: string | null
Token: string
Username: string
UserID: number
Admin: boolean
Platform: number | null
JoinTimestamp: number
}
interface MultiplayerBerry {
Id: string
Type: string
Timestamp: number
PosX: number
}
const clients = new Set<ElysiaWS>() const clients = new Set<ElysiaWS>()
const clientInfo: ClientInfoData[] = [] let clientInfo: ClientInfoData[] = []
let multiplayerClients: MultiplayerClient[] = []
let multiplayerBerries: MultiplayerBerry[] = []
setInterval(async () => {
if (multiplayerClients.length == 0) return
let berry = ''
const spawnProbability = Math.random()
const id = randomBytes(16).toString('hex')
if (spawnProbability <= 0.575) {
berry = 'NormalBerry'
} else if (spawnProbability <= 0.75) {
berry = 'PoisonBerry'
} else if (spawnProbability <= 0.85) {
berry = 'SlowBerry'
} else if (spawnProbability <= 0.95) {
berry = 'UltraBerry'
} else {
berry = 'RandomBerry'
}
const time = Date.now()
const posX = Math.random()
const data: MultiplayerBerry = {
Id: id,
Type: berry,
Timestamp: time,
PosX: posX
}
multiplayerBerries.push(data)
for (const multiplayerClientInfo of multiplayerClients) {
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_berry_spawn',
id,
berryType: berry,
posX,
timestamp: time
})
}
}, 1000)
app.ws('/ws', { app.ws('/ws', {
maxPayloadLength: 10 * 1024 * 1024, maxPayloadLength: 10 * 1024 * 1024,
open (ws) { open(ws) {
clients.add(ws) clients.add(ws)
console.log(ws.id, 'connected') // console.log(ws.id, 'connected')
}, },
async message (ws, message: any) { async message(ws, message: any) {
// console.log('received:', message, 'from', ws.id) console.log('received:', message, 'from', ws.id)
if (message == 'ping') ws.send('ping')
if (message.type) { if (message.type) {
if (message.type == 'connected_with_current_scene') { if (message.type == 'multiplayer_player_unpause') {
const multiplayerInfo = multiplayerClients.find(
client => client.Id == ws.id
)
if (!multiplayerInfo) return
multiplayerInfo.Paused = false
const index = multiplayerClients.findIndex(client => client.Id == ws.id)
if (index === -1) return
multiplayerClients[index] = multiplayerInfo
for (const multiplayerClientInfo of multiplayerClients) {
if (ws.id == multiplayerClientInfo.Id) continue
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_player_unpause',
id: multiplayerInfo.UserID
})
}
} else if (message.type == 'multiplayer_player_pause') {
const multiplayerInfo = multiplayerClients.find(
client => client.Id == ws.id
)
if (!multiplayerInfo) return
multiplayerInfo.Paused = true
const index = multiplayerClients.findIndex(client => client.Id == ws.id)
if (index === -1) return
multiplayerClients[index] = multiplayerInfo
for (const multiplayerClientInfo of multiplayerClients) {
if (ws.id == multiplayerClientInfo.Id) continue
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_player_pause',
id: multiplayerInfo.UserID
})
}
} else if (
message.type == 'multiplayer_player_move' &&
message.x !== undefined &&
message.y !== undefined &&
message.f !== undefined
) {
const multiplayerInfo = multiplayerClients.find(
client => client.Id == ws.id
)
if (!multiplayerInfo) return
multiplayerInfo.PosX = Number(message.x)
multiplayerInfo.PosY = Number(message.y)
multiplayerInfo.Flipped = Boolean(message.f)
const index = multiplayerClients.findIndex(client => client.Id == ws.id)
if (index === -1) return
multiplayerClients[index] = multiplayerInfo
for (const multiplayerClientInfo of multiplayerClients) {
if (ws.id == multiplayerClientInfo.Id) continue
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_player_move',
id: multiplayerInfo.UserID,
x: Number(message.x),
y: Number(message.y),
f: Boolean(message.f)
})
}
} else if (message.type == 'multiplayer_leave') {
const multiplayerInfo = multiplayerClients.find(
client => client.Id == ws.id
)
if (!multiplayerInfo) return
multiplayerClients = multiplayerClients.filter(
client => client.Id !== ws.id
)
for (const multiplayerClientInfo of multiplayerClients) {
if (ws.id == multiplayerClientInfo.Id) continue
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_player_leave',
id: multiplayerInfo.UserID
})
}
} else if (message.type == 'multiplayer_join_request' && message.auth) {
const dbInfo0 = getDatabaseConnection(0)
const dbInfo1 = getDatabaseConnection(1)
if (!dbInfo0 || !dbInfo1) {
ws.send({
type: 'multiplayer_join_response',
accepted: false,
denyReason: 'Unable to authenticate',
key: null,
players: []
})
return
}
const { connection: connection0, db: db0 } = dbInfo0
const { connection: connection1, db: db1 } = dbInfo1
const ip = ws.remoteAddress
const authResult = await checkAuthorization(
message.auth as string,
db0,
ip
)
if (!authResult.valid) {
connection0.end()
connection1.end()
ws.send({
type: 'multiplayer_join_response',
accepted: false,
denyReason: 'Unable to authenticate',
id: null,
players: []
})
return
}
const userId = authResult.id
const userResult = await db0
.select({
username: users.username,
admin: users.admin,
token: users.token
})
.from(users)
.where(eq(users.id, userId))
.execute()
if (userResult.length !== 1) {
connection0.end()
connection1.end()
ws.send({
type: 'multiplayer_join_response',
accepted: false,
denyReason: 'Unable to authenticate',
id: null,
players: null
})
return
}
const currentUserData = await db1
.select({
saveData: berryDashUserData.saveData
})
.from(berryDashUserData)
.where(eq(berryDashUserData.id, userId))
.execute()
const currentSavedata = currentUserData[0]?.saveData
? JSON.parse(currentUserData[0].saveData)
: null
if (!currentSavedata) {
connection0.end()
connection1.end()
ws.send({
type: 'multiplayer_join_response',
accepted: false,
denyReason: 'Unable to load save data',
id: null,
players: null
})
return
}
const currentCustomIcon =
currentSavedata.bird?.customIcon?.selected ?? null
const currentIcon = currentCustomIcon
? 0
: currentSavedata.bird?.icon ?? 1
const currentOverlay = currentCustomIcon
? 1
: currentSavedata.bird?.overlay ?? 0
const currentIconColor = currentCustomIcon
? [255, 255, 255]
: currentSavedata?.settings?.colors?.icon ?? [255, 255, 255]
const currentOverlayColor = currentCustomIcon
? [255, 255, 255]
: currentSavedata?.settings?.colors?.overlay ?? [255, 255, 255]
const players: any = []
for (const marketplaceInfo of multiplayerClients) {
const playerUserData = await db1
.select({
saveData: berryDashUserData.saveData
})
.from(berryDashUserData)
.where(eq(berryDashUserData.id, marketplaceInfo.UserID))
.execute()
const playerSavedata = playerUserData[0]?.saveData
? JSON.parse(playerUserData[0].saveData)
: null
if (!playerSavedata) continue
const playerInfo = await db0
.select({
username: users.username,
admin: users.admin
})
.from(users)
.where(eq(users.id, marketplaceInfo.UserID))
.execute()
if (playerInfo.length !== 1) continue
const playerCustomIcon =
playerSavedata.bird?.customIcon?.selected ?? null
const playerIcon = playerCustomIcon
? 0
: playerSavedata.bird?.icon ?? 1
const playerOverlay = playerCustomIcon
? 1
: playerSavedata.bird?.overlay ?? 0
const playerIconColor = playerCustomIcon
? [255, 255, 255]
: playerSavedata?.settings?.colors?.icon ?? [255, 255, 255]
const playerOverlayColor = playerCustomIcon
? [255, 255, 255]
: playerSavedata?.settings?.colors?.overlay ?? [255, 255, 255]
players.push({
id: marketplaceInfo.UserID,
username: playerInfo[0].username,
admin: playerInfo[0].admin,
timestamp: marketplaceInfo.JoinTimestamp,
iconData: {
icon: playerIcon,
overlay: playerOverlay,
iconColor: playerIconColor,
overlayColor: playerOverlayColor,
customIcon: playerCustomIcon ?? ''
}
})
}
const time = Date.now()
const newClient: MultiplayerClient = {
Id: ws.id,
PosX: 0,
PosY: -1,
Flipped: false,
Paused: false,
FlaggedReason: null,
Token: userResult[0].token,
Username: userResult[0].username,
UserID: userId,
Admin: userResult[0].admin,
Platform: null,
JoinTimestamp: time
}
multiplayerClients.push(newClient)
ws.send({
type: 'multiplayer_join_response',
accepted: true,
denyReason: null,
players
})
for (const existingClient of multiplayerClients) {
if (ws.id === existingClient.Id) continue
const clientSocket = Array.from(clients).find(
client => client.id === existingClient.Id
)
if (!clientSocket) continue
clientSocket.send({
type: 'multiplayer_player_join',
id: userId,
username: userResult[0].username,
admin: userResult[0].admin,
timestamp: time,
iconData: {
icon: currentIcon,
overlay: currentOverlay,
iconColor: currentIconColor,
overlayColor: currentOverlayColor,
customIcon: currentCustomIcon ?? ''
}
})
}
connection0.end()
connection1.end()
} else if (message.type == 'connected_with_current_scene') {
if ( if (
!message.sceneName || !message.sceneName ||
!message.Requester || !message.Requester ||
@@ -535,12 +903,33 @@ app.ws('/ws', {
} }
} }
}, },
close (ws) { async close(ws) {
clients.forEach(client => { clients.forEach(client => {
if (client.id === ws.id) clients.delete(client) if (client.id === ws.id) clients.delete(client)
}) })
clientInfo = clientInfo.filter(client => client.Id !== ws.id)
console.log(ws.id, 'disconnected') const multiplayerInfo = multiplayerClients.find(
client => client.Id == ws.id
)
if (multiplayerInfo) {
multiplayerClients = multiplayerClients.filter(
client => client.Id !== ws.id
)
for (const multiplayerClientInfo of multiplayerClients) {
if (ws.id == multiplayerClientInfo.Id) continue
const client = Array.from(clients).find(
client => client.id === multiplayerClientInfo.Id
)
if (!client) continue
client.send({
type: 'multiplayer_player_leave',
id: multiplayerInfo.UserID
})
}
}
// console.log(ws.id, 'disconnected')
} }
}) })
@@ -568,22 +957,37 @@ app.get('/can-load-client', context => canLoadClientHandler(context), {
) )
}) })
}) })
app.get('/launcher/versions', context => launcherVersionsHandler(context), { app.get('/launcher/mods', context => launcherModsGetHandler(context), {
detail: { detail: {
description: description: 'The endpoint for getting mods for a version.',
'The endpoint for getting the launcher manifest.\n\nNote: if going to use the params, both must be provided at the same time.',
tags: ['Launcher'] tags: ['Launcher']
}, },
query: t.Object({ query: t.Object({
platform: t.Optional(t.String({ examples: ['windows', 'macos', 'linux'] })), version: t.String()
arch: t.Optional(t.String({ examples: ['x86_64', 'aarch64'] }))
}), }),
headers: t.Object({ headers: t.Object({
'x-forwarded-for': t.Optional( 'x-forwarded-for': t.Optional(
t.String({ t.String({
hide: true hide: true
}) })
) ),
clientplatform: t.String()
})
})
app.get('/launcher/versions', context => launcherVersionsHandler(context), {
detail: {
description:
'The endpoint for getting the launcher manifest.\n\nNote: if going to use the params, both must be provided at the same time.',
tags: ['Launcher']
},
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
hide: true
})
),
clientplatform: t.Optional(t.String()),
authorization: t.Optional(t.String())
}) })
}) })
app.get('/launcher/latest', launcherLatestHandler, { app.get('/launcher/latest', launcherLatestHandler, {
@@ -599,12 +1003,29 @@ app.get('/launcher/latest', launcherLatestHandler, {
) )
}) })
}) })
app.get('/launcher/download', context => launcherDownloadHandler(context), { app.get('/launcher/update', launcherUpdateHandler, {
detail: { detail: {
hide: true description: "The endpoint for getting the launcher's update data.",
tags: ['Launcher']
}, },
query: t.Object({ query: t.Object({
id: t.String() target: t.String(),
arch: t.Optional(t.String()),
currentVersion: t.String()
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
hide: true
})
)
})
})
app.get('/launcher/download', context => launcherDownloadHandler(context), {
query: t.Object({
versionId: t.Optional(t.String({ hide: true })),
modId: t.Optional(t.String({ hide: true })),
downloadId: t.String()
}), }),
headers: t.Object({ headers: t.Object({
'x-forwarded-for': t.Optional( 'x-forwarded-for': t.Optional(
@@ -628,31 +1049,6 @@ app.get('/launcher/loader/latest', launcherLoaderLatestHandler, {
) )
}) })
}) })
app.get(
'/launcher/loader/update-data',
context => launcherLoaderUpdateDataHandler(context),
{
detail: {
description:
'The endpoint for getting Launcher Update data for when a new Update is released. It will be send & read by the updater.\n\nNote: if going to use the params, both must be provided at the same time.',
tags: ['Launcher']
},
query: t.Object({
platform: t.Optional(
t.String({ examples: ['windows', 'macos', 'linux'] })
),
arch: t.Optional(t.String({ examples: ['x86_64', 'aarch64'] }))
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
}
)
app.post('/account/login', context => accountLoginPostHandler(context), { app.post('/account/login', context => accountLoginPostHandler(context), {
detail: { detail: {
description: description:
@@ -810,105 +1206,65 @@ app.get('/berrydash/latest-version', berryDashLatestVersionGetHandler, {
}) })
app.get( app.get(
'/berrydash/leaderboards/score', '/berrydash/leaderboards/score',
context => berrydashLeaderboardGetHandler(context, 0), ({ query, redirect }) => {
return redirect(
`/api/berrydash/leaderboard/score?${new URLSearchParams(
query
).toString()}`
)
},
{ {
detail: { detail: { hide: true }
deprecated: true,
description:
'This endpoint was renamed to `/berrydash/leaderboard/score` and will be removed on March 19th 2026.',
tags: ['Berry Dash', 'Leaderboards']
},
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
} }
) )
app.get( app.get(
'/berrydash/leaderboards/berry', '/berrydash/leaderboards/berry',
context => berrydashLeaderboardGetHandler(context, 1), ({ query, redirect }) => {
return redirect(
`/api/berrydash/leaderboard/berry?${new URLSearchParams(
query
).toString()}`
)
},
{ {
detail: { detail: { hide: true }
deprecated: true,
description:
'This endpoint was renamed to `/berrydash/leaderboard/berry` and will be removed on March 19th 2026.',
tags: ['Berry Dash', 'Leaderboards']
},
query: t.Object({
berry: t.String()
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
} }
) )
app.get( app.get(
'/berrydash/leaderboards/coin', '/berrydash/leaderboards/coin',
context => berrydashLeaderboardGetHandler(context, 2), ({ query, redirect }) => {
return redirect(
`/api/berrydash/leaderboard/coin?${new URLSearchParams(query).toString()}`
)
},
{ {
detail: { detail: { hide: true }
deprecated: true,
description:
'This endpoint was renamed to `/berrydash/leaderboard/coin` and will be removed on March 19th 2026.',
tags: ['Berry Dash', 'Leaderboards']
},
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
} }
) )
app.get( app.get(
'/berrydash/leaderboards/legacy', '/berrydash/leaderboards/legacy',
context => berrydashLeaderboardGetHandler(context, 3), ({ query, redirect }) => {
return redirect(
`/api/berrydash/leaderboard/legacy?${new URLSearchParams(
query
).toString()}`
)
},
{ {
detail: { detail: { hide: true }
deprecated: true,
description:
'This endpoint was renamed to `/berrydash/leaderboard/legacy` and will be removed on March 19th 2026.',
tags: ['Berry Dash', 'Leaderboards']
},
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
} }
) )
app.get( app.get(
'/berrydash/leaderboards/total', '/berrydash/leaderboards/total',
context => berrydashLeaderboardGetHandler(context, 4), ({ query, redirect }) => {
return redirect(
`/api/berrydash/leaderboard/total?${new URLSearchParams(
query
).toString()}`
)
},
{ {
detail: { detail: { hide: true }
deprecated: true,
description:
'This endpoint was renamed to `/berrydash/leaderboard/total` and will be removed on March 19th 2026.',
tags: ['Berry Dash', 'Leaderboards']
},
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
description:
'Ignore this header. It cannot be set or overridden and is required for endpoints to work properly'
})
)
})
} }
) )
app.get( app.get(
@@ -1005,25 +1361,6 @@ app.get(
}) })
} }
) )
app.get('/berrydash/profile', context => berrydashProfileGetHandler(context), {
detail: {
description:
"The endpoint for getting a user's profile.\n\n**Do not use this endpoint. It is the same as `/api/berrydash/account?id=(id)&exact=true` and will be removed on March 19th 2026.**" +
intNotStr('userId'),
tags: ['Berry Dash', 'Profiles'],
deprecated: true
},
query: t.Object({
userId: t.String()
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
hide: true
})
)
})
})
app.delete( app.delete(
'/berrydash/profile/posts', '/berrydash/profile/posts',
context => berrydashProfilePostsDeleteHandler(context), context => berrydashProfilePostsDeleteHandler(context),
+48 -25
View File
@@ -1,10 +1,12 @@
import { import {
bigint, bigint,
boolean, boolean,
char,
int, int,
longtext, longtext,
mysqlTable, mysqlTable,
text, text,
timestamp,
tinyint, tinyint,
varchar varchar
} from 'drizzle-orm/mysql-core' } from 'drizzle-orm/mysql-core'
@@ -23,24 +25,6 @@ export const users = mysqlTable('users', {
leaderboardsBanned: boolean('leaderboards_banned').default(false).notNull() leaderboardsBanned: boolean('leaderboards_banned').default(false).notNull()
}) })
export const launcherUpdates = mysqlTable('launcherupdates', {
id: varchar('id', { length: 24 }).primaryKey().notNull(),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
downloadUrls: text('downloadUrls').notNull(),
platforms: text('platforms').notNull(),
executables: text('executables').notNull(),
hidden: boolean('hidden').default(false).notNull(),
place: bigint('place', { mode: 'number' }).default(0).notNull(),
sha512sums: text('sha512sums').default('[]').notNull()
})
export const loaderUpdates = mysqlTable('loaderupdates', {
id: varchar('id', { length: 24 }).primaryKey().notNull(),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
hidden: boolean('hidden').default(false).notNull(),
place: bigint('place', { mode: 'number' }).default(0).notNull()
})
export const games = mysqlTable('games', { export const games = mysqlTable('games', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(), id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
name: text('name').notNull(), name: text('name').notNull(),
@@ -53,23 +37,23 @@ export const games = mysqlTable('games', {
export const launcherVersionManifest = mysqlTable('launcherversionmanifest', { export const launcherVersionManifest = mysqlTable('launcherversionmanifest', {
id: varchar('id', { length: 24 }).primaryKey().notNull(), id: varchar('id', { length: 24 }).primaryKey().notNull(),
displayName: text('displayName').notNull(), displayName: text('displayName').notNull(),
versionName: text('versionName').notNull(),
downloads: text('downloads').default('{}').notNull(),
executables: text('executables').default('[]').notNull(),
modSupportDownloads: text('modSupportDownloads').default('{}').notNull(),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(), releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
downloadUrlVersion: text('downloadUrlVersion').notNull(),
downloadUrls: text('downloadUrls').notNull(),
platforms: text('platforms').notNull(),
executables: text('executables').notNull(),
hidden: boolean('hidden').default(false).notNull(), hidden: boolean('hidden').default(false).notNull(),
game: bigint('game', { mode: 'number' }) game: bigint('game', { mode: 'number' })
.default(0) .default(0)
.references(() => games.id) .references(() => games.id)
.notNull(), .notNull(),
place: bigint('place', { mode: 'number' }).default(0).notNull(), place: bigint('place', { mode: 'number' }).default(0).notNull(),
sha512sums: text('sha512sums').default('[]').notNull(),
sizes: text('sizes').default('[]').notNull(),
changelog: text('changelog'), changelog: text('changelog'),
category: int('category').notNull().default(-1), category: int('category').notNull().default(-1),
lastRevision: bigint('lastRevision', { mode: 'number' }).notNull().default(0), lastRevision: bigint('lastRevision', { mode: 'number' }).notNull().default(0),
downloads: bigint('downloads', { mode: 'number' }).notNull().default(0) downloadCount: bigint('downloadCount', { mode: 'number' })
.notNull()
.default(0)
}) })
export const verifyCodes = mysqlTable('verifycodes', { export const verifyCodes = mysqlTable('verifycodes', {
@@ -94,6 +78,45 @@ export const resetCodes = mysqlTable('resetcodes', {
type: int('type').notNull() type: int('type').notNull()
}) })
export const files = mysqlTable('files', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
path: varchar('path', { length: 4096 }).notNull(),
hash: char('hash', { length: 128 }).notNull(),
size: bigint('size', { mode: 'number' }).notNull(),
host: int('host').notNull().default(0),
mimeType: varchar('mimeType', { length: 255 })
})
export const mods = mysqlTable('mods', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
name: text('name').notNull(),
creators: text('creators').default('{}').notNull(),
description: text('description').notNull(),
downloadCount: bigint('downloadCount', { mode: 'number' })
.notNull()
.default(0),
hidden: boolean('hidden').default(false).notNull()
})
export const modVersions = mysqlTable('modversions', {
id: text('id').primaryKey().notNull(),
downloads: text('downloads').default('{}').notNull(),
gameVersion: varchar('gameVersion', { length: 24 })
.notNull()
.references(() => launcherVersionManifest.id),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
changelog: text('changelog'),
hidden: boolean('hidden').default(false).notNull()
})
export const launcherUpdates = mysqlTable('launcherupdates', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
version: varchar('version', { length: 32 }).notNull(),
notes: text('notes').notNull(),
releaseDate: timestamp('release_date').notNull(),
downloadData: text('download_data').notNull()
})
// berrydashdatabase // berrydashdatabase
export const berryDashUserData = mysqlTable('userdata', { export const berryDashUserData = mysqlTable('userdata', {
+29 -15
View File
@@ -10,14 +10,28 @@ import { Context } from 'elysia'
import axios from 'axios' import axios from 'axios'
import FormData from 'form-data' import FormData from 'form-data'
import nodemailer from 'nodemailer' import nodemailer from 'nodemailer'
import { createHash } from 'crypto' import { createHash, createPrivateKey, sign } from 'node:crypto'
import { and, eq, sql } from 'drizzle-orm' import { and, eq, sql } from 'drizzle-orm'
import { verifyCodes } from './tables' import { verifyCodes } from './tables'
const privateKey = createPrivateKey({
key: Buffer.from(process.env.PRIVATE_SIGNING_KEY!, 'base64').toString('utf8'),
format: 'pem'
})
export const generateSignature = (body: string) => {
return sign(null, Buffer.from(body), privateKey).toString('base64')
}
export const jsonResponse = (data: any, status = 200) => { export const jsonResponse = (data: any, status = 200) => {
return new Response(JSON.stringify(data, null, 2), { const body = JSON.stringify(data)
return new Response(body, {
status, status,
headers: { 'Content-Type': 'application/json' } headers: {
'Content-Type': 'application/json',
'X-Signature': generateSignature(body)
}
}) })
} }
@@ -27,19 +41,19 @@ export const getDatabaseConnection = (type: number) => {
const env = const env =
type === 0 type === 0
? { ? {
host: process.env.GAMES_DB_HOST ?? 'localhost', host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.GAMES_DB_PORT) || 3306, port: Number(process.env.DB_PORT) || 3306,
user: process.env.GAMES_DB_USER ?? '', user: process.env.GAMES_DB_USER ?? '',
pass: process.env.GAMES_DB_PASS ?? '', pass: process.env.GAMES_DB_PASS ?? '',
name: process.env.GAMES_DB_NAME ?? '' name: process.env.GAMES_DB_NAME ?? ''
} }
: { : {
host: process.env.BERRYDASH_DB_HOST ?? 'localhost', host: process.env.DB_HOST ?? 'localhost',
port: Number(process.env.BERRYDASH_DB_PORT) || 3306, port: Number(process.env.DB_PORT) || 3306,
user: process.env.BERRYDASH_DB_USER ?? '', user: process.env.BERRYDASH_DB_USER ?? '',
pass: process.env.BERRYDASH_DB_PASS ?? '', pass: process.env.BERRYDASH_DB_PASS ?? '',
name: process.env.BERRYDASH_DB_NAME ?? '' name: process.env.BERRYDASH_DB_NAME ?? ''
} }
const connection = mysql.createConnection({ const connection = mysql.createConnection({
host: env.host, host: env.host,
+1 -1
View File
@@ -8,7 +8,7 @@ import { checkAuthorization } from '../../../lib/auth'
import { users } from '../../../lib/tables' import { users } from '../../../lib/tables'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
type Body = { type Body = {
newPassword: string newPassword: string
+1 -1
View File
@@ -7,7 +7,7 @@ import {
import { checkAuthorization } from '../../../lib/auth' import { checkAuthorization } from '../../../lib/auth'
import { users } from '../../../lib/tables' import { users } from '../../../lib/tables'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
type Body = { type Body = {
newUsername: string newUsername: string
+1 -1
View File
@@ -9,7 +9,7 @@ import {
import { resetCodes, users } from '../../../lib/tables' import { resetCodes, users } from '../../../lib/tables'
import { and, desc, eq, sql } from 'drizzle-orm' import { and, desc, eq, sql } from 'drizzle-orm'
import isEmail from 'validator/lib/isEmail' import isEmail from 'validator/lib/isEmail'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
type Body = { type Body = {
token: string | null token: string | null
+4 -2
View File
@@ -27,7 +27,8 @@ export const handler = async (context: Context) => {
id: users.id, id: users.id,
username: users.username, username: users.username,
password: users.password, password: users.password,
token: users.token token: users.token,
admin: users.admin
}) })
.from(users) .from(users)
.where(eq(users.username, body.username)) .where(eq(users.username, body.username))
@@ -61,7 +62,8 @@ export const handler = async (context: Context) => {
data: { data: {
session: user[0].token, session: user[0].token,
username: user[0].username, username: user[0].username,
id: user[0].id id: user[0].id,
admin: user[0].admin
} }
}) })
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import isEmail from 'validator/lib/isEmail' import isEmail from 'validator/lib/isEmail'
import { berryDashUserData, users } from '../../../lib/tables' import { berryDashUserData, users } from '../../../lib/tables'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
type Body = { type Body = {
token: string | null token: string | null
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import { resetCodes, users } from '../../../lib/tables' import { resetCodes, users } from '../../../lib/tables'
import { and, desc, eq, sql } from 'drizzle-orm' import { and, desc, eq, sql } from 'drizzle-orm'
import bcrypt from 'bcryptjs' import bcrypt from 'bcryptjs'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
type Body = { type Body = {
token: string | null token: string | null
+2 -2
View File
@@ -10,7 +10,7 @@ export const handler = async () => {
const version = await db const version = await db
.select({ .select({
downloadUrlVersion: launcherVersionManifest.downloadUrlVersion versionName: launcherVersionManifest.versionName
}) })
.from(launcherVersionManifest) .from(launcherVersionManifest)
.where( .where(
@@ -25,5 +25,5 @@ export const handler = async () => {
connection.end() connection.end()
return version[0] ? version[0].downloadUrlVersion : '-1' return version[0] ? version[0].versionName : '-1'
} }
+11 -3
View File
@@ -1,4 +1,8 @@
import { getDatabaseConnection, jsonResponse } from '../../../lib/util' import {
generateSignature,
getDatabaseConnection,
jsonResponse
} from '../../../lib/util'
import { berryDashSplashTexts } from '../../../lib/tables' import { berryDashSplashTexts } from '../../../lib/tables'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { Context } from 'elysia' import { Context } from 'elysia'
@@ -40,9 +44,13 @@ export const handler = async (context: Context) => {
}) })
.map(i => atob(i.content)) .map(i => atob(i.content))
return new Response(data.join('\n'), { const body = data.join('\n')
return new Response(body, {
status: 200, status: 200,
headers: { 'Content-Type': 'text/plain' } headers: {
'Content-Type': 'text/plain',
'X-Signature': generateSignature(body)
}
}) })
} else { } else {
return jsonResponse( return jsonResponse(
+13 -1
View File
@@ -11,7 +11,19 @@ export const handler = async ({ request, set }: Context) => {
const requester = request.headers.get('Requester') ?? '0' const requester = request.headers.get('Requester') ?? '0'
const clientVersion = request.headers.get('ClientVersion') ?? '0' const clientVersion = request.headers.get('ClientVersion') ?? '0'
if (requester === 'BerryDashGodotClient') return 'all;1.0.0' if (requester === 'BerryDashGodotClient') {
const allowedVersions = ["1.0.0", "1.1.0"]
let permissions = "all"
if (allowedVersions.indexOf(clientVersion) !== -1) {
permissions = "all"
} else {
permissions = "client"
}
return permissions + ';' + allowedVersions[allowedVersions.length - 1]
}
else if (isLatestVersion(clientVersion)) return '1' else if (isLatestVersion(clientVersion)) return '1'
else if (isBetaVersion(clientVersion)) return '4' else if (isBetaVersion(clientVersion)) return '4'
else if ( else if (
+1 -1
View File
@@ -5,7 +5,7 @@ import {
jsonResponse, jsonResponse,
validateTurnstile validateTurnstile
} from '../lib/util' } from '../lib/util'
import { randomBytes } from 'crypto' import { randomBytes } from 'node:crypto'
import { verifyCodes } from '../lib/tables' import { verifyCodes } from '../lib/tables'
import { and, desc, eq, sql } from 'drizzle-orm' import { and, desc, eq, sql } from 'drizzle-orm'
-21
View File
@@ -1,21 +0,0 @@
import { Context } from 'elysia'
import { launcherVersionManifest } from '../../lib/tables'
import { getDatabaseConnection } from '../../lib/util'
import { eq, sql } from 'drizzle-orm'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult) return null
const { connection, db } = dbResult
await db
.update(launcherVersionManifest)
.set({
downloads: sql`${launcherVersionManifest.downloads} + 1`
})
.where(eq(launcherVersionManifest.id, context.query.id))
connection.end()
return null
}
+48
View File
@@ -0,0 +1,48 @@
import { Context } from 'elysia'
import { files, launcherVersionManifest, mods } from '../../../lib/tables'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { eq, sql } from 'drizzle-orm'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult) return jsonResponse({ success: false, data: null }, 400)
const { connection, db } = dbResult
const versionId = context.query.versionId
const modId = context.query.modId
const downloadId = Number(context.query.downloadId)
const result = await db
.select({ path: files.path, hash: files.hash, host: files.host })
.from(files)
.where(eq(files.id, downloadId))
.execute()
if (result.length != 1)
return jsonResponse({ success: false, data: null }, 400)
if (versionId)
await db
.update(launcherVersionManifest)
.set({ downloadCount: sql`${launcherVersionManifest.downloadCount} + 1` })
.where(eq(launcherVersionManifest.id, versionId))
if (modId)
await db
.update(mods)
.set({ downloadCount: sql`${mods.downloadCount} + 1` })
.where(eq(mods.id, Number(modId)))
connection.end()
return jsonResponse({
success: true,
data: {
url:
result[0].host == 1
? 'https://github.com/' + result[0].path
: 'https://archive.org/download/lncvrt-games-archive/' + result[0].path,
hash: result[0].hash
}
})
}
-23
View File
@@ -1,23 +0,0 @@
import { launcherUpdates } from '../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../lib/util'
export const handler = async () => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const version = await db
.select({
id: launcherUpdates.id
})
.from(launcherUpdates)
.where(eq(launcherUpdates.hidden, false))
.orderBy(desc(launcherUpdates.place))
.limit(1)
connection.end()
return version[0].id
}
+31
View File
@@ -0,0 +1,31 @@
import { launcherUpdates } from '../../../lib/tables'
import { desc } from 'drizzle-orm'
import {
generateSignature,
getDatabaseConnection,
jsonResponse
} from '../../../lib/util'
export const handler = async () => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const version = await db
.select({
version: launcherUpdates.version
})
.from(launcherUpdates)
.orderBy(desc(launcherUpdates.id))
.limit(1)
connection.end()
return new Response(version[0].version, {
headers: {
'Content-Type': 'text/plain',
'X-Signature': generateSignature(version[0].version)
}
})
}
-23
View File
@@ -1,23 +0,0 @@
import { loaderUpdates } from '../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
export const handler = async () => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const version = await db
.select({
id: loaderUpdates.id
})
.from(loaderUpdates)
.where(eq(loaderUpdates.hidden, false))
.orderBy(desc(loaderUpdates.place))
.limit(1)
connection.end()
return version[0].id
}
+3
View File
@@ -0,0 +1,3 @@
export const handler = async () => {
return '-1'
}
-101
View File
@@ -1,101 +0,0 @@
import { launcherUpdates } from '../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const platform = context.query.platform as string | undefined
const arch = context.query.arch as string | undefined
let showAll = false
if (!platform || !arch) {
showAll = true
}
let platString = null
if (!showAll) {
if (platform == 'windows') {
if (arch == 'x86_64') platString = 'windows'
else if (arch == 'aarch64') platString = 'windows-arm64'
else {
connection.end()
return jsonResponse(
{ error: 'Unsupported architecture for Windows' },
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86_64') platString = 'linux'
else {
connection.end()
return jsonResponse(
{ error: 'Unsupported architecture for Linux' },
400
)
}
} else if (platform == 'macos') {
if (arch == 'x86_64') platString = 'macos-intel'
else if (arch == 'aarch64') platString = 'macos-silicon'
else {
connection.end()
return jsonResponse(
{ error: 'Unsupported architecture for macOS' },
400
)
}
} else {
connection.end()
return jsonResponse({ error: 'Unsupported platform' }, 400)
}
}
const versionsRaw = await db
.select({
id: launcherUpdates.id,
releaseDate: launcherUpdates.releaseDate,
downloadUrls: launcherUpdates.downloadUrls,
platforms: launcherUpdates.platforms,
sha512sums: launcherUpdates.sha512sums
})
.from(launcherUpdates)
.where(eq(launcherUpdates.hidden, false))
.orderBy(desc(launcherUpdates.place))
.limit(1)
.execute()
const versions = versionsRaw
.map(v => ({
...v,
downloadUrls: JSON.parse(v.downloadUrls),
platforms: JSON.parse(v.platforms),
sha512sums: JSON.parse(v.sha512sums),
downloadUrl: undefined as string | undefined,
sha512sum: undefined as string | undefined
}))
.filter(v => {
if (showAll || !platString) {
delete v.downloadUrl
delete v.sha512sum
return true
}
const i = v.platforms.indexOf(platString)
if (i !== -1) {
v.downloadUrl = v.downloadUrls[i]
v.sha512sum = v.sha512sums[i]
delete v.downloadUrls
delete v.platforms
delete v.sha512sums
return true
}
return false
})
connection.end()
return jsonResponse(versions[0])
}
+152
View File
@@ -0,0 +1,152 @@
import { mods, modVersions, users } from '../../../lib/tables'
import { and, asc, desc, eq, inArray, like } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const clientplatform = context.headers.clientplatform
const platform = clientplatform?.split('-')[0]
const arch = clientplatform?.split('-')[1]
const version = context.query.version
let platformsToShow: string[] | null = null
if (platform == 'windows') {
if (arch == 'x86') platformsToShow = ['windows-x86']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['windows-x64', 'windows-x86']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['windows-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Windows',
versions: null,
games: null
},
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86') platformsToShow = ['linux-x64']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['linux-x64', 'linux-x86', 'windows-x64', 'windows-x86']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['linux-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Linux',
versions: null,
games: null
},
400
)
}
} else if (platform == 'macos') {
if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['macos-x64', 'macos-universal']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['macos-arm64', 'macos-universal', 'macos-x64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for macOS',
versions: null,
games: null
},
400
)
}
} else if (platform == 'android') platformsToShow = ['android']
else if (platform == 'ios') platformsToShow = ['ios']
else {
connection.end()
return jsonResponse(
{ message: 'Unsupported platform', versions: null, games: null },
400
)
}
const modsRaw = await db
.select({
id: mods.id,
name: mods.name,
creators: mods.creators,
description: mods.description,
downloadCount: mods.downloadCount
})
.from(mods)
.where(eq(mods.hidden, false))
.orderBy(asc(mods.downloadCount), asc(mods.id))
.execute()
const modsFinal = (
await Promise.all(
modsRaw.map(async v => {
const vi = await db
.select({
id: modVersions.id,
downloads: modVersions.downloads,
gameVersion: modVersions.gameVersion,
releaseDate: modVersions.releaseDate,
changelog: modVersions.changelog
})
.from(modVersions)
.where(
and(
eq(modVersions.hidden, false),
eq(modVersions.gameVersion, version),
like(modVersions.id, `${v.id}%`)
)
)
.orderBy(desc(modVersions.releaseDate))
.execute()
if (!vi || vi.length < 1) return
const downloads: Record<string, number> = JSON.parse(vi[0].downloads)
let download: string | undefined = undefined
for (const key of platformsToShow) {
if (key in downloads) {
download = key
break
}
}
if (!download || !downloads[download]) return
const creators = await db
.select({ username: users.username })
.from(users)
.where(inArray(users.id, JSON.parse(v.creators)))
.execute()
return {
id: v.id,
name: v.name,
description: v.description,
creators: creators.map(u => u.username),
downloads: v.downloadCount,
released: vi[vi.length - 1].releaseDate,
updated: vi[0].releaseDate,
latestVersion: vi[0].id.substring(String(v.id).length + 1),
latestDownload: downloads[download],
changelog: vi[0].changelog
}
})
)
).filter(v => v != null)
connection.end()
return jsonResponse(modsFinal)
}
+55
View File
@@ -0,0 +1,55 @@
import { files, launcherUpdates } from '../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult) return
const { connection, db } = dbResult
const target = context.query.target
const arch = context.query.arch ?? undefined
// const currentVersion = context.query.currentVersion
const versionsData = await db
.select({
version: launcherUpdates.version,
notes: launcherUpdates.notes,
releaseDate: launcherUpdates.releaseDate,
downloadData: launcherUpdates.downloadData
})
.from(launcherUpdates)
.orderBy(desc(launcherUpdates.id))
.limit(1)
.execute()
if (versionsData.length != 1) return
const downloadData = JSON.parse(versionsData[0].downloadData)[
(target.split('-')[0] == 'linux' && arch ? target + '-' + arch : target)
.replace('windows', 'win')
.replace('darwin', 'macos')
.replace('x86_64', 'x64')
.replace('aarch64', 'arm64')
.replace('fedora', 'redhat')
]
if (!downloadData) return
const fileInfo = await db
.select({ path: files.path })
.from(files)
.where(eq(files.id, downloadData.download))
if (fileInfo.length != 1) return
connection.end()
return jsonResponse({
version: versionsData[0].version,
notes: versionsData[0].notes,
pub_date: versionsData[0].releaseDate,
url: 'https://archive.org/download/lncvrt-games-archive/' + fileInfo[0].path,
signature: downloadData.signature
})
}
-177
View File
@@ -1,177 +0,0 @@
import { games, launcherVersionManifest } from '../../lib/tables'
import { asc, desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const platform = context.query.platform as string | undefined
const arch = context.query.arch as string | undefined
let showAll = false
if (!platform || !arch) {
showAll = true
}
let platString = null
if (!showAll) {
if (platform == 'windows') {
if (arch == 'x86_64' || arch == 'x64') platString = 'windows'
else if (arch == 'aarch64' || arch == 'arm64')
platString = 'windows-arm64'
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Windows',
versions: null,
games: null
},
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86_64' || arch == 'x64') platString = 'linux'
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Linux',
versions: null,
games: null
},
400
)
}
} else if (platform == 'macos') {
if (arch == 'x86_64' || arch == 'x64') platString = 'macos'
else if (arch == 'aarch64' || arch == 'arm64') platString = 'macos'
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for macOS',
versions: null,
games: null
},
400
)
}
} else if (platform == 'android') platString = 'android'
else if (platform == 'ios') platString = 'ios'
else {
connection.end()
return jsonResponse(
{ message: 'Unsupported platform', versions: null, games: null },
400
)
}
}
const versionsRaw = await db
.select({
id: launcherVersionManifest.id,
displayName: launcherVersionManifest.displayName,
releaseDate: launcherVersionManifest.releaseDate,
game: launcherVersionManifest.game,
downloadUrlVersion: launcherVersionManifest.downloadUrlVersion,
downloadUrls: launcherVersionManifest.downloadUrls,
platforms: launcherVersionManifest.platforms,
executables: launcherVersionManifest.executables,
sha512sums: launcherVersionManifest.sha512sums,
sizes: launcherVersionManifest.sizes,
place: launcherVersionManifest.place,
changelog: launcherVersionManifest.changelog,
category: launcherVersionManifest.category,
lastRevision: launcherVersionManifest.lastRevision
})
.from(launcherVersionManifest)
.where(eq(launcherVersionManifest.hidden, false))
.orderBy(
asc(launcherVersionManifest.game),
desc(launcherVersionManifest.place)
)
.execute()
const versions = versionsRaw
.map(v => ({
...v,
downloadUrls: JSON.parse(
v.downloadUrls.replaceAll('%version%', v.downloadUrlVersion)
),
platforms: JSON.parse(v.platforms),
executables: JSON.parse(v.executables),
sha512sums: JSON.parse(v.sha512sums),
sizes: JSON.parse(v.sizes),
downloadUrlVersion: undefined,
downloadUrl: undefined as string | undefined,
executable: undefined as string | undefined,
sha512sum: undefined as string | undefined,
size: undefined as number | undefined,
wine: undefined as boolean | undefined
}))
.filter(v => {
delete v.downloadUrlVersion
if (showAll || !platString) {
delete v.downloadUrl
delete v.executable
delete v.sha512sum
delete v.size
delete v.wine
return true
}
let i = v.platforms.indexOf(platString)
if (i !== -1) {
v.downloadUrl = v.downloadUrls[i]
v.executable = v.executables[i]
v.sha512sum = v.sha512sums[i]
v.size = v.sizes[i]
delete v.downloadUrls
delete v.platforms
delete v.executables
delete v.sha512sums
delete v.sizes
delete v.wine
return true
} else if (platString == 'linux') {
i = v.platforms.indexOf('windows')
if (i !== -1) {
v.downloadUrl = v.downloadUrls[i]
v.executable = v.executables[i]
v.sha512sum = v.sha512sums[i]
v.size = v.sizes[i]
v.wine = true
delete v.downloadUrls
delete v.platforms
delete v.executables
delete v.sha512sums
delete v.sizes
return true
}
}
return false
})
const gamesListRaw = await db.select().from(games).execute()
const gamesList = gamesListRaw.map(v => ({
...v,
categoryNames: JSON.parse(v.categoryNames)
}))
connection.end()
const seenGames = new Set<number>()
const finalVersions = versions.filter(v => {
if (platString !== 'android' && platString !== 'ios') return true
if (seenGames.has(v.game)) return false
seenGames.add(v.game)
return true
})
return jsonResponse({ versions: finalVersions, games: gamesList })
}
+177
View File
@@ -0,0 +1,177 @@
import { games, launcherVersionManifest } from '../../../lib/tables'
import { and, asc, desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const authorization =
context.headers.authorization?.trim() == '' ||
context.headers.authorization == null
? null
: context.headers.authorization
const clientplatform = context.headers.clientplatform
const platform = clientplatform?.split('-')[0]
const arch = clientplatform?.split('-')[1]
let showAll = false
if (!platform || !arch) showAll = true
let platformsToShow: string[] | null = null
if (!showAll) {
if (platform == 'windows') {
if (arch == 'x86') platformsToShow = ['windows-x86']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['windows-x64', 'windows-x86']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['windows-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Windows',
versions: null,
games: null
},
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86') platformsToShow = ['linux-x86']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = [
'linux-x64',
'linux-x86',
'windows-x64',
'windows-x86'
]
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['linux-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Linux',
versions: null,
games: null
},
400
)
}
} else if (platform == 'macos') {
if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['macos-x64', 'macos-universal']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['macos-arm64', 'macos-universal', 'macos-x64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for macOS',
versions: null,
games: null
},
400
)
}
} else if (platform == 'android') platformsToShow = ['android']
else if (platform == 'ios') platformsToShow = ['ios']
else {
connection.end()
return jsonResponse(
{ message: 'Unsupported platform', versions: null, games: null },
400
)
}
}
const versionsRaw = await db
.select({
id: launcherVersionManifest.id,
displayName: launcherVersionManifest.displayName,
releaseDate: launcherVersionManifest.releaseDate,
game: launcherVersionManifest.game,
versionName: launcherVersionManifest.versionName,
downloads: launcherVersionManifest.downloads,
executables: launcherVersionManifest.executables,
modSupportDownloads: launcherVersionManifest.modSupportDownloads,
place: launcherVersionManifest.place,
changelog: launcherVersionManifest.changelog,
category: launcherVersionManifest.category,
lastRevision: launcherVersionManifest.lastRevision
})
.from(launcherVersionManifest)
.where(eq(launcherVersionManifest.hidden, false))
.orderBy(
asc(launcherVersionManifest.game),
desc(launcherVersionManifest.place)
)
.execute()
const versions = versionsRaw
.map(v => {
const downloads: Record<string, number> = JSON.parse(v.downloads)
const executables: (string | null)[] = JSON.parse(v.executables)
const modSupportDownload: Record<string, number> = JSON.parse(
v.modSupportDownloads
)
let download: string | undefined = undefined
let executable: string | null | undefined = undefined
if (platformsToShow) {
const downloadKeys = Object.keys(downloads)
for (const key of platformsToShow) {
if (key in downloads) {
download = key
const idx = downloadKeys.indexOf(key)
executable = executables[idx] ?? null
break
}
}
}
const { versionName, ...rest } = v
return {
...rest,
executables: platformsToShow ? undefined : executables,
executable: platformsToShow ? executable : undefined,
downloads: platformsToShow ? undefined : downloads,
modSupportDownloads: platformsToShow ? undefined : modSupportDownload,
download: platformsToShow
? download
? downloads[download]
: undefined
: undefined,
modSupportDownload: platformsToShow
? download
? modSupportDownload[download]
: undefined
: undefined,
wine:
platformsToShow &&
platform == 'linux' &&
download?.startsWith('windows')
? true
: undefined
}
})
.filter(v => {
if (!platformsToShow) return true
return v.download !== undefined
})
const gamesListRaw = await db.select().from(games).execute()
const gamesList = gamesListRaw.map(v => ({
...v,
categoryNames: JSON.parse(v.categoryNames)
}))
connection.end()
return jsonResponse({ versions: versions, games: gamesList })
}