ticket #88 attempt 1
This commit is contained in:
parent
43c5a948e8
commit
aa178a8c62
1
.dpc/verify_cache.json
Normal file
1
.dpc/verify_cache.json
Normal file
File diff suppressed because one or more lines are too long
@ -1070,6 +1070,7 @@ def init_db():
|
||||
("planted_at", ""),
|
||||
("ready_at", ""),
|
||||
("watered_by", "[]"),
|
||||
("cooldown_until", ""),
|
||||
("created_at", ""),
|
||||
("updated_at", ""),
|
||||
):
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import time
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Form, HTTPException, Request
|
||||
@ -18,6 +19,25 @@ from devplacepy.utils import (
|
||||
|
||||
from ._shared import game_seo, notify_farm, owner_by_username
|
||||
|
||||
_farm_action_rate: dict[str, list[float]] = {}
|
||||
|
||||
_MAX_ACTIONS_PER_MINUTE = 30
|
||||
_RATE_WINDOW_SECONDS = 60
|
||||
|
||||
|
||||
def _check_farm_rate_limit(user_uid: str) -> None:
|
||||
now = time.time()
|
||||
window_start = now - _RATE_WINDOW_SECONDS
|
||||
times = _farm_action_rate.setdefault(user_uid, [])
|
||||
times[:] = [t for t in times if t > window_start]
|
||||
if len(times) >= _MAX_ACTIONS_PER_MINUTE:
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
detail="Rate limit exceeded. Max 30 actions per minute.",
|
||||
)
|
||||
times.append(now)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@ -53,6 +73,7 @@ async def water_farm(
|
||||
request: Request, username: str, data: Annotated[GameSlotForm, Form()]
|
||||
):
|
||||
viewer = require_user(request)
|
||||
_check_farm_rate_limit(viewer["uid"])
|
||||
owner = owner_by_username(username)
|
||||
if not owner:
|
||||
raise HTTPException(status_code=404, detail="Farm not found")
|
||||
@ -77,6 +98,7 @@ async def steal_farm(
|
||||
request: Request, username: str, data: Annotated[GameSlotForm, Form()]
|
||||
):
|
||||
viewer = require_user(request)
|
||||
_check_farm_rate_limit(viewer["uid"])
|
||||
owner = owner_by_username(username)
|
||||
if not owner:
|
||||
raise HTTPException(status_code=404, detail="Farm not found")
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import time
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi import APIRouter, Form, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
|
||||
from devplacepy.models import (
|
||||
@ -20,6 +21,25 @@ from devplacepy.utils import award_rewards, get_current_user, require_user, trac
|
||||
|
||||
from ._shared import game_seo, notify_farm, state_payload
|
||||
|
||||
_action_rate: dict[str, list[float]] = {}
|
||||
|
||||
_MAX_ACTIONS_PER_MINUTE = 30
|
||||
_RATE_WINDOW_SECONDS = 60
|
||||
|
||||
|
||||
def _check_rate_limit(user_uid: str) -> None:
|
||||
now = time.time()
|
||||
window_start = now - _RATE_WINDOW_SECONDS
|
||||
times = _action_rate.setdefault(user_uid, [])
|
||||
times[:] = [t for t in times if t > window_start]
|
||||
if len(times) >= _MAX_ACTIONS_PER_MINUTE:
|
||||
raise HTTPException(
|
||||
status_code=429,
|
||||
detail="Rate limit exceeded. Max 30 actions per minute.",
|
||||
)
|
||||
times.append(now)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@ -58,6 +78,7 @@ async def game_leaderboard(request: Request):
|
||||
|
||||
|
||||
async def _respond_action(request: Request, user: dict, fn, on_success=None):
|
||||
_check_rate_limit(user["uid"])
|
||||
try:
|
||||
result = fn()
|
||||
except GameError as exc:
|
||||
|
||||
@ -60,6 +60,9 @@ CONFIRM_REQUIRED = {
|
||||
"gateway_model_delete",
|
||||
"email_account_delete",
|
||||
"email_delete_message",
|
||||
"game_plant",
|
||||
"game_harvest",
|
||||
"game_fertilize",
|
||||
}
|
||||
|
||||
CONDITIONAL_CONFIRM = {
|
||||
|
||||
@ -39,6 +39,9 @@ def plant(user: dict, slot: int, crop_key: str) -> dict:
|
||||
plot = _plot_at(farm["uid"], slot)
|
||||
if not plot:
|
||||
raise GameError("That plot does not exist.")
|
||||
cooldown = plot.get("cooldown_until", "")
|
||||
if cooldown and _now() < _parse_date(cooldown):
|
||||
raise GameError("This plot is on cooldown. Wait a moment before planting.")
|
||||
if plot.get("crop_key"):
|
||||
raise GameError("That plot is already in use.")
|
||||
coins = int(farm.get("coins", 0))
|
||||
@ -85,6 +88,7 @@ def harvest(user: dict, slot: int) -> dict:
|
||||
"planted_at": "",
|
||||
"ready_at": "",
|
||||
"watered_by": "[]",
|
||||
"cooldown_until": _iso(now + timedelta(seconds=5)),
|
||||
"updated_at": _iso(now),
|
||||
},
|
||||
["uid"],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user