added arkjet :D
This commit is contained in:
16
env.example
Normal file
16
env.example
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
PORT=8082
|
||||||
|
HOST=0.0.0.0
|
||||||
|
DATABASE_URL="postgresql://adm_cowch:Bluish-Tycoon4-Cuddly@localhost:5432/websocketTutorial?sslmode=require&channel_binding=require"
|
||||||
|
|
||||||
|
DATABASE_HOST=localhost
|
||||||
|
DATABASE_PORT=5432
|
||||||
|
DATABASE_USER=adm_cowch
|
||||||
|
DATABASE_PASSWORD=Bluish-Tycoon4-Cuddly
|
||||||
|
DATABASE_DB=websocketTutorial
|
||||||
|
|
||||||
|
# Run Arcjet in development <https://docs.arcjet.com/environment#arcjet-env>.
|
||||||
|
ARCJET_ENV=development
|
||||||
|
# Arcjet key for your site (from <https://app.arcjet.com>).
|
||||||
|
# More info: <https://docs.arcjet.com/environment#arcjet-key>.
|
||||||
|
ARCJET_KEY="ajkey_01kgraw410ey49fv7r7jzxbzcc"
|
||||||
|
ARCJET_MODE=""
|
||||||
256
package-lock.json
generated
256
package-lock.json
generated
@@ -9,6 +9,8 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@arcjet/inspect": "^1.1.0",
|
||||||
|
"@arcjet/node": "^1.1.0",
|
||||||
"@dotenvx/dotenvx": "^1.52.0",
|
"@dotenvx/dotenvx": "^1.52.0",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
@@ -28,6 +30,182 @@
|
|||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@arcjet/analyze": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/analyze/-/analyze-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-rbgjTjk4PvoRh8wweerD5Qr0G/xar0XWNiRTvzcmrnJofuhESLjWD87p3Y3Od307gzex8Whtu+jA0LvII1H4DA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/analyze-wasm": "1.1.0",
|
||||||
|
"@arcjet/protocol": "1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/analyze-wasm": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/analyze-wasm/-/analyze-wasm-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CrzSBa9rQFQRfIUofm2FuwVjlnCdWK8hQz3uxbMqYFFLeGPAQftM2noHM7P2p+2Rq6j1p6u462RIpjg2Y2rhXQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/body": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/body/-/body-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ijMcy71ezPtu9aj29Cde6McBd634ugtqD26ufF8r/fb7YH/eYsWJQS4LZBnkEdgangAfb2VO1VEUpKwGSyZmqA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/cache": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/cache/-/cache-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-TFt5PdlQVOngGTL+Q02NLGtv6Ks15m68a5Jksk9rEDM8vVzwoFE6KXawPNPm0pcwow4a/C+GcYwYprDm7MpXlw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/duration": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/duration/-/duration-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-22MVyEnJgRv4RpFol6LefKAjh2ei7ekBIkawPZi+F0YNQtGPTTJP7r+gLT615oUcZFptBxiuogsXk6zhFycSsA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/env/-/env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-q6GLj4HRBJZy12EHtqre1ihvSJMikl5jwenzIMjBkkAIuyl0bpOpMpnIuaP5bJ4dkVLTpsicIcEYn6aFGZsXOA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/headers": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/headers/-/headers-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-V4PVy84gxc30g8CGZaLl//QAogdaAKJ2Sbtjccd5iUmxeUXXdqW3L0/xwm0xCebwB8rxojwhG7CzD3uv781Zdw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/inspect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/inspect/-/inspect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-yaBGbG/iwfpR/mfyiomLGzePjUr7YFzLi2qss/TjDLcX6TvQ4nW+HqOidGzAE5IVqdtl13TG9vD2f2tv9AkIog==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/protocol": "1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/ip": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/ip/-/ip-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-mxOqZ/EVhO3bN7NBr36yURyFLE9KEXUzW+Pbq3U9HogqjMoNA4g3rkomVo3cNWhJcNCHNMRKplWbFsvVuyRdHQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/logger": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/logger/-/logger-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-QQkDpdevbDOK8Ojy7REQap6y3J0fC1/5D/TE7hlC41Y2Gz6oyXIYzbLxEoyCYkfzMeOnMthHAXXwzI+E2gvdgg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/sprintf": "1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/node": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/node/-/node-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-89WpKP3xwvpLuKPhN7hWMAER76EnI6f5tUydVMm+LJF1TKJcZt80L91vEO4nTQU+is28BR57P4/IF7q+voDQSQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/body": "1.1.0",
|
||||||
|
"@arcjet/env": "1.1.0",
|
||||||
|
"@arcjet/headers": "1.1.0",
|
||||||
|
"@arcjet/ip": "1.1.0",
|
||||||
|
"@arcjet/logger": "1.1.0",
|
||||||
|
"@arcjet/protocol": "1.1.0",
|
||||||
|
"@arcjet/transport": "1.1.0",
|
||||||
|
"arcjet": "1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/protocol": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/protocol/-/protocol-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-evjUsyOh04bQgH9lWm/xIyqF3C8XkiKvRa/QYuUvMYEpe/J1hndHaOZqzqlzQqkm44cbM5l8s+06SpHyaG/iFg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/cache": "1.1.0",
|
||||||
|
"@bufbuild/protobuf": "2.11.0",
|
||||||
|
"@connectrpc/connect": "2.1.1",
|
||||||
|
"typeid-js": "1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/runtime": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/runtime/-/runtime-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-v/7I9n3l9ZHBWGOoSBIxhEZ5WhcLPToWWXz8HBn30H1UFzholJCSDvIKesTogSt/EmxEUVDwxnHFZxZh+EX9Zw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/sprintf": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/sprintf/-/sprintf-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-YA19YrUYnEPxVPS5O41MlkynVLEtBVeu/eqrXGckQHmS7PuyNZEO2h8ZU+6G+ppua9igZUEzgYFVIh6UJRjWpQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/stable-hash": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/stable-hash/-/stable-hash-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-JdeLw2r7f4sJKRi48BAypjcqgwlMf8LgcVZs3xH+CjE0N9DAxkl1AY/6Wt03WOz4S0FahC/amzCVNMJ6TlnQfA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@arcjet/transport": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@arcjet/transport/-/transport-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-nv/9HSroy4UxndxFYF39St4t8yXTTKGnaKXqPDhRGBHwPGPMLvLHxvXD4bguDWu5iMSKGazsJFJx/RD/7SwG9Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@bufbuild/protobuf": "2.11.0",
|
||||||
|
"@connectrpc/connect": "2.1.1",
|
||||||
|
"@connectrpc/connect-node": "2.1.1",
|
||||||
|
"@connectrpc/connect-web": "2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
"node_modules/@biomejs/biome": {
|
||||||
"version": "2.3.13",
|
"version": "2.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.13.tgz",
|
||||||
@@ -191,6 +369,44 @@
|
|||||||
"node": ">=14.21.3"
|
"node": ">=14.21.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@bufbuild/protobuf": {
|
||||||
|
"version": "2.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
|
||||||
|
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
|
||||||
|
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||||
|
},
|
||||||
|
"node_modules/@connectrpc/connect": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-JzhkaTvM73m2K1URT6tv53k2RwngSmCXLZJgK580qNQOXRzZRR/BCMfZw3h+90JpnG6XksP5bYT+cz0rpUzUWQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@bufbuild/protobuf": "^2.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@connectrpc/connect-node": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@connectrpc/connect-node/-/connect-node-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-s3TfsI1XF+n+1z6MBS9rTnFsxxR4Rw5wmdEnkQINli81ESGxcsfaEet8duzq8LVuuCupmhUsgpRo0Nv9pZkufg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@bufbuild/protobuf": "^2.7.0",
|
||||||
|
"@connectrpc/connect": "2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@connectrpc/connect-web": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@connectrpc/connect-web/-/connect-web-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-J8317Q2MaFRCT1jzVR1o06bZhDIBmU0UAzWx6xOIXzOq8+k71/+k7MUF7AwcBUX+34WIvbm5syRgC5HXQA8fOg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@bufbuild/protobuf": "^2.7.0",
|
||||||
|
"@connectrpc/connect": "2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@dotenvx/dotenvx": {
|
"node_modules/@dotenvx/dotenvx": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.52.0.tgz",
|
||||||
@@ -1215,6 +1431,24 @@
|
|||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/arcjet": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/arcjet/-/arcjet-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-YRzr8kfTLdSdp45W5Xrzf1+TMAvDYELD2D2Gg6jXgLZl0PjM/SoK3TMCTz/CMxohBx2AV17/wSM24YjcDEoHcg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@arcjet/analyze": "1.1.0",
|
||||||
|
"@arcjet/cache": "1.1.0",
|
||||||
|
"@arcjet/duration": "1.1.0",
|
||||||
|
"@arcjet/headers": "1.1.0",
|
||||||
|
"@arcjet/protocol": "1.1.0",
|
||||||
|
"@arcjet/runtime": "1.1.0",
|
||||||
|
"@arcjet/stable-hash": "1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
||||||
@@ -3211,6 +3445,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typeid-js": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/typeid-js/-/typeid-js-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-t76ZucAnvGC60ea/HjVsB0TSoB0cw9yjnfurUgtInXQWUI/VcrlZGpO23KN3iSe8yOGUgb1zr7W7uEzJ3hSljA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": "^10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
@@ -3241,6 +3484,19 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@arcjet/inspect": "^1.1.0",
|
||||||
|
"@arcjet/node": "^1.1.0",
|
||||||
"@dotenvx/dotenvx": "^1.52.0",
|
"@dotenvx/dotenvx": "^1.52.0",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import http from "http";
|
|||||||
// routes
|
// routes
|
||||||
import { matchRouter } from "./routes/matches.route.js";
|
import { matchRouter } from "./routes/matches.route.js";
|
||||||
import { attachWebsocketServer } from "./ws/server.js";
|
import { attachWebsocketServer } from "./ws/server.js";
|
||||||
|
import { securityMiddleware } from "./utils/arkjet.js";
|
||||||
|
|
||||||
const PORT = process.env.PORT || 8081;
|
const PORT = process.env.PORT || 8081;
|
||||||
const HOST = process.env.HOST || "0.0.0.0";
|
const HOST = process.env.HOST || "0.0.0.0";
|
||||||
@@ -18,6 +19,9 @@ app.get("/", (_, res) => {
|
|||||||
res.send("Hello from express server!");
|
res.send("Hello from express server!");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// middleware
|
||||||
|
app.use(securityMiddleware());
|
||||||
|
|
||||||
app.use("/matches", matchRouter);
|
app.use("/matches", matchRouter);
|
||||||
|
|
||||||
const { broadcastMatchCreated } = attachWebsocketServer(server);
|
const { broadcastMatchCreated } = attachWebsocketServer(server);
|
||||||
|
|||||||
57
src/utils/arkjet.js
Normal file
57
src/utils/arkjet.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import arcjet, { detectBot, shield, slidingWindow } from "@arcjet/node";
|
||||||
|
|
||||||
|
const arcjetKey = process.env.ARCJET_KEY;
|
||||||
|
const arcjetMODE = process.env.ARCJET_MODE === "DRY_RUN" ? "DRY_RUN" : "LIVE";
|
||||||
|
|
||||||
|
if (!arcjetKey) throw new Error("ARKJET_KEY environment variable is missing.");
|
||||||
|
|
||||||
|
export const httpArkjet = arcjetKey
|
||||||
|
? arcjet({
|
||||||
|
key: arcjetKey,
|
||||||
|
rules: [
|
||||||
|
shield({ mode: arcjetMODE }),
|
||||||
|
detectBot({
|
||||||
|
mode: arcjetMODE,
|
||||||
|
allow: ["CATEGORY:SEARCH_ENGINE", "CATEGORY:PREVIEW"],
|
||||||
|
}),
|
||||||
|
slidingWindow({ mode: arcjetMODE, interval: "10s", max: 50 }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
export const wsArkjet = arcjetKey
|
||||||
|
? arcjet({
|
||||||
|
key: arcjetKey,
|
||||||
|
rules: [
|
||||||
|
shield({ mode: arcjetMODE }),
|
||||||
|
detectBot({
|
||||||
|
mode: arcjetMODE,
|
||||||
|
allow: ["CATEGORY:SEARCH_ENGINE", "CATEGORY:PREVIEW"],
|
||||||
|
}),
|
||||||
|
slidingWindow({ mode: arcjetMODE, interval: "2s", max: 5 }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
export const securityMiddleware = () => {
|
||||||
|
return async (req, res, next) => {
|
||||||
|
if (!httpArkjet) return next;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decision = await httpArkjet.protect(req);
|
||||||
|
|
||||||
|
if (decision.isDenied()) {
|
||||||
|
if (decision.reason.isRateLimit()) {
|
||||||
|
return res.status(429).json({ error: "Too Many request" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(403).json({ error: "Forbidden" });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("ArkJet middleware error", e);
|
||||||
|
res.status(503).json({ error: "Service Unavailable" });
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { WebSocket, WebSocketServer } from "ws";
|
import { WebSocket, WebSocketServer } from "ws";
|
||||||
|
import { wsArkjet } from "../utils/arkjet.js";
|
||||||
|
|
||||||
const sendJson = (socket, payload) => {
|
const sendJson = (socket, payload) => {
|
||||||
if (socket.readyState !== WebSocket.OPEN) {
|
if (socket.readyState !== WebSocket.OPEN) {
|
||||||
@@ -10,7 +11,7 @@ const sendJson = (socket, payload) => {
|
|||||||
|
|
||||||
const broadcast = (wss, payload) => {
|
const broadcast = (wss, payload) => {
|
||||||
for (const client of wss.clients) {
|
for (const client of wss.clients) {
|
||||||
if (client.readyState !== WebSocket.OPEN) return;
|
if (client.readyState !== WebSocket.OPEN) continue;
|
||||||
|
|
||||||
client.send(JSON.stringify(payload));
|
client.send(JSON.stringify(payload));
|
||||||
}
|
}
|
||||||
@@ -23,12 +24,48 @@ export const attachWebsocketServer = (server) => {
|
|||||||
maxPayload: 1024 * 1024, // 1mb
|
maxPayload: 1024 * 1024, // 1mb
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on("connection", (socket) => {
|
wss.on("connection", async (socket, req) => {
|
||||||
|
if (wsArkjet) {
|
||||||
|
try {
|
||||||
|
const desision = await wsArkjet.protect(req);
|
||||||
|
|
||||||
|
if (desision.isDenied) {
|
||||||
|
const code = desision.reason.isRateLimit() ? 1013 : 1008;
|
||||||
|
const reason = desision.reason.isRateLimit()
|
||||||
|
? "Rate limit exceedeed"
|
||||||
|
: "Access denied";
|
||||||
|
|
||||||
|
socket.close(code, reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("WS connection error", e);
|
||||||
|
socket.close(1011, "Server security error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.isAlive = true;
|
||||||
|
socket.on("pong", () => {
|
||||||
|
socket.isAlive = true;
|
||||||
|
});
|
||||||
sendJson(socket, { type: "welcome" });
|
sendJson(socket, { type: "welcome" });
|
||||||
|
|
||||||
socket.on("error", console.error);
|
socket.on("error", console.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
wss.clients.forEach((ws) => {
|
||||||
|
if (ws.isAlive === false) {
|
||||||
|
ws.terminate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ws.isAlive = false;
|
||||||
|
ws.ping();
|
||||||
|
});
|
||||||
|
}, 30 * 1000);
|
||||||
|
|
||||||
|
wss.on("close", () => clearInterval(interval));
|
||||||
|
|
||||||
function broadcastMatchCreated(match) {
|
function broadcastMatchCreated(match) {
|
||||||
broadcast(wss, { type: "match_created", data: match });
|
broadcast(wss, { type: "match_created", data: match });
|
||||||
}
|
}
|
||||||
|
|||||||
56
testScripts/testRateLimits.js
Normal file
56
testScripts/testRateLimits.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Import the built-in HTTP module from Node.js
|
||||||
|
// This lets us make HTTP requests without curl or extra packages
|
||||||
|
import http from "http";
|
||||||
|
|
||||||
|
// How many times we want to hit the endpoint
|
||||||
|
const TOTAL_REQUESTS = 60;
|
||||||
|
|
||||||
|
// Counter to keep track of how many requests we've made
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
// The URL we are testing
|
||||||
|
const url = "http://localhost:8082/matches";
|
||||||
|
|
||||||
|
// test websocket rate limit
|
||||||
|
const testWS = () => {
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const ws = new WebSocket("ws://localhost:8082/ws");
|
||||||
|
ws.onopen = () => console.info(`Socket ${i} opened`);
|
||||||
|
ws.onclose = (e) =>
|
||||||
|
console.info(`Socket ${i} closed: ${e.code} ${e.reason}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function makes ONE HTTP request
|
||||||
|
function makeRequest() {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// Create an HTTP GET request
|
||||||
|
const req = http.get(url, (res) => {
|
||||||
|
// res.statusCode is the HTTP response code (200, 404, 500, etc)
|
||||||
|
console.info(res.statusCode);
|
||||||
|
|
||||||
|
// We don't care about the response body, so just discard it
|
||||||
|
res.resume();
|
||||||
|
|
||||||
|
// If we haven't hit our limit yet, make another request
|
||||||
|
if (count < TOTAL_REQUESTS) {
|
||||||
|
makeRequest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If something goes wrong (server down, connection refused, etc)
|
||||||
|
req.on("error", (err) => {
|
||||||
|
console.error("Request failed:", err.message);
|
||||||
|
|
||||||
|
// Still continue so we always try 60 times
|
||||||
|
if (count < TOTAL_REQUESTS) {
|
||||||
|
makeRequest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the first request
|
||||||
|
makeRequest();
|
||||||
Reference in New Issue
Block a user