Atualizar app/app.py
This commit is contained in:
+63
@@ -34,6 +34,60 @@ def notifications(ws):
|
|||||||
ws.send(json.dumps({"type": "pong", "payload": data}))
|
ws.send(json.dumps({"type": "pong", "payload": data}))
|
||||||
|
|
||||||
|
|
||||||
|
# ================
|
||||||
|
# DECORATORS
|
||||||
|
# ================
|
||||||
|
import structlog
|
||||||
|
|
||||||
|
logger = structlog.get_logger()
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def log_request():
|
||||||
|
logger.info(
|
||||||
|
"request",
|
||||||
|
path=request.path,
|
||||||
|
method=request.method,
|
||||||
|
remote=request.remote_addr,
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def log_response(response):
|
||||||
|
logger.info(
|
||||||
|
"response",
|
||||||
|
path=request.path,
|
||||||
|
status=response.status_code,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
from limits import RateLimitItemPerMinute
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
rate_store = {}
|
||||||
|
|
||||||
|
def rate_limited(f):
|
||||||
|
@wraps(f)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if app.config["AUTH_MODE"] == "oauth":
|
||||||
|
email = session["user"]["email"]
|
||||||
|
else:
|
||||||
|
email = session["db_user"]["email"]
|
||||||
|
|
||||||
|
key = f"{email}:{f.__name__}"
|
||||||
|
limit = RateLimitItemPerMinute(30) # 30 req/min por endpoint
|
||||||
|
|
||||||
|
now = int(time())
|
||||||
|
window = now // 60
|
||||||
|
|
||||||
|
used = rate_store.get((key, window), 0)
|
||||||
|
if used >= limit.amount:
|
||||||
|
return jsonify({"error": "Rate limit exceeded"}), 429
|
||||||
|
|
||||||
|
rate_store[(key, window)] = used + 1
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def admin_required(f):
|
def admin_required(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
@@ -290,6 +344,7 @@ def list_conversations():
|
|||||||
|
|
||||||
@app.route("/api/export/md/<cid>")
|
@app.route("/api/export/md/<cid>")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def export_md(cid):
|
def export_md(cid):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
conv = db.query(Conversation).filter_by(id=cid).first()
|
conv = db.query(Conversation).filter_by(id=cid).first()
|
||||||
@@ -314,6 +369,7 @@ from docx import Document
|
|||||||
|
|
||||||
@app.route("/api/import/<cid>", methods=["POST"])
|
@app.route("/api/import/<cid>", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def import_file(cid):
|
def import_file(cid):
|
||||||
file = request.files.get("file")
|
file = request.files.get("file")
|
||||||
if not file:
|
if not file:
|
||||||
@@ -351,6 +407,7 @@ def import_file(cid):
|
|||||||
|
|
||||||
@app.route("/api/export/pdf/<cid>")
|
@app.route("/api/export/pdf/<cid>")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def export_pdf(cid):
|
def export_pdf(cid):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
conv = db.query(Conversation).filter_by(id=cid).first()
|
conv = db.query(Conversation).filter_by(id=cid).first()
|
||||||
@@ -372,6 +429,7 @@ def export_pdf(cid):
|
|||||||
|
|
||||||
@app.route("/api/history/<cid>")
|
@app.route("/api/history/<cid>")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def history(cid):
|
def history(cid):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
msgs = db.query(Message).filter_by(conversation_id=cid).order_by(Message.created_at).all()
|
msgs = db.query(Message).filter_by(conversation_id=cid).order_by(Message.created_at).all()
|
||||||
@@ -381,6 +439,7 @@ def history(cid):
|
|||||||
|
|
||||||
@app.route("/api/attachment/<cid>", methods=["POST"])
|
@app.route("/api/attachment/<cid>", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def upload_attachment(cid):
|
def upload_attachment(cid):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
file = request.files.get("file")
|
file = request.files.get("file")
|
||||||
@@ -401,6 +460,7 @@ def upload_attachment(cid):
|
|||||||
|
|
||||||
@app.route("/api/attachments/<cid>")
|
@app.route("/api/attachments/<cid>")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def list_attachments(cid):
|
def list_attachments(cid):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
atts = db.query(Attachment).filter_by(conversation_id=cid).all()
|
atts = db.query(Attachment).filter_by(conversation_id=cid).all()
|
||||||
@@ -411,6 +471,7 @@ def list_attachments(cid):
|
|||||||
|
|
||||||
@app.route("/api/download/<att_id>")
|
@app.route("/api/download/<att_id>")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def download(att_id):
|
def download(att_id):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
att = db.query(Attachment).filter_by(id=att_id).first()
|
att = db.query(Attachment).filter_by(id=att_id).first()
|
||||||
@@ -424,6 +485,7 @@ def download(att_id):
|
|||||||
|
|
||||||
@app.route("/api/stream")
|
@app.route("/api/stream")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def stream():
|
def stream():
|
||||||
cid = request.args.get("conversation_id")
|
cid = request.args.get("conversation_id")
|
||||||
model = request.args.get("model", "llama3.2")
|
model = request.args.get("model", "llama3.2")
|
||||||
@@ -485,6 +547,7 @@ def update_tags(cid):
|
|||||||
|
|
||||||
@app.route("/api/search")
|
@app.route("/api/search")
|
||||||
@login_required
|
@login_required
|
||||||
|
@rate_limited
|
||||||
def search():
|
def search():
|
||||||
q = request.args.get("q", "").lower()
|
q = request.args.get("q", "").lower()
|
||||||
user_email = session["user"]["email"]
|
user_email = session["user"]["email"]
|
||||||
|
|||||||
Reference in New Issue
Block a user