Compare commits

..

1 Commits

Author SHA1 Message Date
038efb745a add: etag support 2024-12-09 08:00:25 +00:00
5 changed files with 40 additions and 6 deletions

View File

@@ -1,11 +1,10 @@
#api/__init__.py
import base64
import os
from functools import wraps
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from flask import jsonify, Blueprint, request
from flask import jsonify, Blueprint, request, make_response
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from jwt import decode, ExpiredSignatureError, InvalidTokenError, get_unverified_header
@@ -14,6 +13,11 @@ import requests
from threading import Lock
import env_provider
import hashlib
import json
_public_key_cache = {}
_lock = Lock()
@@ -135,3 +139,24 @@ def register_blueprints(app):
app.register_blueprint(bp)
def generate_etag(data):
serialized = json.dumps(data, sort_keys=True).encode('utf-8')
return hashlib.md5(serialized).hexdigest()
def etag_response(f):
@wraps(f)
def decorator(*args, **kwargs):
response = f(*args, **kwargs)
if response[1] in (200, 201):
if isinstance(response[0], (dict, list)):
etag = generate_etag(response[0])
if_none_match = request.headers.get("if_none_match")
if if_none_match == etag:
return jsonify({}), 200
resp = make_response(response[0], response[1])
resp.headers["ETag"] = etag
return resp
return response
return decorator

View File

@@ -1,6 +1,6 @@
from flask import Blueprint, jsonify, request
from api import require_auth, rate_limits
from api import require_auth, rate_limits, etag_response
import re
config_bp = Blueprint('config', __name__, url_prefix='/api/config')
@@ -10,6 +10,7 @@ def is_valid_rate_limit(limit):
return bool(RATE_LIMIT_REGEX.match(limit))
@config_bp.route('/limits', methods=['GET'])
@require_auth(roles=['admin'])
@etag_response
def limits():
return jsonify(rate_limits), 200

View File

@@ -2,7 +2,7 @@
from flask import Blueprint, request, jsonify
import api
from api import require_auth
from api import require_auth, etag_response
from contexts.RequestContext import RequestContext
from db import get_db
from db.models.Markdown import Markdown
@@ -14,6 +14,7 @@ markdown_bp = Blueprint('markdown', __name__, url_prefix='/api/markdown')
@markdown_bp.route('/', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_markdowns():
with get_db() as session:
mds = session.query(Markdown).all()
@@ -21,12 +22,14 @@ def get_markdowns():
@markdown_bp.route('/by_path/<int:path_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_markdowns_by_path(path_id):
with get_db() as session:
markdowns = session.query(Markdown).filter(Markdown.path_id == path_id).all()
return jsonify([md.to_dict() for md in markdowns]), 200
@markdown_bp.route('/get_index/<int:path_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_index(path_id):
with get_db() as session:
markdown = session.query(Markdown).filter(Markdown.path_id == path_id).filter(Markdown.title == "index").first()
@@ -38,6 +41,7 @@ def get_index(path_id):
@markdown_bp.route('/<int:markdown_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_markdown(markdown_id):
with get_db() as session:
markdown = session.query(Markdown).get(markdown_id)

View File

@@ -1,7 +1,7 @@
from flask import Blueprint, request, jsonify
import api
from api import require_auth
from api import require_auth, etag_response
from db import get_db
from db.models.Markdown import Markdown
from db.models.Path import Path
@@ -13,6 +13,7 @@ path_bp = Blueprint('path', __name__, url_prefix='/api/path')
@path_bp.route('/', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_root_paths():
with get_db() as session:
paths = session.query(Path).filter(Path.parent_id == 1)
@@ -20,6 +21,7 @@ def get_root_paths():
@path_bp.route('/<int:path_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_path(path_id):
with get_db() as session:
path = session.query(Path).get(path_id)
@@ -29,6 +31,7 @@ def get_path(path_id):
@path_bp.route('/parent/<int:parent_id>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
@etag_response
def get_path_by_parent(parent_id):
with get_db() as session:
paths = session.query(Path).filter(Path.parent_id == parent_id).all()

View File

@@ -4,12 +4,13 @@ from flask import Blueprint, jsonify, request
from contexts.RequestContext import RequestContext
from db import get_db
from db.models.Resource import Resource
from api import require_auth, limiter
from api import require_auth, limiter, etag_response
import logging
resource_bp = Blueprint('resource', __name__, url_prefix='/api/resource')
logger = logging.getLogger(__name__)
@resource_bp.route('/<identifier>', methods=['GET'])
@limiter.limit(api.get_rate_limit)
def get_resource(identifier):
with get_db() as session:
resource = session.query(Resource).get(identifier)