Skip to content

Commit 5cff18f

Browse files
committed
include ip, fix env and add trusted proxies
1 parent ba74af8 commit 5cff18f

File tree

4 files changed

+67
-66
lines changed

4 files changed

+67
-66
lines changed

apps/server/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
"@fastify/static": "^8.3.0",
1818
"@fastify/websocket": "^11.2.0",
1919
"@graphql-yoga/subscription": "^5.0.5",
20+
"@t3-oss/env-core": "^0.13.8",
2021
"@trpc/server": "11.6.0",
21-
"dotenv": "^16.6.1",
22-
"dotenv-cli": "^6.0.0",
2322
"execa": "^9.6.0",
2423
"fastify": "^5.6.1",
2524
"gel": "^2.1.1",
25+
"ipaddr.js": "^2.2.0",
2626
"nanoid": "^3.3.11",
27-
"zod": "^3.25.76"
27+
"zod": "^4.1.12"
2828
},
2929
"devDependencies": {
3030
"@gel/generate": "^0.7.0",

apps/server/src/env.ts

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
1-
import dotenv from "dotenv";
2-
3-
if (process.env.NODE_ENV !== "production") {
4-
dotenv.config({});
5-
dotenv.config({ path: "../../.env" });
6-
}
7-
8-
function envVar(name: string): string | undefined {
9-
return process.env[name];
10-
}
11-
12-
function envVarProdForce(name: string): string | undefined {
13-
const value = envVar(name);
14-
if (value === undefined && process.env.NODE_ENV === "production") {
15-
throw new Error(
16-
`${name} environment variable must be defined in production`,
17-
);
18-
}
19-
return value;
20-
}
21-
22-
export const env = {
23-
port: envVarProdForce("PORT") ?? "8080",
24-
publicDir: envVarProdForce("PUBLIC_DIR"),
25-
};
1+
import { createEnv } from "@t3-oss/env-core";
2+
import z from "zod";
3+
4+
export const env = createEnv({
5+
server: {
6+
PORT: z.coerce.number().default(8080),
7+
PUBLIC_DIR: z.string().optional(),
8+
TRUSTED_PROXIES: z.string()
9+
.transform(value => value.split(','))
10+
.pipe(z.string().array()).optional(),
11+
},
12+
runtimeEnv: process.env,
13+
});

apps/server/src/main.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
} from "@trpc/server/adapters/fastify";
77
import type { TRPCReconnectNotification } from "@trpc/server/rpc";
88
import fastify from "fastify";
9+
import ipaddr from "ipaddr.js";
910
import { env } from "./env";
1011
import { roomRouter } from "./routers/room";
1112
import { roomAdminRouter } from "./routers/room-admin";
@@ -28,6 +29,38 @@ const server = fastify({
2829
routerOptions: {
2930
maxParamLength: 5000,
3031
},
32+
trustProxy: env.TRUSTED_PROXIES
33+
? (address) => {
34+
const ip = ipaddr.parse(address);
35+
const trustedCidrs =
36+
env.TRUSTED_PROXIES?.filter((c) => ipaddr.isValidCIDR(c)).map((c) =>
37+
ipaddr.parseCIDR(c),
38+
) || [];
39+
const trustedIps =
40+
env.TRUSTED_PROXIES?.filter((c) => ipaddr.isValid(c)).map((c) =>
41+
ipaddr.parse(c),
42+
) || [];
43+
44+
// Check if the IP matches any trusted CIDR
45+
for (const [range, prefix] of trustedCidrs) {
46+
if (ip.match(range, prefix)) {
47+
return true;
48+
}
49+
}
50+
51+
// Check if the IP matches any trusted IP directly
52+
for (const trustedIp of trustedIps) {
53+
if (
54+
ip.kind() === trustedIp.kind() &&
55+
ip.toNormalizedString() === trustedIp.toNormalizedString()
56+
) {
57+
return true;
58+
}
59+
}
60+
61+
return false;
62+
}
63+
: false,
3164
});
3265

