Лимит запросов по IP, мелкие правки

This commit is contained in:
justuser-31 2025-11-11 19:36:48 +03:00
parent 4241a20926
commit 4407afcc18
3 changed files with 76 additions and 4 deletions

View File

@ -27,9 +27,10 @@
from db import read from db import read
import requests import requests
from json import loads from json import loads
import asyncio
# Synchronous database read # Synchronous database read
CONFIG = read() CONFIG = asyncio.run(read())
url_prefix = CONFIG['user_api_url'] url_prefix = CONFIG['user_api_url']
def call(api_url, data, pre=True, fix=True): def call(api_url, data, pre=True, fix=True):

View File

@ -1,5 +1,9 @@
from datetime import datetime from datetime import datetime
import pytz import pytz
import asyncio
import time
from collections import defaultdict, deque
from typing import Dict, Deque
from db import user_token_in_db_func from db import user_token_in_db_func
@ -38,3 +42,43 @@ async def append_line_with_limit(text, new_line, max_lines = 5000) -> str:
# Join lines back together with newlines # Join lines back together with newlines
return '\n'.join(lines) return '\n'.join(lines)
class UserRateLimiter:
def __init__(self):
# Store request timestamps for each IP
self.ip_requests: Dict[str, Deque[float]] = defaultdict(deque)
self.lock = asyncio.Lock()
async def is_rate_limited(self, ip: str) -> bool:
"""
Check if user with given IP is making too many requests.
Args:
ip (str): User's IP address
Returns:
bool: True if user makes more than 30 requests per minute, False otherwise
"""
async with self.lock:
current_time = time.time()
# Remove timestamps older than 60 seconds
while self.ip_requests[ip] and current_time - self.ip_requests[ip][0] > 60:
self.ip_requests[ip].popleft()
# Add current request
self.ip_requests[ip].append(current_time)
# Log the IP and request count
request_count = len(self.ip_requests[ip])
print(f"IP: {ip}, Requests in last minute: {request_count}")
# Return True if rate limit exceeded (more than 30 requests per minute)
return request_count > 30
# Global instance
rate_limiter = UserRateLimiter()
async def is_rate_limited(ip: str) -> bool:
return await rate_limiter.is_rate_limited(ip)

View File

