Skip to content

Commit

Permalink
Merge pull request #136 from edinstance/main
Browse files Browse the repository at this point in the history
feat: added rate limiting to the submit map mutation
  • Loading branch information
delasy authored Nov 2, 2024
2 parents c225954 + efd1534 commit 4fc9661
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 29 deletions.
41 changes: 26 additions & 15 deletions app/playground/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,19 @@ export default function PlaygroundPage() {
setPublishing(true);

try {
await submitMap({ map });
const submitted = await submitMap({ map });

toast({
description: "Map submitted successfully!",
});
if (submitted == 200) {
toast({
description: "Map submitted successfully!",
});
} else if (submitted == 429) {
toast({
description:
"You have exceeded the rate limit for submitting maps. Please try again later.",
variant: "destructive",
});
}
} catch (error) {
if (errorMessage(error).includes(SIGN_IN_ERROR_MESSAGE)) {
setOpenSignInModal(true);
Expand Down Expand Up @@ -465,17 +473,20 @@ export default function PlaygroundPage() {
</>
) : (
<>
<Button
className="gap-1"
disabled={publishing}
onClick={handlePublish}
type="button"
variant="secondary"
>
<UploadIcon size={16} />
<span>{publishing ? "Submitting..." : "Submit Map"}</span>
</Button>

<div className="flex flex-col">
<Button
disabled={publishing}
onClick={handlePublish}
type="button"
variant="secondary"
>
<UploadIcon size={16} />
<span>{publishing ? "Submitting..." : "Submit Map"}</span>
</Button>
<p className="flex justify-end pt-2 text-xs text-gray-600">
*You can only submit 3 maps per minute
</p>
</div>
<Button
disabled={
!(solution === null && !isSimulating && !userPlaying)
Expand Down
97 changes: 90 additions & 7 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@
* @module
*/

import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";
import type * as attempts from "../attempts.js";
import type * as auth from "../auth.js";
import type * as constants from "../constants.js";
Expand All @@ -28,10 +23,16 @@ import type * as maps from "../maps.js";
import type * as models from "../models.js";
import type * as playerresults from "../playerresults.js";
import type * as prompts from "../prompts.js";
import type * as rateLimits from "../rateLimits.js";
import type * as results from "../results.js";
import type * as scores from "../scores.js";
import type * as users from "../users.js";

import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";
/**
* A utility for referencing Convex functions in your app's API.
*
Expand All @@ -54,17 +55,99 @@ declare const fullApi: ApiFromModules<{
models: typeof models;
playerresults: typeof playerresults;
prompts: typeof prompts;
rateLimits: typeof rateLimits;
results: typeof results;
scores: typeof scores;
users: typeof users;
}>;
declare const fullApiWithMounts: typeof fullApi;

export declare const api: FilterApi<
typeof fullApi,
typeof fullApiWithMounts,
FunctionReference<any, "public">
>;
export declare const internal: FilterApi<
typeof fullApi,
typeof fullApiWithMounts,
FunctionReference<any, "internal">
>;

export declare const components: {
rateLimiter: {
lib: {
checkRateLimit: FunctionReference<
"query",
"internal",
{
config:
| {
capacity?: number;
kind: "token bucket";
maxReserved?: number;
period: number;
rate: number;
shards?: number;
}
| {
capacity?: number;
kind: "fixed window";
maxReserved?: number;
period: number;
rate: number;
shards?: number;
start?: number;
};
count?: number;
key?: string;
name: string;
reserve?: boolean;
throws?: boolean;
},
{ ok: true; retryAfter?: number } | { ok: false; retryAfter: number }
>;
clearAll: FunctionReference<
"mutation",
"internal",
{ before?: number },
null
>;
rateLimit: FunctionReference<
"mutation",
"internal",
{
config:
| {
capacity?: number;
kind: "token bucket";
maxReserved?: number;
period: number;
rate: number;
shards?: number;
}
| {
capacity?: number;
kind: "fixed window";
maxReserved?: number;
period: number;
rate: number;
shards?: number;
start?: number;
};
count?: number;
key?: string;
name: string;
reserve?: boolean;
throws?: boolean;
},
{ ok: true; retryAfter?: number } | { ok: false; retryAfter: number }
>;
resetRateLimit: FunctionReference<
"mutation",
"internal",
{ key?: string; name: string },
null
>;
};
};
};

/* prettier-ignore-end */
3 changes: 2 additions & 1 deletion convex/_generated/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* @module
*/

import { anyApi } from "convex/server";
import { anyApi, componentsGeneric } from "convex/server";

/**
* A utility for referencing Convex functions in your app's API.
Expand All @@ -22,5 +22,6 @@ import { anyApi } from "convex/server";
*/
export const api = anyApi;
export const internal = anyApi;
export const components = componentsGeneric();

/* prettier-ignore-end */
7 changes: 7 additions & 0 deletions convex/_generated/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import {
ActionBuilder,
AnyComponents,
HttpActionBuilder,
MutationBuilder,
QueryBuilder,
Expand All @@ -20,9 +21,15 @@ import {
GenericQueryCtx,
GenericDatabaseReader,
GenericDatabaseWriter,
FunctionReference,
} from "convex/server";
import type { DataModel } from "./dataModel.js";

type GenericCtx =
| GenericActionCtx<DataModel>
| GenericMutationCtx<DataModel>
| GenericQueryCtx<DataModel>;

/**
* Define a query in this Convex app's public API.
*
Expand Down
1 change: 1 addition & 0 deletions convex/_generated/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
internalActionGeneric,
internalMutationGeneric,
internalQueryGeneric,
componentsGeneric,
} from "convex/server";

/**
Expand Down
7 changes: 7 additions & 0 deletions convex/convex.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import rateLimiter from "@convex-dev/rate-limiter/convex.config";
import { defineApp } from "convex/server";

const app = defineApp();
app.use(rateLimiter);

export default app;
27 changes: 21 additions & 6 deletions convex/maps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { runModel } from "../models";
import { ZombieSurvival } from "../simulators/zombie-survival";
import { getAuthUserId } from "@convex-dev/auth/server";
import { isRateLimitError } from "@convex-dev/rate-limiter";
import { v } from "convex/values";
import { api, internal } from "./_generated/api";
import {
Expand All @@ -11,6 +12,7 @@ import {
query,
} from "./_generated/server";
import { Prompt } from "./prompts";
import { rateLimiter } from "./rateLimits";
import {
adminMutationBuilder,
adminQueryBuilder,
Expand Down Expand Up @@ -159,12 +161,25 @@ export const submitMap = authenticatedMutation({
map: v.array(v.array(v.string())),
},
handler: async (ctx, args) => {
await ctx.db.insert("maps", {
grid: args.map,
level: undefined,
submittedBy: ctx.userId,
isReviewed: false,
});
try {
const status = await rateLimiter.limit(ctx, "submitMap", {
key: ctx.userId,
throws: true,
});
if (status.ok) {
await ctx.db.insert("maps", {
grid: args.map,
level: undefined,
submittedBy: ctx.userId,
isReviewed: false,
});
return 200;
}
} catch (e) {
if (isRateLimitError(e)) {
return 429;
}
}
},
});

Expand Down
6 changes: 6 additions & 0 deletions convex/rateLimits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { MINUTE, RateLimiter } from "@convex-dev/rate-limiter";
import { components } from "./_generated/api";

export const rateLimiter = new RateLimiter(components.rateLimiter, {
submitMap: { kind: "fixed window", rate: 3, period: MINUTE, capacity: 3 },
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@anthropic-ai/sdk": "^0.29.1",
"@auth/core": "^0.34.2",
"@convex-dev/auth": "^0.0.71",
"@convex-dev/rate-limiter": "^0.2.3",
"@google/generative-ai": "^0.21.0",
"@icons-pack/react-simple-icons": "^10.1.0",
"@mistralai/mistralai": "^1.1.0",
Expand Down

0 comments on commit 4fc9661

Please sign in to comment.