Skip to content

Commit

Permalink
feat: add captcha dependency to lobbies module
Browse files Browse the repository at this point in the history
  • Loading branch information
ABCxFF committed Oct 16, 2024
1 parent fc5064c commit 0a6f5d3
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 9 deletions.
24 changes: 24 additions & 0 deletions modules/lobbies/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BackendLocalDevelopmentConfig, BackendLocalDevelopmentConfigPort } from
import { BackendServerConfig } from "./utils/lobby/backend/server.ts";
import { BackendTestConfig } from "./utils/lobby/backend/test.ts";

import type { CaptchaProvider as ExternalCaptchaProvider } from "../captcha/utils/types.ts";

export interface Config {
lobbies: LobbyConfig;
lobbyRules: LobbyRule[];
Expand All @@ -11,6 +13,28 @@ export interface Config {
unconnectedExpireAfter: number;
autoDestroyAfter?: number;
};
captcha?: CaptchaConfig | null;
}

export type CaptchaProvider = ExternalCaptchaProvider;

export interface RateLimitConfig {
period: number;
requests: number;
}


export type RateLimitByEndpointId = {
list: RateLimitConfig;
create: RateLimitConfig;
join: RateLimitConfig;
find_or_create: RateLimitConfig;
find: RateLimitConfig;
};

export interface CaptchaConfig {
provider: CaptchaProvider;
endpointRateLimits: Partial<RateLimitByEndpointId>;
}

