diff --git a/api/auth.py b/api/auth.py index bb97713..8744e50 100644 --- a/api/auth.py +++ b/api/auth.py @@ -2,6 +2,8 @@ from flask import Blueprint, session, redirect, url_for, jsonify from authlib.integrations.flask_client import OAuth import env_provider +import logging +logger = logging.getLogger(__name__) auth_bp = Blueprint('auth', __name__, url_prefix='/api') oauth = OAuth() @@ -26,10 +28,13 @@ def authorize(): session['user'] = user_info return jsonify({"message": "login successful", "user": user_info}) except Exception as e: + logger.error(f"Authorization failed: {str(e)}") return jsonify({"error": "Authorization failed"}), 401 @auth_bp.route('/logout', methods=['GET']) def logout(): - session.pop('user', None) + u = session.pop('user', None) + if u: + logger.info(f"Logged out user: {u}") logout_url = "https://login.hangman-lab.top/auth/realms/Hangman-Lab/protocol/openid-connect/logout" return redirect(logout_url) diff --git a/api/log.py b/api/log.py new file mode 100644 index 0000000..4513b8b --- /dev/null +++ b/api/log.py @@ -0,0 +1,43 @@ +#api/log.py +from flask import Blueprint, jsonify, request +from db import get_db +from db.models.Log import Log +from db.utils import insert_log + +logs_bp = Blueprint('log', __name__, url_prefix='/api/log') + +@logs_bp.route('/', methods=['GET']) +def get_logs(): + level = request.args.get('level') + application = request.args.get('application') + page = int(request.args.get('page', 1)) + per_page = int(request.args.get('per_page', 10)) + + with get_db() as db: + query = db.query(Log) + if level: + query = query.filter(Log.level == level) + if application: + query = query.filter(Log.application == application) + total_logs = query.count() + logs = query.order_by(Log.timestamp.desc()).offset((page - 1)*per_page).limit(per_page).all() + return jsonify({ + "total": total_logs, + "page": page, + "per_page": per_page, + "logs": [log.to_dict() for log in logs] + }) + +@logs_bp.route('/', methods=['POST']) +def create_log(): + data = request.json + required_fields = ['level', 'message'] + for field in required_fields: + if field not in data: + return jsonify({"error": f"missing {field} in request"}), 400 + level = data.get('level') + message = data.get('message') + application = "frontend" + extra = data.get('extra', None) + log_entry = Log(level=level, message=message, application=application, extra=extra) + insert_log(log_entry) diff --git a/api/markdown.py b/api/markdown.py index 27f412e..bc1d4b0 100644 --- a/api/markdown.py +++ b/api/markdown.py @@ -2,6 +2,8 @@ from flask import Blueprint, request, jsonify from db import get_db from db.models.Markdown import Markdown +import logging +logger = logging.getLogger(__name__) markdown_bp = Blueprint('markdown', __name__, url_prefix='/api/markdown') @@ -23,9 +25,14 @@ def create_markdown(): return jsonify({"error": "missing required fields"}), 400 new_markdown = Markdown(title=title, content=content, path=path) with get_db() as db: - db.add(new_markdown) - db.commit() - return jsonify(new_markdown.to_dict()), 201 + try: + db.add(new_markdown) + db.commit() + return jsonify(new_markdown.to_dict()), 201 + except Exception as e: + logger.error(f"failed to create markdown: {e}") + db.rollback() + return jsonify({"error": "create failed"}), 500 @markdown_bp.route('/', methods=['PUT']) def update_markdown(markdown_id): diff --git a/app.py b/app.py index 0140ac9..be1fd5c 100644 --- a/app.py +++ b/app.py @@ -1,13 +1,25 @@ # app.py from urllib.parse import urlparse -from flask import Flask +from flask import Flask, request from flask_cors import CORS import env_provider import db from api.auth import auth_bp +from api.log import logs_bp from api.markdown import markdown_bp +import logging +from logging_handlers.DatabaseLogHandler import DatabaseLogHandler + +logger = logging.getLogger(__name__) +db_handler = DatabaseLogHandler(application="backend") + +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +db_handler.setFormatter(formatter) + +logger.addHandler(db_handler) +logger.setLevel(logging.INFO) def is_allowed_origin(origin): parsed_origin = urlparse(origin) @@ -26,5 +38,14 @@ CORS(app, resources={r"/api/*": {"origins": is_allowed_origin}}) app.register_blueprint(markdown_bp) app.register_blueprint(auth_bp) +app.register_blueprint(logs_bp) +@app.before_request +def log_request(): + if request.path.startswith("/api/log"): + return + logger.info(f"Request received: {request.method} {request.path} from {request.remote_addr}") + + if __name__ == '__main__': + logger.info("Starting app") app.run(host='0.0.0.0', port=5000) diff --git a/db/models/BackendLog.py b/db/models/Log.py similarity index 68% rename from db/models/BackendLog.py rename to db/models/Log.py index 8fcb2ce..ea79a89 100644 --- a/db/models/BackendLog.py +++ b/db/models/Log.py @@ -1,16 +1,17 @@ -#db/models/BackendLog.py +#db/models/Log.py from sqlalchemy import Column, Integer, String, DateTime, Text from db.models import Base import datetime -class BackendLog(Base): - __tablename__ = 'backend_log' +class Log(Base): + __tablename__ = 'log' id = Column(Integer, primary_key=True, autoincrement=True) level = Column(String(50), nullable=False) message = Column(Text, nullable=False) - timestamp = Column(DateTime, nullable=False) + timestamp = Column(DateTime, default=datetime.datetime.utcnow, nullable=False) extra = Column(Text, nullable=True) + application = Column(String(50), nullable=False) def to_dict(self): return { "id": self.id, @@ -18,4 +19,5 @@ class BackendLog(Base): "message": self.message, "timestamp": self.timestamp, "extra": self.extra, + "application": self.application, } diff --git a/db/utils.py b/db/utils.py index 9328fb7..600e4fc 100644 --- a/db/utils.py +++ b/db/utils.py @@ -1,9 +1,7 @@ #db/utils.py from db import get_db -from db.models.BackendLog import BackendLog -def insert_backend_log(level, message, extra=None): - log_entry = BackendLog(level=level, message=message, extra=extra) +def insert_log(log_entry): with get_db() as db: db.add(log_entry) db.commit() \ No newline at end of file diff --git a/logging_handlers/DatabaseLogHandler.py b/logging_handlers/DatabaseLogHandler.py index 88e55bf..87b48ed 100644 --- a/logging_handlers/DatabaseLogHandler.py +++ b/logging_handlers/DatabaseLogHandler.py @@ -3,14 +3,17 @@ import logging import traceback from db import get_db -from db.models.BackendLog import BackendLog +from db.models.Log import Log class DatabaseLogHandler(logging.Handler): + def __init__(self, application="backend"): + super().__init__() + self.application = application def emit(self, record): message = self.format(record) level = record.levelname - extra = getattr(record, 'extra', None) + extra = str(getattr(record, 'extra', None)) if hasattr(record, 'extra') else None - log_entry = BackendLog(message=message, level=level, extra=extra) + log_entry = Log(message=message, level=level, application=self.application, extra=extra) try: with get_db() as db: db.add(log_entry)