add project size ratelimiting

This commit is contained in:
Ishaan Dey 2024-05-09 22:32:21 -07:00
parent e86e86dbe2
commit 3325b20aed
4 changed files with 65 additions and 23 deletions

View File

@ -13,6 +13,7 @@ import {
createFile, createFile,
deleteFile, deleteFile,
generateCode, generateCode,
getProjectSize,
getSandboxFiles, getSandboxFiles,
renameFile, renameFile,
saveFile, saveFile,
@ -155,8 +156,16 @@ io.on("connection", async (socket) => {
} }
}) })
socket.on("createFile", async (name: string) => { socket.on("createFile", async (name: string, callback) => {
try { try {
const size: number = await getProjectSize(data.sandboxId)
// limit is 200mb
if (size > 200 * 1024 * 1024) {
io.emit("rateLimit", "Rate limited: project size exceeded. Please delete some files.")
callback({success: false})
}
await createFileRL.consume(data.userId, 1) await createFileRL.consume(data.userId, 1)
const id = `projects/${data.sandboxId}/${name}` const id = `projects/${data.sandboxId}/${name}`
@ -177,6 +186,8 @@ io.on("connection", async (socket) => {
}) })
await createFile(id) await createFile(id)
callback({success: true})
} catch (e) { } catch (e) {
io.emit("rateLimit", "Rate limited: file creation. Please slow down.") io.emit("rateLimit", "Rate limited: file creation. Please slow down.")
} }

View File

@ -1,3 +1,4 @@
import e from "cors"
import { import {
R2FileBody, R2FileBody,
R2Files, R2Files,
@ -134,6 +135,13 @@ export const deleteFile = async (fileId: string) => {
return res.ok return res.ok
} }
export const getProjectSize = async (id: string) => {
const res = await fetch(
`https://storage.ishaan1013.workers.dev/api/size?sandboxId=${id}`
)
return (await res.json()).size
}
export const generateCode = async ({ export const generateCode = async ({
fileName, fileName,
code, code,

View File

@ -16,7 +16,23 @@ export default {
const path = url.pathname; const path = url.pathname;
const method = request.method; const method = request.method;
if (path === '/api') { if (path === '/api/size' && method === 'GET') {
const params = url.searchParams;
const sandboxId = params.get('sandboxId');
if (sandboxId) {
const res = await env.R2.list({ prefix: `projects/${sandboxId}` });
// sum up the size of all files
let size = 0;
for (const file of res.objects) {
size += file.size;
}
return new Response(JSON.stringify({ size }), { status: 200 });
} else return invalidRequest;
}
else if (path === '/api') {
if (method === 'GET') { if (method === 'GET') {
const params = url.searchParams; const params = url.searchParams;
const sandboxId = params.get('sandboxId'); const sandboxId = params.get('sandboxId');

View File

@ -1,9 +1,9 @@
"use client" "use client";
import { validateName } from "@/lib/utils" import { validateName } from "@/lib/utils";
import Image from "next/image" import Image from "next/image";
import { useEffect, useRef } from "react" import { useEffect, useRef } from "react";
import { Socket } from "socket.io-client" import { Socket } from "socket.io-client";
export default function New({ export default function New({
socket, socket,
@ -11,31 +11,38 @@ export default function New({
stopEditing, stopEditing,
addNew, addNew,
}: { }: {
socket: Socket socket: Socket;
type: "file" | "folder" type: "file" | "folder";
stopEditing: () => void stopEditing: () => void;
addNew: (name: string, type: "file" | "folder") => void addNew: (name: string, type: "file" | "folder") => void;
}) { }) {
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null);
const createNew = () => { const createNew = () => {
const name = inputRef.current?.value const name = inputRef.current?.value;
if (name) { if (name) {
const valid = validateName(name, "", type) const valid = validateName(name, "", type);
if (valid.status) { if (valid.status) {
if (type === "file") { if (type === "file") {
socket.emit("createFile", name) socket.emit(
"createFile",
name,
({ success }: { success: boolean }) => {
if (success) {
addNew(name, type);
}
}
);
} }
addNew(name, type)
} }
} }
stopEditing() stopEditing();
} };
useEffect(() => { useEffect(() => {
inputRef.current?.focus() inputRef.current?.focus();
}, []) }, []);
return ( return (
<div className="w-full flex items-center h-7 px-1 hover:bg-secondary rounded-sm cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"> <div className="w-full flex items-center h-7 px-1 hover:bg-secondary rounded-sm cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring">
@ -52,8 +59,8 @@ export default function New({
/> />
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault() e.preventDefault();
createNew() createNew();
}} }}
> >
<input <input
@ -63,5 +70,5 @@ export default function New({
/> />
</form> </form>
</div> </div>
) );
} }