diff --git a/.gitignore b/.gitignore index 2eea525..6d78c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +summerizer.py \ No newline at end of file diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..ddceb2f --- /dev/null +++ b/api/__init__.py @@ -0,0 +1 @@ +#api/__init__.py \ No newline at end of file diff --git a/api/auth.py b/api/auth.py new file mode 100644 index 0000000..bb97713 --- /dev/null +++ b/api/auth.py @@ -0,0 +1,42 @@ +#api/auth.py +from flask import Blueprint, session, redirect, url_for, jsonify +from authlib.integrations.flask_client import OAuth +import env_provider +auth_bp = Blueprint('auth', __name__, url_prefix='/api') + +oauth = OAuth() +keycloak = oauth.register( + 'keycloak', + client_id=env_provider.CLIENT_ID, + client_secret=env_provider.CLIENT_SECRET, + server_metadata_url="https://login.hangman-lab.top/auth/realms/Hangman-Lab/.well-known/openid-configuration", + client_kwargs={"scope": "openid email profile"}, +) + +@auth_bp.route('/login', methods=['GET']) +def login(): + redirect_uri = url_for("auth.authorize", _external=True) + return keycloak.authorize_redirect(redirect_uri) + +@auth_bp.route('/authorize', methods=['GET']) +def authorize(): + try: + token = keycloak.authorize_access_token() + user_info = keycloak.parse_id_token(token) + session['user'] = user_info + return jsonify({"message": "login successful", "user": user_info}) + except Exception as e: + return jsonify({"error": "Authorization failed"}), 401 +@auth_bp.route('/logout', methods=['GET']) +def logout(): + session.pop('user', None) + logout_url = "https://login.hangman-lab.top/auth/realms/Hangman-Lab/protocol/openid-connect/logout" + return redirect(logout_url) + +@auth_bp.route("/user", methods=["GET"]) +def user(): + u = session.get('user') + if not u: + return jsonify({"username": "guest", "role": "guest"}) + return jsonify(u) + diff --git a/api/markdown.py b/api/markdown.py new file mode 100644 index 0000000..27f412e --- /dev/null +++ b/api/markdown.py @@ -0,0 +1,51 @@ +#api/markdown.py +from flask import Blueprint, request, jsonify +from db import get_db +from db.models.Markdown import Markdown + +markdown_bp = Blueprint('markdown', __name__, url_prefix='/api/markdown') + +@markdown_bp.route('/', methods=['GET']) +def get_markdown(markdown_id): + with get_db() as db: + markdown = db.query(Markdown).get(markdown_id) + if markdown is None: + return jsonify({"error": "file not found"}), 404 + return jsonify(markdown.to_dict()) + +@markdown_bp.route('/', methods=['POST']) +def create_markdown(): + data = request.json + title = data.get('title') + content = data.get('content') + path = data.get('path') + if not title or not content: + 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 + +@markdown_bp.route('/', methods=['PUT']) +def update_markdown(markdown_id): + with get_db() as db: + markdown = db.query(Markdown).get(markdown_id) + if markdown is None: + return jsonify({"error": "file not found"}), 404 + data = request.json + markdown.title = data.get('title') + markdown.content = data.get('content') + markdown.path = data.get('path') + db.commit() + return jsonify(markdown.to_dict()), 200 + +@markdown_bp.route('/', methods=['DELETE']) +def delete_markdown(markdown_id): + with get_db() as db: + markdown = db.query(Markdown).get(markdown_id) + if markdown is None: + return jsonify({"error": "file not found"}), 404 + db.delete(markdown) + db.commit() + return jsonify({"message": "deleted"}), 200 \ No newline at end of file diff --git a/app.py b/app.py index f407c0e..0140ac9 100644 --- a/app.py +++ b/app.py @@ -1,51 +1,30 @@ -from authlib.integrations import flask_client -from flask import Flask, jsonify, request, url_for, redirect, session +# app.py +from urllib.parse import urlparse + +from flask import Flask from flask_cors import CORS -from authlib.integrations.flask_client import OAuth -import os +import env_provider +import db +from api.auth import auth_bp +from api.markdown import markdown_bp + +def is_allowed_origin(origin): + parsed_origin = urlparse(origin) + if parsed_origin.hostname in ['localhost', '127.0.0.1']: + return True + allowed_origins = [ + "https://login.hangman-lab.top", + "https://git.hangman-lab.top", + ] + return origin in allowed_origins + +db.create_all() app = Flask(__name__) -CORS(app) -oauth = OAuth(app) - -keycloak = oauth.register( - 'keycloak', - client_id="main", - client_secret="", - server_metadata_url="https://login.hangman-lab.top/auth/realms/Hangman-Lab/.well-known/openid-configuration", - client_kwargs={"scope": "openid email profile"}, -) - - -@app.route('/api/login') -def login(): - redirect_uri = url_for("authorize", _external=True) - return keycloak.authorize_redirect(redirect_uri) - -@app.route('/api/authorize') -def authorize(): - token = keycloak.authorize_access_token() - user_info = keycloak.parse_id_token(token) - session['user'] = user_info - -@app.route('/api/logout') -def logout(): - session.pop('user', None) - logout_url = "https://login.hangman-lab.top/auth/realms/Hangman-Lab/protocol/openid-connect/logout" - return redirect(logout_url) - -@app.route("/api/user") -def user(): - u = session.get('user') - if not u: - return jsonify({"username": "guest", "role": "guest"}) - return jsonify(user) - -@app.route('/api/get_note_list', methods=['GET']) -def get_note_list(): - raise NotImplementedError - -@app.route('/api/get_note', methods=['GET']) -def get_note(idx): - raise NotImplementedError +app.secret_key = env_provider.SESSION_SECRET_KEY +CORS(app, resources={r"/api/*": {"origins": is_allowed_origin}}) +app.register_blueprint(markdown_bp) +app.register_blueprint(auth_bp) +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..902c784 --- /dev/null +++ b/db/__init__.py @@ -0,0 +1,23 @@ +#db/__init__.py +from contextlib import contextmanager +from models import Base + + +from sqlalchemy.orm import sessionmaker +from sqlalchemy import create_engine +import env_provider + +engine = create_engine(env_provider.DB_CONNECT_STRING) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +@contextmanager +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +def create_all(): + with engine.begin() as conn: + Base.metadata.create_all(bind=conn) \ No newline at end of file diff --git a/db/models/BackendLog.py b/db/models/BackendLog.py new file mode 100644 index 0000000..8fcb2ce --- /dev/null +++ b/db/models/BackendLog.py @@ -0,0 +1,21 @@ +#db/models/BackendLog.py +from sqlalchemy import Column, Integer, String, DateTime, Text +from db.models import Base +import datetime + + +class BackendLog(Base): + __tablename__ = 'backend_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) + extra = Column(Text, nullable=True) + def to_dict(self): + return { + "id": self.id, + "level": self.level, + "message": self.message, + "timestamp": self.timestamp, + "extra": self.extra, + } diff --git a/db/models/Markdown.py b/db/models/Markdown.py new file mode 100644 index 0000000..002a8d3 --- /dev/null +++ b/db/models/Markdown.py @@ -0,0 +1,21 @@ +#db/models/Markdown.py +from sqlalchemy import Column, Text, Integer, String, DateTime +from db.models import Base +import datetime + +class Markdown(Base): + __tablename__ = 'markdown' + id = Column(Integer, primary_key=True) + title = Column(String(255), nullable=False) + content = Column(Text, nullable=False) + created_at = Column(DateTime, default=datetime.datetime.utcnow) + path = Column(String(255), nullable=False) + + def to_dict(self): + return { + 'id': self.id, + 'title': self.title, + 'content': self.content, + 'created_at': self.created_at, + 'path': self.path, + } diff --git a/db/models/__init__.py b/db/models/__init__.py new file mode 100644 index 0000000..5bf6f52 --- /dev/null +++ b/db/models/__init__.py @@ -0,0 +1,10 @@ +#db/models/__init__.py + +import pkgutil +import importlib +from sqlalchemy.ext.declarative import declarative_base +Base = declarative_base() +package_name = "db.models" +for _, module_name, _ in pkgutil.iter_modules(__path__): + importlib.import_module(f"{package_name}.{module_name}") + diff --git a/db/utils.py b/db/utils.py new file mode 100644 index 0000000..9328fb7 --- /dev/null +++ b/db/utils.py @@ -0,0 +1,9 @@ +#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) + with get_db() as db: + db.add(log_entry) + db.commit() \ No newline at end of file diff --git a/env_provider.py b/env_provider.py new file mode 100644 index 0000000..1487a64 --- /dev/null +++ b/env_provider.py @@ -0,0 +1,10 @@ +#env_provider.py +import os +from dotenv import load_dotenv + +load_dotenv() + +CLIENT_ID = os.getenv("CLIENT_ID") +CLIENT_SECRET = os.getenv("CLIENT_SECRET") +DB_CONNECT_STRING = os.getenv("DB_CONNECT_STRING") +SESSION_SECRET_KEY = os.getenv("SESSION_SECRET_KEY") \ No newline at end of file diff --git a/logging_handlers/DatabaseLogHandler.py b/logging_handlers/DatabaseLogHandler.py new file mode 100644 index 0000000..88e55bf --- /dev/null +++ b/logging_handlers/DatabaseLogHandler.py @@ -0,0 +1,19 @@ +#logging_handlers/DatabaseLogHandler.py + +import logging +import traceback +from db import get_db +from db.models.BackendLog import BackendLog +class DatabaseLogHandler(logging.Handler): + def emit(self, record): + message = self.format(record) + level = record.levelname + extra = getattr(record, 'extra', None) + + log_entry = BackendLog(message=message, level=level, extra=extra) + try: + with get_db() as db: + db.add(log_entry) + db.commit() + except Exception: + print(f"Failed to log to database: {traceback.format_exc()}") \ No newline at end of file diff --git a/logging_handlers/__init__.py b/logging_handlers/__init__.py new file mode 100644 index 0000000..6305613 --- /dev/null +++ b/logging_handlers/__init__.py @@ -0,0 +1 @@ +#logging_handlers/__init__.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29