3366
await server.register(ws, {
@@ -50,12 +83,16 @@ await server.register(ws, {
5083
},
5184
});
5285

53-
server.websocketServer.on("connection", (ws) => {
54-
console.log(`➕➕ Connection (${server.websocketServer.clients.size})`);
86+
server.websocketServer.on("connection", (ws, req) => {
87+
console.log(
88+
`➕➕ Connection (${server.websocketServer.clients.size}) - ${req.socket.remoteAddress}`,
89+
);
5590
ws.once("close", () => {
56-
console.log(`➖➖ Connection (${server.websocketServer.clients.size})`);
91+
console.log(
92+
`➖➖ Connection (${server.websocketServer.clients.size}) - ${req.socket.remoteAddress}`,
93+
);
5794
});
58-
})
95+
});
5996

6097
// Allow CORS for dev
6198
if (process.env.NODE_ENV !== "production") {
@@ -86,8 +123,8 @@ await server.register(fastifyTRPCPlugin, {
86123
} satisfies FastifyTRPCPluginOptions<AppRouter>["trpcOptions"],
87124
});
88125

89-
if (env.publicDir) {
90-
const publicDir = env.publicDir;
126+
if (env.PUBLIC_DIR) {
127+
const publicDir = env.PUBLIC_DIR;
91128
await server.register(import("@fastify/static"), {
92129
root: publicDir,
93130
prefix: "/",
@@ -98,9 +135,8 @@ if (env.publicDir) {
98135
});
99136
}
100137

101-
const PORT = env.port || 8080;
102-
server.listen({ port: Number(PORT), host: "0.0.0.0" }).then(() => {
103-
console.log(`Server listening on port ${PORT}`);
138+
server.listen({ port: env.PORT, host: "0.0.0.0" }).then(() => {
139+
console.log(`Server listening on port ${env.PORT}`);
104140
});
105141

106142
process.on("SIGTERM", () => {
@@ -110,4 +146,3 @@ process.on("SIGTERM", () => {
110146
});
111147
});
112148

113-

bun.lock

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@
5454
"@fastify/static": "^8.3.0",
5555
"@fastify/websocket": "^11.2.0",
5656
"@graphql-yoga/subscription": "^5.0.5",
57+
"@t3-oss/env-core": "^0.13.8",
5758
"@trpc/server": "11.6.0",
58-
"dotenv": "^16.6.1",
59-
"dotenv-cli": "^6.0.0",
6059
"execa": "^9.6.0",
6160
"fastify": "^5.6.1",
6261
"gel": "^2.1.1",
62+
"ipaddr.js": "^2.2.0",
6363
"nanoid": "^3.3.11",
64-
"zod": "^3.25.76",
64+
"zod": "^4.1.12",
6565
},
6666
"devDependencies": {
6767
"@gel/generate": "^0.7.0",
@@ -361,6 +361,8 @@
361361

362362
"@standard-schema/utils": ["@standard-schema/[email protected]", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
363363

364+
"@t3-oss/env-core": ["@t3-oss/[email protected]", "", { "peerDependencies": { "arktype": "^2.1.0", "typescript": ">=5.0.0", "valibot": "^1.0.0-beta.7 || ^1.0.0", "zod": "^3.24.0 || ^4.0.0-beta.0" }, "optionalPeers": ["arktype", "typescript", "valibot", "zod"] }, "sha512-L1inmpzLQyYu4+Q1DyrXsGJYCXbtXjC4cICw1uAKv0ppYPQv656lhZPU91Qd1VS6SO/bou1/q5ufVzBGbNsUpw=="],
365+
364366
"@tailwindcss/node": ["@tailwindcss/[email protected]", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
365367

366368
"@tailwindcss/oxide": ["@tailwindcss/[email protected]", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="],
@@ -523,7 +525,7 @@
523525

524526
"cookie-es": ["[email protected]", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="],
525527

526-
"cross-spawn": ["[email protected].3", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w=="],
528+
"cross-spawn": ["[email protected].6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
527529

528530
"csstype": ["[email protected]", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
529531

@@ -547,12 +549,6 @@
547549

548550
"dijkstrajs": ["[email protected]", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="],
549551

550-
"dotenv": ["[email protected]", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
551-
552-
"dotenv-cli": ["[email protected]", "", { "dependencies": { "cross-spawn": "^7.0.3", "dotenv": "^16.0.0", "dotenv-expand": "^8.0.1", "minimist": "^1.2.5" }, "bin": { "dotenv": "cli.js" } }, "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg=="],
553-
554-
"dotenv-expand": ["[email protected]", "", {}, "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg=="],
555-
556552
"dts-resolver": ["[email protected]", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg=="],
557553

558554
"duplexify": ["[email protected]", "", { "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", "readable-stream": "^3.1.1", "stream-shift": "^1.0.2" } }, "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA=="],
@@ -729,8 +725,6 @@
729725

730726
"minimatch": ["[email protected]", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
731727

732-
"minimist": ["[email protected]", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
733-
734728
"minipass": ["[email protected]", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
735729

736730
"ms": ["[email protected]", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
@@ -1019,12 +1013,6 @@
10191013

10201014
"cross-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
10211015

1022-
"dotenv-cli/dotenv": ["[email protected]", "", {}, "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="],
1023-
1024-
"execa/cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
1025-
1026-
"foreground-child/cross-spawn": ["[email protected]", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
1027-
10281016
"formik/tslib": ["[email protected]", "", {}, "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="],
10291017

10301018
"gel/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w=="],
@@ -1049,8 +1037,6 @@
10491037

10501038
"server/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ=="],
10511039

1052-
"server/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
1053-
10541040
"tsdown/chokidar": ["[email protected]", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
10551041

10561042
"tsdown/debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
@@ -1063,20 +1049,12 @@
10631049

10641050
"cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
10651051

1066-
"execa/cross-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
1067-
1068-
"foreground-child/cross-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
1069-
10701052
"rolldown-plugin-dts/debug/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
10711053

10721054
"server/@types/node/undici-types": ["[email protected]", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
10731055

10741056
"tsdown/chokidar/readdirp": ["[email protected]", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
10751057

10761058
"tsdown/debug/ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
1077-
1078-
"execa/cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
1079-
1080-
"foreground-child/cross-spawn/which/isexe": ["[email protected]", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
10811059
}
10821060
}

0 commit comments

Comments
 (0)