Skip to content

Commit 80faa42

Browse files
Merge pull request #28 from Mr-Sunglasses/feat/ratelimmiting
Feat/ratelimmiting
2 parents db7d327 + 34f9ea8 commit 80faa42

File tree

3 files changed

+133
-25
lines changed

3 files changed

+133
-25
lines changed

pdm.lock

Lines changed: 93 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies = [
99
"fastapi[all]>=0.104.1",
1010
"sqlalchemy>=2.0.23",
1111
"jinja2>=3.1.2",
12+
"slowapi>=0.1.8",
1213
]
1314
requires-python = ">=3.10"
1415
readme = "README.md"

src/paste/main.py

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
from fastapi import FastAPI
77
from fastapi.templating import Jinja2Templates
88
from fastapi.middleware.cors import CORSMiddleware
9+
from slowapi.errors import RateLimitExceeded
10+
from slowapi import Limiter, _rate_limit_exceeded_handler
11+
from slowapi.util import get_remote_address
912
from .utils import generate_uuid
1013

14+
limiter = Limiter(key_func=get_remote_address)
1115
app = FastAPI(title="paste.py 🐍")
16+
app.state.limiter = limiter
17+
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
1218

1319
origins = ["*"]
1420

@@ -24,88 +30,97 @@
2430

2531
BASE_DIR = Path(__file__).resolve().parent
2632

27-
templates = Jinja2Templates(directory=str(Path(BASE_DIR, 'templates')))
33+
templates = Jinja2Templates(directory=str(Path(BASE_DIR, "templates")))
2834

2935

3036
@app.post("/file")
31-
def post_as_a_file(file: UploadFile = File(...)):
37+
@limiter.limit("100/minute")
38+
async def post_as_a_file(request: Request, file: UploadFile = File(...)):
3239
try:
3340
uuid = generate_uuid()
3441
if uuid in large_uuid_storage:
3542
uuid = generate_uuid()
3643
path = f"data/{uuid}"
37-
with open(path, 'wb') as f:
44+
with open(path, "wb") as f:
3845
shutil.copyfileobj(file.file, f)
3946
large_uuid_storage.append(uuid)
4047
print(large_uuid_storage)
4148
except Exception:
4249
# return {"message": "There was an error uploading the file"}
43-
raise HTTPException(detail="There was an error uploading the file",
44-
status_code=status.HTTP_403_FORBIDDEN)
50+
raise HTTPException(
51+
detail="There was an error uploading the file",
52+
status_code=status.HTTP_403_FORBIDDEN,
53+
)
4554
finally:
4655
file.file.close()
4756

4857
return PlainTextResponse(uuid, status_code=status.HTTP_201_CREATED)
4958

5059

5160
@app.get("/paste/{uuid}")
52-
def post_as_a_text(uuid):
61+
async def post_as_a_text(uuid):
5362
path = f"data/{uuid}"
5463
try:
55-
with open(path, 'rb') as f:
64+
with open(path, "rb") as f:
5665
return PlainTextResponse(f.read())
5766
except Exception as e:
5867
print(e)
59-
raise HTTPException(detail="404: The Requested Resource is not found",
60-
status_code=status.HTTP_404_NOT_FOUND)
68+
raise HTTPException(
69+
detail="404: The Requested Resource is not found",
70+
status_code=status.HTTP_404_NOT_FOUND,
71+
)
6172

6273

6374
@app.get("/", response_class=HTMLResponse)
64-
def indexpage(request: Request):
75+
async def indexpage(request: Request):
6576
return templates.TemplateResponse("index.html", {"request": request})
6677

6778

6879
@app.delete("/paste/{uuid}", response_class=PlainTextResponse)
69-
def delete_paste(uuid):
80+
async def delete_paste(uuid):
7081
path = f"data/{uuid}"
7182
try:
7283
os.remove(path)
7384
return PlainTextResponse(f"File successfully deleted {uuid}")
7485
except FileNotFoundError:
75-
raise HTTPException(detail="File Not Found",
76-
status_code=status.HTTP_404_NOT_FOUND)
86+
raise HTTPException(
87+
detail="File Not Found", status_code=status.HTTP_404_NOT_FOUND
88+
)
7789
except Exception as e:
7890
raise HTTPException(
79-
detail=f"The exception is {e}", status_code=status.HTTP_409_CONFLICT)
91+
detail=f"The exception is {e}", status_code=status.HTTP_409_CONFLICT
92+
)
8093

8194

8295
@app.get("/web", response_class=HTMLResponse)
83-
def web(request: Request):
96+
async def web(request: Request):
8497
return templates.TemplateResponse("web.html", {"request": request})
8598

8699

87100
@app.post("/web", response_class=PlainTextResponse)
88-
def web_post(content: str = Form(...)):
89-
# print(content)
90-
# return PlainTextResponse(content=content)
101+
@limiter.limit("100/minute")
102+
async def web_post(request: Request, content: str = Form(...)):
91103
try:
92104
file_content = content.encode()
93105
uuid = generate_uuid()
94106
if uuid in large_uuid_storage:
95107
uuid = generate_uuid()
96108
path = f"data/{uuid}"
97-
with open(path, 'wb') as f:
109+
with open(path, "wb") as f:
98110
f.write(file_content)
99111
large_uuid_storage.append(uuid)
100112
except Exception as e:
101-
# return {"message": "There was an error uploading the file"}
102113
print(e)
103-
raise HTTPException(detail="There was an error uploading the file",
104-
status_code=status.HTTP_403_FORBIDDEN)
114+
raise HTTPException(
115+
detail="There was an error uploading the file",
116+
status_code=status.HTTP_403_FORBIDDEN,
117+
)
105118

106-
return RedirectResponse(f"http://paste.fosscu.org/paste/{uuid}", status_code=status.HTTP_303_SEE_OTHER)
119+
return RedirectResponse(
120+
f"http://paste.fosscu.org/paste/{uuid}", status_code=status.HTTP_303_SEE_OTHER
121+
)
107122

108123

109124
@app.get("/health", status_code=status.HTTP_200_OK)
110-
def health() -> dict[str, str]:
125+
async def health() -> dict[str, str]:
111126
return {"status": "ok"}

0 commit comments

Comments
 (0)