export interface LobbyRule {
Expand Down
3 changes: 2 additions & 1 deletion modules/lobbies/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@
},
"dependencies": {
"tokens": {},
"rivet": {}
"rivet": {},
"captcha": {}
},
"defaultConfig": {
"lobbies": {
Expand Down
18 changes: 17 additions & 1 deletion modules/lobbies/scripts/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import {
PlayerRequest,
PlayerResponseWithToken,
} from "../utils/player.ts";
import { getCaptchaProvider, getRateLimitConfigByEndpoint } from "../utils/captcha_config.ts";

export interface Request {
version: string;
region: string;
region: string;
tags?: Record<string, string>;
maxPlayers: number;
maxPlayersDirect: number;

players: PlayerRequest[];

noWait?: boolean;

captchaToken?: string;
}

export interface Response {
Expand All @@ -33,6 +36,19 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const rateLimitConfig = getRateLimitConfigByEndpoint(ctx.config, "create");
const captchaProvider = getCaptchaProvider(ctx.config);
if (captchaProvider !== null) {
await ctx.modules.captcha.guard({
key: "default",
period: rateLimitConfig.period,
requests: rateLimitConfig.requests,
type: "lobbies.create",
captchaToken: req.captchaToken,
captchaProvider: captchaProvider
});
}

const lobbyId = crypto.randomUUID();

const { lobby, players } = await ctx.actors.lobbyManager
Expand Down
19 changes: 17 additions & 2 deletions modules/lobbies/scripts/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
PlayerRequest,
PlayerResponseWithToken,
} from "../utils/player.ts";
import { getCaptchaProvider, getRateLimitConfigByEndpoint } from "../utils/captcha_config.ts";

export interface Request {
version: string;
regions?: string[];
regions?: string[];
tags?: Record<string, string>;
players: PlayerRequest[];
noWait?: boolean;
noWait?: boolean;
captchaToken?: string;
}

export interface Response {
Expand All @@ -27,6 +29,19 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const rateLimitConfig = getRateLimitConfigByEndpoint(ctx.config, "find");
const captchaProvider = getCaptchaProvider(ctx.config);
if (captchaProvider !== null) {
await ctx.modules.captcha.guard({
key: "default",
period: rateLimitConfig.period,
requests: rateLimitConfig.requests,
type: "lobbies.find",
captchaToken: req.captchaToken,
captchaProvider: captchaProvider
});
}

const { lobby, players } = await ctx.actors.lobbyManager
.getOrCreateAndCall<undefined, FindLobbyRequest, FindLobbyResponse>(
"default",
Expand Down
20 changes: 18 additions & 2 deletions modules/lobbies/scripts/find_or_create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@ import {
PlayerRequest,
PlayerResponseWithToken,
} from "../utils/player.ts";
import { getCaptchaProvider, getRateLimitConfigByEndpoint } from "../utils/captcha_config.ts";

export interface Request {
version: string;
regions?: string[];
regions?: string[];
tags?: Record<string, string>;
players: PlayerRequest[];
noWait?: boolean;
noWait?: boolean;

createConfig: {
region: string;
tags?: Record<string, string>;
maxPlayers: number;
maxPlayersDirect: number;
};

captchaToken?: string;
}

export interface Response {
Expand All @@ -34,6 +37,19 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const rateLimitConfig = getRateLimitConfigByEndpoint(ctx.config, "find_or_create");
const captchaProvider = getCaptchaProvider(ctx.config);
if (captchaProvider !== null) {
await ctx.modules.captcha.guard({
key: "default",
period: rateLimitConfig.period,
requests: rateLimitConfig.requests,
type: "lobbies.find_or_create",
captchaToken: req.captchaToken,
captchaProvider: captchaProvider
});
}

const lobbyId = crypto.randomUUID();

const { lobby, players } = await ctx.actors
Expand Down
18 changes: 17 additions & 1 deletion modules/lobbies/scripts/join.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import {
PlayerRequest,
PlayerResponseWithToken,
} from "../utils/player.ts";
import { getCaptchaProvider, getRateLimitConfigByEndpoint } from "../utils/captcha_config.ts";

export interface Request {
lobbyId: string;
players: PlayerRequest[];
noWait?: boolean;
noWait?: boolean;

captchaToken?: string;
}

export interface Response {
Expand All @@ -25,6 +28,19 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
const rateLimitConfig = getRateLimitConfigByEndpoint(ctx.config, "join");
const captchaProvider = getCaptchaProvider(ctx.config);
if (captchaProvider !== null) {
await ctx.modules.captcha.guard({
key: "default",
period: rateLimitConfig.period,
requests: rateLimitConfig.requests,
type: "lobbies.join",
captchaToken: req.captchaToken,
captchaProvider: captchaProvider
});
}

const { lobby, players } = await ctx.actors
.lobbyManager.getOrCreateAndCall<undefined, JoinLobbyRequest, JoinLobbyResponse>(
"default",
Expand Down
19 changes: 17 additions & 2 deletions modules/lobbies/scripts/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import {
ListLobbiesResponse,
} from "../utils/lobby_manager/rpc.ts";
import { ScriptContext } from "../module.gen.ts";
import { getCaptchaProvider, getRateLimitConfigByEndpoint } from "../utils/captcha_config.ts";

export interface Request {
version: string;
regions?: string[];
regions?: string[];
tags?: Record<string, string>;

captchaToken?: string;
}

export interface Response {
Expand All @@ -29,8 +32,20 @@ export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
// TODO: Cache this without hitting the DO
const rateLimitConfig = getRateLimitConfigByEndpoint(ctx.config, "list");
const captchaProvider = getCaptchaProvider(ctx.config);
if (captchaProvider !== null) {
await ctx.modules.captcha.guard({
key: "default",
period: rateLimitConfig.period,
requests: rateLimitConfig.requests,
type: "lobbies.list",
captchaToken: req.captchaToken,
captchaProvider: captchaProvider
});
}

// TODO: Cache this without hitting the DO
const { lobbies } = await ctx.actors.lobbyManager.getOrCreateAndCall<
undefined,
ListLobbiesRequest,
Expand Down
39 changes: 39 additions & 0 deletions modules/lobbies/utils/captcha_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Config, CaptchaProvider, RateLimitByEndpointId, RateLimitConfig } from "../config.ts";

export function getCaptchaProvider(config: Config): CaptchaProvider | null {
if (!config.captcha || !config.captcha.provider) return null;

return config.captcha.provider;
}

type KnownEndpoint = keyof RateLimitByEndpointId;

const endpointRateLimits: RateLimitByEndpointId = {
list: {
period: 20,
requests: 5
},
create: {
period: 15,
requests: 5
},
join: {
period: 15,
requests: 8
},
find_or_create: {
period: 15,
requests: 8
},
find: {
period: 15,
requests: 10
}
}

export function getRateLimitConfigByEndpoint(
config: Config,
endpoint: KnownEndpoint
): Readonly<RateLimitConfig> {
return config.captcha?.endpointRateLimits?.[endpoint] ?? endpointRateLimits[endpoint];
}

0 comments on commit 0a6f5d3

Please sign in to comment.