@ -1,4 +1,4 @@
from fastapi import Depends, FastAPI, HTTPException, Query, Body from fastapi import Depends, FastAPI, HTTPException, Query, Body, Request
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
import asyncio import asyncio
from statistics import median from statistics import median
@ -6,7 +6,7 @@ from statistics import median
from db import * from db import *
from call2api import check_user_token, user_in_db, transfer_coins, get_stats, create_invoice, delete_invoice, \ from call2api import check_user_token, user_in_db, transfer_coins, get_stats, create_invoice, delete_invoice, \
get_invoice get_invoice
from func import log from func import *
#------------------------------------------------------------ #------------------------------------------------------------
@ -40,10 +40,13 @@ async def token_check(username, user_token):
# OUT: 'OK' / 'Invalid token' (401) / 'Token already exist' (409) / General error (500) # OUT: 'OK' / 'Invalid token' (401) / 'Token already exist' (409) / General error (500)
@app.post('/api/register_user_token/') @app.post('/api/register_user_token/')
async def register_user_token_api( async def register_user_token_api(
request: Request,
token: str = Body(), token: str = Body(),
user_token: str = Body(), user_token: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if token != SYSTEM_API_TOKEN: if token != SYSTEM_API_TOKEN:
raise HTTPException(status_code=401, detail='Invalid token') raise HTTPException(status_code=401, detail='Invalid token')
user_token_db = await user_token_in_db_func(session, user_token) user_token_db = await user_token_in_db_func(session, user_token)
@ -64,10 +67,13 @@ async def register_user_token_api(
# OUT: 'OK' / 'Invalid token' (401) / 'Token not exist' (404) / General error (500) # OUT: 'OK' / 'Invalid token' (401) / 'Token not exist' (404) / General error (500)
@app.post('/api/unregister_user_token/') @app.post('/api/unregister_user_token/')
async def unregister_user_token_api( async def unregister_user_token_api(
request: Request,
token: str = Body(), token: str = Body(),
user_token: str = Body(), user_token: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if token != SYSTEM_API_TOKEN: if token != SYSTEM_API_TOKEN:
raise HTTPException(status_code=401, detail='Invalid token') raise HTTPException(status_code=401, detail='Invalid token')
user_token_db = await user_token_in_db_func(session, user_token) user_token_db = await user_token_in_db_func(session, user_token)
@ -85,10 +91,13 @@ async def unregister_user_token_api(
# OUT: {'issue_date': str, 'logs': str} / 'Invalid username or token' (401) / 'Token not exist' (404) / General error (500) # OUT: {'issue_date': str, 'logs': str} / 'Invalid username or token' (401) / 'Token not exist' (404) / General error (500)
@app.post('/api/get_user_token_info/') @app.post('/api/get_user_token_info/')
async def get_user_token_info_api( async def get_user_token_info_api(
request: Request,
username: str | None = Body(None), username: str | None = Body(None),
user_token: str = Body(), user_token: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
user_token_db = await user_token_in_db_func(session, user_token) user_token_db = await user_token_in_db_func(session, user_token)
@ -116,10 +125,13 @@ async def get_user_token_info_api(
# } / 'Invalid username or token' (401) / 'User not found' (404) / General error (500) # } / 'Invalid username or token' (401) / 'User not found' (404) / General error (500)
@app.post('/api/user_in_db/') @app.post('/api/user_in_db/')
async def user_in_db_api( async def user_in_db_api(
request: Request,
username: str | None = Body(None), username: str | None = Body(None),
user_token: str = Body(), user_token: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/user_in_db') await log(session, user_token, f'/user_in_db')
@ -131,12 +143,15 @@ async def user_in_db_api(
# / General error (500) # / General error (500)
@app.post('/api/transfer_coins/') @app.post('/api/transfer_coins/')
async def transfer_coins_api( async def transfer_coins_api(
request: Request,
username: str = Body(), username: str = Body(),
user_token: str = Body(), user_token: str = Body(),
dst_username: str = Body(), dst_username: str = Body(),
amount: float = Body(), amount: float = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/transfer_coins: (dst_username: {dst_username}, amount: {amount})') await log(session, user_token, f'/transfer_coins: (dst_username: {dst_username}, amount: {amount})')
@ -156,10 +171,13 @@ async def transfer_coins_api(
# } / 'Invalid username or token' (401) / General error (500) # } / 'Invalid username or token' (401) / General error (500)
@app.post('/api/get_stats/') @app.post('/api/get_stats/')
async def get_stats_api( async def get_stats_api(
request: Request,
username: str = Body(), username: str = Body(),
user_token: str = Body(), user_token: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/get_stats') await log(session, user_token, f'/get_stats')
@ -170,11 +188,14 @@ async def get_stats_api(
# / General error (500) # / General error (500)
@app.post('/api/create_invoice/') @app.post('/api/create_invoice/')
async def create_invoice_api( async def create_invoice_api(
request: Request,
username: str = Body(), username: str = Body(),
user_token: str = Body(), user_token: str = Body(),
amount: float | None = Body(None), amount: float | None = Body(None),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/create_invoice: (amount: {amount})') await log(session, user_token, f'/create_invoice: (amount: {amount})')
@ -184,11 +205,14 @@ async def create_invoice_api(
# OUT: 'OK' / 'Invalid username or token' (401) / 'Invoice id not found' (404) / General error (500) # OUT: 'OK' / 'Invalid username or token' (401) / 'Invoice id not found' (404) / General error (500)
@app.post('/api/delete_invoice/') @app.post('/api/delete_invoice/')
async def delete_invoice_api( async def delete_invoice_api(
request: Request,
username: str = Body(), username: str = Body(),
user_token: str = Body(), user_token: str = Body(),
invoice_id: str = Body(), invoice_id: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/delete_invoice: (id: {invoice_id})') await log(session, user_token, f'/delete_invoice: (id: {invoice_id})')
@ -203,11 +227,14 @@ async def delete_invoice_api(
# } / 'Invalid username or token' (401) / 'Invoice id not found' (404) / General error (500) # } / 'Invalid username or token' (401) / 'Invoice id not found' (404) / General error (500)
@app.post('/api/get_invoice/') @app.post('/api/get_invoice/')
async def get_invoice_api( async def get_invoice_api(
request: Request,
username: str = Body(), username: str = Body(),
user_token: str = Body(), user_token: str = Body(),
invoice_id: str = Body(), invoice_id: str = Body(),
session: AsyncSession = Depends(get_session) session: AsyncSession = Depends(get_session)
): ):
if await is_rate_limited(request.client.host):
raise HTTPException(status_code=429, detail='Too many requests')
if not await token_check(username, user_token): if not await token_check(username, user_token):
raise HTTPException(status_code=401, detail='Invalid username or token') raise HTTPException(status_code=401, detail='Invalid username or token')
await log(session, user_token, f'/get_invoice: (id: {invoice_id})') await log(session, user_token, f'/get_invoice: (id: {invoice_id})')