add basic rate limiting

This commit is contained in:
2025-07-01 11:38:38 -04:00
parent 1991e5b31b
commit c3f753a4f0
9 changed files with 64 additions and 20 deletions

View File

@ -26,7 +26,13 @@ app = FastAPI(
constants = importlib.import_module("constants").Constants() constants = importlib.import_module("constants").Constants()
util = importlib.import_module("util").Utilities(app, constants) util = importlib.import_module("util").Utilities(app, constants)
origins = ["https://codey.lol", "https://old.codey.lol", "https://api.codey.lol", "https://_new.codey.lol", "http://localhost:4321"] origins = [
"https://codey.lol",
"https://old.codey.lol",
"https://api.codey.lol",
"https://_new.codey.lol",
"http://localhost:4321",
]
app.add_middleware( app.add_middleware(
CORSMiddleware, # type: ignore CORSMiddleware, # type: ignore

View File

@ -2,7 +2,8 @@ import importlib
import logging import logging
import traceback import traceback
from typing import Optional, Union from typing import Optional, Union
from fastapi import FastAPI from fastapi import FastAPI, Depends
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from .constructors import ( from .constructors import (
ValidArtistSearchRequest, ValidArtistSearchRequest,
@ -32,7 +33,9 @@ class LastFM(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["POST"], include_in_schema=True f"/{endpoint}", handler,
methods=["POST"], include_in_schema=True,
dependencies=[Depends(RateLimiter(times=2, seconds=2))]
) )
async def artist_by_name_handler( async def artist_by_name_handler(

View File

@ -3,7 +3,8 @@ import os
import urllib.parse import urllib.parse
import regex import regex
import aiosqlite as sqlite3 import aiosqlite as sqlite3
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException, Depends
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from typing import LiteralString, Optional, Union, Iterable from typing import LiteralString, Optional, Union, Iterable
from regex import Pattern from regex import Pattern
@ -76,6 +77,7 @@ class LyricSearch(FastAPI):
handler, handler,
methods=["POST"], methods=["POST"],
include_in_schema=_schema_include, include_in_schema=_schema_include,
dependencies=[Depends(RateLimiter(times=2, seconds=3))]
) )
async def typeahead_handler(self, data: ValidTypeAheadRequest) -> JSONResponse: async def typeahead_handler(self, data: ValidTypeAheadRequest) -> JSONResponse:

View File

@ -1,5 +1,10 @@
import logging import logging
from fastapi import FastAPI, Request, Response from fastapi import (
FastAPI,
Request,
Response,
Depends)
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from utils.meme_util import MemeUtil from utils.meme_util import MemeUtil
@ -22,7 +27,11 @@ class Meme(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["GET"], include_in_schema=True f"/{endpoint}", handler,
methods=["GET"], include_in_schema=True,
dependencies=[Depends(
RateLimiter(times=2, seconds=2)
)]
) )
async def get_meme_by_id(self, id: int, request: Request) -> Response: async def get_meme_by_id(self, id: int, request: Request) -> Response:

View File

@ -4,7 +4,8 @@ import os
import json import json
import random import random
from typing import Optional, Annotated from typing import Optional, Annotated
from fastapi import FastAPI, Request, UploadFile, Response, HTTPException, Form from fastapi import FastAPI, Request, UploadFile, Response, HTTPException, Form, Depends
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import redis.asyncio as redis import redis.asyncio as redis
from lyric_search.sources import private, cache as LyricsCache, redis_cache from lyric_search.sources import private, cache as LyricsCache, redis_cache
@ -40,11 +41,16 @@ class Misc(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["GET"], include_in_schema=True f"/{endpoint}",
handler,
methods=["GET"],
include_in_schema=True,
dependencies=[Depends(RateLimiter(times=2, seconds=5))],
) )
app.add_api_route( app.add_api_route(
"/misc/upload_activity_image", self.upload_activity_image, methods=["POST"] "/misc/upload_activity_image", self.upload_activity_image, methods=["POST"],
dependencies=[Depends(RateLimiter(times=2, seconds=5))],
) )
logging.debug("Loading NaaS reasons") logging.debug("Loading NaaS reasons")
@ -66,6 +72,7 @@ class Misc(FastAPI):
except Exception as e: except Exception as e:
logging.debug("Exception: %s", str(e)) logging.debug("Exception: %s", str(e))
return "No." return "No."
async def no(self) -> JSONResponse: async def no(self) -> JSONResponse:
"""NaaS""" """NaaS"""

View File

@ -13,8 +13,14 @@ from .constructors import (
) )
from utils import radio_util from utils import radio_util
from typing import Optional from typing import Optional
from fastapi import FastAPI, BackgroundTasks, Request, Response, HTTPException from fastapi import (
from starlette.concurrency import run_in_threadpool FastAPI,
BackgroundTasks,
Request,
Response,
HTTPException,
Depends)
from fastapi_throttle import RateLimiter
from fastapi.responses import RedirectResponse, JSONResponse from fastapi.responses import RedirectResponse, JSONResponse
@ -43,7 +49,8 @@ class Radio(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["POST"], include_in_schema=True f"/{endpoint}", handler, methods=["POST"], include_in_schema=True,
dependencies=[Depends(RateLimiter(times=10, seconds=5))],
) )
# NOTE: Not in loop because method is GET for this endpoint # NOTE: Not in loop because method is GET for this endpoint
@ -58,7 +65,7 @@ class Radio(FastAPI):
async def on_start(self) -> None: async def on_start(self) -> None:
logging.info("radio: Initializing") logging.info("radio: Initializing")
await run_in_threadpool(self.radio_util.load_playlist) self.loop.run_in_executor(None, self.radio_util.load_playlist)
async def radio_skip( async def radio_skip(
self, data: ValidRadioNextRequest, request: Request self, data: ValidRadioNextRequest, request: Request
@ -317,7 +324,7 @@ class Radio(FastAPI):
if len(self.radio_util.active_playlist) > 1: if len(self.radio_util.active_playlist) > 1:
self.radio_util.active_playlist.append(next) # Push to end of playlist self.radio_util.active_playlist.append(next) # Push to end of playlist
else: else:
await run_in_threadpool(self.radio_util.load_playlist) self.loop.run_in_executor(None, self.radio_util.load_playlist)
self.radio_util.now_playing = next self.radio_util.now_playing = next
next["start"] = time_started next["start"] = time_started

View File

@ -2,7 +2,8 @@ import os
import random import random
from typing import LiteralString, Optional, Union from typing import LiteralString, Optional, Union
import aiosqlite as sqlite3 import aiosqlite as sqlite3
from fastapi import FastAPI from fastapi import FastAPI, Depends
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from .constructors import RandMsgRequest from .constructors import RandMsgRequest
@ -19,7 +20,10 @@ class RandMsg(FastAPI):
self.endpoint_name = "randmsg" self.endpoint_name = "randmsg"
app.add_api_route( app.add_api_route(
f"/{self.endpoint_name}", self.randmsg_handler, methods=["POST"] f"/{self.endpoint_name}", self.randmsg_handler,
methods=["POST"], dependencies=[Depends(
RateLimiter(times=5, seconds=2)
)]
) )
async def randmsg_handler( async def randmsg_handler(

View File

@ -1,6 +1,7 @@
import os import os
import aiosqlite as sqlite3 import aiosqlite as sqlite3
from fastapi import FastAPI from fastapi import FastAPI, Depends
from fastapi_throttle import RateLimiter
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from typing import Optional, LiteralString, Union from typing import Optional, LiteralString, Union
from .constructors import ValidShowEpisodeLineRequest, ValidShowEpisodeListRequest from .constructors import ValidShowEpisodeLineRequest, ValidShowEpisodeListRequest
@ -24,7 +25,9 @@ class Transcriptions(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["POST"], include_in_schema=True f"/{endpoint}", handler,
methods=["POST"], include_in_schema=True,
dependencies=[Depends(RateLimiter(times=2, seconds=2))]
) )
async def get_episodes_handler( async def get_episodes_handler(

View File

@ -1,6 +1,7 @@
import importlib import importlib
from fastapi import FastAPI from fastapi import FastAPI, Depends
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi_throttle import RateLimiter
from typing import Optional, Union from typing import Optional, Union
from .constructors import ValidYTSearchRequest from .constructors import ValidYTSearchRequest
@ -22,7 +23,9 @@ class YT(FastAPI):
for endpoint, handler in self.endpoints.items(): for endpoint, handler in self.endpoints.items():
app.add_api_route( app.add_api_route(
f"/{endpoint}", handler, methods=["POST"], include_in_schema=True f"/{endpoint}", handler,
methods=["POST"], include_in_schema=True,
dependencies=[Depends(RateLimiter(times=2, seconds=2))]
) )
async def yt_video_search_handler(self, data: ValidYTSearchRequest) -> JSONResponse: async def yt_video_search_handler(self, data: ValidYTSearchRequest) -> JSONResponse: