feat(platform): add Discord onboarding and Velocity gate
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@minecraft-account-manager/network",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": { ".": "./src/index.ts" },
|
||||
"scripts": { "test": "vitest run", "typecheck": "tsc --noEmit" },
|
||||
"devDependencies": { "@types/node": "^25.0.3", "typescript": "^5.9.3", "vitest": "^4.1.0" }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { isIP } from "node:net";
|
||||
|
||||
export type IpClassification = "unknown" | "clear" | "vpn" | "proxy" | "hosting" | "tor";
|
||||
|
||||
export interface IpIntelligenceResult {
|
||||
classification: IpClassification;
|
||||
provider: string | null;
|
||||
rawResponse?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface IpIntelligenceProvider {
|
||||
classify(ipAddress: string): Promise<IpIntelligenceResult>;
|
||||
}
|
||||
|
||||
export class NoopIpIntelligenceProvider implements IpIntelligenceProvider {
|
||||
async classify(_ipAddress: string): Promise<IpIntelligenceResult> {
|
||||
return { classification: "unknown", provider: null };
|
||||
}
|
||||
}
|
||||
|
||||
export function getClientIp(headers: Headers, trustProxy: boolean) {
|
||||
if (!trustProxy) return null;
|
||||
|
||||
const candidate =
|
||||
headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
|
||||
headers.get("x-real-ip")?.trim() ||
|
||||
null;
|
||||
|
||||
return candidate && isIP(candidate) ? candidate : null;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getClientIp, NoopIpIntelligenceProvider } from "../src/index";
|
||||
|
||||
describe("client IP extraction", () => {
|
||||
it("ignores spoofable forwarding headers unless a trusted proxy is configured", () => {
|
||||
const headers = new Headers({ "x-forwarded-for": "203.0.113.1" });
|
||||
expect(getClientIp(headers, false)).toBeNull();
|
||||
});
|
||||
|
||||
it("uses the first valid address supplied by a trusted proxy", () => {
|
||||
const headers = new Headers({ "x-forwarded-for": "203.0.113.1, 10.0.0.2" });
|
||||
expect(getClientIp(headers, true)).toBe("203.0.113.1");
|
||||
});
|
||||
|
||||
it("rejects malformed proxy values", () => {
|
||||
expect(getClientIp(new Headers({ "x-forwarded-for": "not-an-ip" }), true)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("VPN intelligence", () => {
|
||||
it("explicitly reports unknown when no provider is configured", async () => {
|
||||
await expect(new NoopIpIntelligenceProvider().classify("203.0.113.1")).resolves.toEqual({
|
||||
classification: "unknown",
|
||||
provider: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": { "types": ["node", "vitest/globals"] },
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user