api impl
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.env
|
||||
.env
|
||||
summerizer.py
|
||||
1
api/__init__.py
Normal file
1
api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#api/__init__.py
|
||||
42
api/auth.py
Normal file
42
api/auth.py
Normal file
@@ -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)
|
||||
|
||||
51
api/markdown.py
Normal file
51
api/markdown.py
Normal file
@@ -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('/<int:markdown_id>', 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('/<int:markdown_id>', 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('/<int:markdown_id>', 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
|
||||
73
app.py
73
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="<KEY>",
|
||||
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)
|
||||
|
||||
23
db/__init__.py
Normal file
23
db/__init__.py
Normal file
@@ -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)
|
||||
21
db/models/BackendLog.py
Normal file
21
db/models/BackendLog.py
Normal file
@@ -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,
|
||||
}
|
||||
21
db/models/Markdown.py
Normal file
21
db/models/Markdown.py
Normal file
@@ -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,
|
||||
}
|
||||
10
db/models/__init__.py
Normal file
10
db/models/__init__.py
Normal file
@@ -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}")
|
||||
|
||||
9
db/utils.py
Normal file
9
db/utils.py
Normal file
@@ -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()
|
||||
10
env_provider.py
Normal file
10
env_provider.py
Normal file
@@ -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")
|
||||
19
logging_handlers/DatabaseLogHandler.py
Normal file
19
logging_handlers/DatabaseLogHandler.py
Normal file
@@ -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()}")
|
||||
1
logging_handlers/__init__.py
Normal file
1
logging_handlers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#logging_handlers/__init__.py
|
||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
Reference in New Issue
Block a user