152 строки
3.7 KiB
Python
152 строки
3.7 KiB
Python
|
import asyncio
|
||
|
import database
|
||
|
from fastapi import FastAPI, Request, Body, WebSocket, WebSocketDisconnect
|
||
|
from fastapi.responses import Response
|
||
|
from fastapi.exceptions import HTTPException
|
||
|
import models
|
||
|
import typing
|
||
|
|
||
|
|
||
|
app = FastAPI()
|
||
|
|
||
|
|
||
|
@app.middleware('http')
|
||
|
async def function(
|
||
|
request: Request,
|
||
|
callback: typing.Callable,
|
||
|
):
|
||
|
async def too_many_requests(
|
||
|
message: str,
|
||
|
delay: int,
|
||
|
):
|
||
|
raise HTTPException(
|
||
|
status_code=429,
|
||
|
detail={
|
||
|
'msg': message,
|
||
|
},
|
||
|
headers={
|
||
|
'Retry-After': str(delay),
|
||
|
},
|
||
|
)
|
||
|
|
||
|
requests_frequency = await database.get_requests_frequency(
|
||
|
ip_address=request.client.host,
|
||
|
)
|
||
|
if requests_frequency.per_day > 10000:
|
||
|
return await too_many_requests(
|
||
|
message='Слишком много запросов в день.',
|
||
|
delay=86400,
|
||
|
)
|
||
|
if requests_frequency.per_hour > 1000:
|
||
|
return await too_many_requests(
|
||
|
message='Слишком много запросов в час.',
|
||
|
delay=3600,
|
||
|
)
|
||
|
if requests_frequency.per_minute > 100:
|
||
|
return await too_many_requests(
|
||
|
message='Слишком много запросов в минуту.',
|
||
|
delay=60,
|
||
|
)
|
||
|
if requests_frequency.per_second > 1:
|
||
|
return await too_many_requests(
|
||
|
message='Слишком много запросов в секунду.',
|
||
|
delay=1,
|
||
|
)
|
||
|
asyncio.ensure_future(
|
||
|
database.put_request(
|
||
|
ip_address=request.client.host,
|
||
|
method=request.method,
|
||
|
url=str(request.url),
|
||
|
is_secure=request.base_url.is_secure,
|
||
|
user_agent=request.headers.get('User-Agent'),
|
||
|
referer=request.headers.get('Referer'),
|
||
|
)
|
||
|
)
|
||
|
return await callback(request)
|
||
|
|
||
|
|
||
|
class ConnectionManager:
|
||
|
def __init__(self):
|
||
|
self.connections: list[WebSocket] = list[WebSocket]()
|
||
|
|
||
|
async def connect(self, websocket: WebSocket):
|
||
|
await websocket.accept()
|
||
|
self.connections.append(websocket)
|
||
|
|
||
|
def disconnect(self, websocket: WebSocket):
|
||
|
self.connections.remove(websocket)
|
||
|
|
||
|
async def broadcast(self, data: dict):
|
||
|
for connection in self.connections:
|
||
|
asyncio.ensure_future(connection.send_json(data))
|
||
|
|
||
|
|
||
|
manager = ConnectionManager()
|
||
|
|
||
|
|
||
|
@app.websocket('/ws/v1/pixels')
|
||
|
async def function(
|
||
|
websocket: WebSocket,
|
||
|
):
|
||
|
await manager.connect(websocket)
|
||
|
try:
|
||
|
await websocket.receive()
|
||
|
except WebSocketDisconnect:
|
||
|
manager.disconnect(websocket)
|
||
|
|
||
|
|
||
|
@app.get('/api/v1/pixels')
|
||
|
async def function():
|
||
|
pixels_co = database.get_pixels()
|
||
|
return Response(
|
||
|
content=models.Area(
|
||
|
pixels=await pixels_co,
|
||
|
).json(),
|
||
|
status_code=200,
|
||
|
headers=models.headers,
|
||
|
media_type='application/json',
|
||
|
)
|
||
|
|
||
|
|
||
|
@app.put('/api/v1/pixels')
|
||
|
async def function(
|
||
|
request: Request,
|
||
|
x: int = Body(
|
||
|
...,
|
||
|
ge=0,
|
||
|
le=models.area_width - 1,
|
||
|
),
|
||
|
y: int = Body(
|
||
|
...,
|
||
|
ge=0,
|
||
|
le=models.area_height - 1,
|
||
|
),
|
||
|
color: int = Body(
|
||
|
...,
|
||
|
ge=0x000000,
|
||
|
le=0xFFFFFF,
|
||
|
),
|
||
|
):
|
||
|
await database.put_pixel(
|
||
|
x=x,
|
||
|
y=y,
|
||
|
color=color,
|
||
|
ip_address=request.client.host,
|
||
|
)
|
||
|
await manager.broadcast({
|
||
|
'x': x,
|
||
|
'y': y,
|
||
|
'color': color,
|
||
|
})
|
||
|
return Response(
|
||
|
status_code=201,
|
||
|
headers=models.headers,
|
||
|
)
|
||
|
|
||
|
|
||
|
@app.options('/api/v1/pixels')
|
||
|
async def function():
|
||
|
return Response(
|
||
|
headers=models.headers,
|
||
|
)
|