resource impl
This commit is contained in:
@@ -1 +1,18 @@
|
|||||||
#api/__init__.py
|
#api/__init__.py
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
from flask import jsonify, session
|
||||||
|
|
||||||
|
|
||||||
|
def require_auth(roles=[]):
|
||||||
|
def decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
user = session.get('user')
|
||||||
|
if not user:
|
||||||
|
return jsonify({"error": "Unauthorized"}), 401
|
||||||
|
if user.get('role') not in roles:
|
||||||
|
return jsonify({"error": "Forbidden, permission denied"}), 403
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#api/auth.py
|
#api/auth.py
|
||||||
from flask import Blueprint, session, redirect, url_for, jsonify
|
from flask import Blueprint, session, redirect, url_for, jsonify
|
||||||
from authlib.integrations.flask_client import OAuth
|
from authlib.integrations.flask_client import OAuth
|
||||||
|
from contexts.RequestContext import RequestContext
|
||||||
import env_provider
|
import env_provider
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -29,7 +30,8 @@ def authorize():
|
|||||||
return jsonify({"message": "login successful", "user": user_info})
|
return jsonify({"message": "login successful", "user": user_info})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Authorization failed: {str(e)}")
|
logger.error(f"Authorization failed: {str(e)}")
|
||||||
return jsonify({"error": "Authorization failed"}), 401
|
errno = RequestContext.get_error_id()
|
||||||
|
return jsonify({"error": f"Authorization failed - {errno}"}), 401
|
||||||
@auth_bp.route('/logout', methods=['GET'])
|
@auth_bp.route('/logout', methods=['GET'])
|
||||||
def logout():
|
def logout():
|
||||||
u = session.pop('user', None)
|
u = session.pop('user', None)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#api/markdown.py
|
#api/markdown.py
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
|
|
||||||
|
from api import require_auth
|
||||||
|
from contexts.RequestContext import RequestContext
|
||||||
from db import get_db
|
from db import get_db
|
||||||
from db.models.Markdown import Markdown
|
from db.models.Markdown import Markdown
|
||||||
import logging
|
import logging
|
||||||
@@ -16,6 +19,7 @@ def get_markdown(markdown_id):
|
|||||||
return jsonify(markdown.to_dict())
|
return jsonify(markdown.to_dict())
|
||||||
|
|
||||||
@markdown_bp.route('/', methods=['POST'])
|
@markdown_bp.route('/', methods=['POST'])
|
||||||
|
@require_auth(roles=['admin', 'creator'])
|
||||||
def create_markdown():
|
def create_markdown():
|
||||||
data = request.json
|
data = request.json
|
||||||
title = data.get('title')
|
title = data.get('title')
|
||||||
@@ -31,10 +35,12 @@ def create_markdown():
|
|||||||
return jsonify(new_markdown.to_dict()), 201
|
return jsonify(new_markdown.to_dict()), 201
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"failed to create markdown: {e}")
|
logger.error(f"failed to create markdown: {e}")
|
||||||
|
errno = RequestContext.get_error_id()
|
||||||
db.rollback()
|
db.rollback()
|
||||||
return jsonify({"error": "create failed"}), 500
|
return jsonify({"error": f"create failed - {errno}"}), 500
|
||||||
|
|
||||||
@markdown_bp.route('/<int:markdown_id>', methods=['PUT'])
|
@markdown_bp.route('/<int:markdown_id>', methods=['PUT'])
|
||||||
|
@require_auth(roles=['admin', 'creator'])
|
||||||
def update_markdown(markdown_id):
|
def update_markdown(markdown_id):
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
markdown = db.query(Markdown).get(markdown_id)
|
markdown = db.query(Markdown).get(markdown_id)
|
||||||
@@ -48,11 +54,14 @@ def update_markdown(markdown_id):
|
|||||||
return jsonify(markdown.to_dict()), 200
|
return jsonify(markdown.to_dict()), 200
|
||||||
|
|
||||||
@markdown_bp.route('/<int:markdown_id>', methods=['DELETE'])
|
@markdown_bp.route('/<int:markdown_id>', methods=['DELETE'])
|
||||||
|
@require_auth(roles=['admin'])
|
||||||
def delete_markdown(markdown_id):
|
def delete_markdown(markdown_id):
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
markdown = db.query(Markdown).get(markdown_id)
|
markdown = db.query(Markdown).get(markdown_id)
|
||||||
if markdown is None:
|
if markdown is None:
|
||||||
return jsonify({"error": "file not found"}), 404
|
logger.error(f"failed to delete markdown: {markdown_id}")
|
||||||
|
errno = RequestContext.get_error_id()
|
||||||
|
return jsonify({"error": f"file not found - {errno}"}), 404
|
||||||
db.delete(markdown)
|
db.delete(markdown)
|
||||||
db.commit()
|
db.commit()
|
||||||
return jsonify({"message": "deleted"}), 200
|
return jsonify({"message": "deleted"}), 200
|
||||||
53
api/resource.py
Normal file
53
api/resource.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#api/resource.py
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
resource_bp = Blueprint('resource', __name__, url_prefix='/api/resource')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
@resource_bp.route('/<identifier>', methods=['GET'])
|
||||||
|
def get_resource(identifier):
|
||||||
|
with get_db() as db:
|
||||||
|
resource = db.query(Resource).get(identifier)
|
||||||
|
if resource is None:
|
||||||
|
logger.error(f"resource not found: {identifier}")
|
||||||
|
errno = RequestContext.get_error_id()
|
||||||
|
return jsonify({"error": f"resource not found - {errno}"}), 404
|
||||||
|
return jsonify(resource.to_dict()), 200
|
||||||
|
|
||||||
|
@resource_bp.route('/', methods=['POST'])
|
||||||
|
@require_auth(roles=["admin", "creator"])
|
||||||
|
def create_resource():
|
||||||
|
data = request.get_json()
|
||||||
|
identifier = data.get('id')
|
||||||
|
content = data.get('content')
|
||||||
|
data_type = data.get('data_type')
|
||||||
|
if not identifier or not content or not data_type:
|
||||||
|
return jsonify({"error": "missing required fields"}), 400
|
||||||
|
resource_entry = Resource(id=identifier, content=content, data_type=data_type)
|
||||||
|
with get_db() as db:
|
||||||
|
try:
|
||||||
|
db.add(resource_entry)
|
||||||
|
db.commit()
|
||||||
|
return jsonify(resource_entry.to_dict()), 201
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
logger.error(f"Failed to create resource: {e}")
|
||||||
|
errno = RequestContext.get_error_id()
|
||||||
|
return jsonify({"error": f"failed to create resource - {errno}"}), 500
|
||||||
|
|
||||||
|
@resource_bp.route('/<identifier>', methods=['DELETE'])
|
||||||
|
@require_auth(roles=["admin"])
|
||||||
|
def delete_resource(identifier):
|
||||||
|
with get_db() as db:
|
||||||
|
resource = db.query(Resource).get(identifier)
|
||||||
|
if not resource:
|
||||||
|
logger.error(f"resource not found: {identifier}")
|
||||||
|
errno = RequestContext.get_error_id()
|
||||||
|
return jsonify({"error": f"Resource not found - {errno}"}), 404
|
||||||
|
db.delete(resource)
|
||||||
|
db.commit()
|
||||||
|
return jsonify({"message": "Resource deleted"}), 200
|
||||||
2
app.py
2
app.py
@@ -10,6 +10,7 @@ from api.log import logs_bp
|
|||||||
from api.markdown import markdown_bp
|
from api.markdown import markdown_bp
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from api.resource import resource_bp
|
||||||
from logging_handlers.DatabaseLogHandler import DatabaseLogHandler
|
from logging_handlers.DatabaseLogHandler import DatabaseLogHandler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -39,6 +40,7 @@ CORS(app, resources={r"/api/*": {"origins": is_allowed_origin}})
|
|||||||
app.register_blueprint(markdown_bp)
|
app.register_blueprint(markdown_bp)
|
||||||
app.register_blueprint(auth_bp)
|
app.register_blueprint(auth_bp)
|
||||||
app.register_blueprint(logs_bp)
|
app.register_blueprint(logs_bp)
|
||||||
|
app.register_blueprint(resource_bp)
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def log_request():
|
def log_request():
|
||||||
if request.path.startswith("/api/log"):
|
if request.path.startswith("/api/log"):
|
||||||
|
|||||||
12
contexts/RequestContext.py
Normal file
12
contexts/RequestContext.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import threading
|
||||||
|
class RequestContext:
|
||||||
|
_thread_local = threading.local()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_error_id(cls, error_id):
|
||||||
|
cls._thread_local.error_id = error_id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_error_id(cls):
|
||||||
|
return getattr(cls._thread_local, "error_id", None)
|
||||||
|
|
||||||
0
contexts/__init__.py
Normal file
0
contexts/__init__.py
Normal file
16
db/models/Resource.py
Normal file
16
db/models/Resource.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#db/models/Resource.py
|
||||||
|
from sqlalchemy import Column, Text, LargeBinary
|
||||||
|
from db.models import Base
|
||||||
|
|
||||||
|
class Resource(Base):
|
||||||
|
__tablename__ = 'resources'
|
||||||
|
id = Column(Text, primary_key=True)
|
||||||
|
content = Column(LargeBinary, nullable=False)
|
||||||
|
data_type = Column(Text, nullable=False)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return{
|
||||||
|
"id": self.id,
|
||||||
|
"data_type": self.data_type,
|
||||||
|
"content": self.content.decode('utf-8'),
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#logging_handlers/DatabaseLogHandler.py
|
#logging_handlers/DatabaseLogHandler.py
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
from db import get_db
|
from db import get_db
|
||||||
from db.models.Log import Log
|
from db.models.Log import Log
|
||||||
|
from contexts.RequestContext import RequestContext
|
||||||
class DatabaseLogHandler(logging.Handler):
|
class DatabaseLogHandler(logging.Handler):
|
||||||
def __init__(self, application="backend"):
|
def __init__(self, application="backend"):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -18,5 +18,6 @@ class DatabaseLogHandler(logging.Handler):
|
|||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
db.add(log_entry)
|
db.add(log_entry)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
RequestContext.set_error_id(log_entry.id)
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f"Failed to log to database: {traceback.format_exc()}")
|
print(f"failed to log")
|
||||||
Reference in New Issue
Block a user