Jelajahi Sumber

feat: 加入HBlog相关API

SongZihuan 2 tahun lalu
induk
melakukan
65b274bfeb
8 mengubah file dengan 362 tambahan dan 36 penghapusan
  1. 286 0
      app/api.py
  2. 2 1
      app/app.py
  3. 20 0
      app/http_auth.py
  4. 13 0
      app/tool.py
  5. 1 1
      object/blog.py
  6. 7 3
      object/user.py
  7. 1 1
      sql/user.py
  8. 32 30
      templates/archive/archive.html

+ 286 - 0
app/api.py

@@ -0,0 +1,286 @@
+from flask import Blueprint, jsonify, request, abort, g
+from configure import conf
+from json import loads
+from datetime import datetime
+
+from object.archive import Archive
+from object.blog import BlogArticle
+from object.msg import Message
+from object.comment import Comment
+from object.user import User
+
+from app.http_auth import http_auth
+from app.tool import api_role_required
+
+
+api = Blueprint("api", __name__)
+
+
+@api.route("/", methods=["GET", "POST"])
+def api_say_hello():
+    json = loads(request.get_json())
+    name = "unknown"
+    if json:
+        name = json.get("name", "unknown")
+    res = {"status": 200, name: "Hello!"}
+    return res
+
+
+@api.route("/get_introduce")
+@http_auth.login_required
+def api_get_introduce():
+    title = request.args.get("title").lower()
+
+    res = {"status": 200, "introduce": {}}
+    have_found = False
+    for info in conf['INTRODUCE']:
+        if title is None or title == info[0].lower():
+            res["introduce"][info[0]] = info[1]
+            have_found = True
+
+    if not have_found:
+        abort(404)
+    return jsonify(res)
+
+
+@api.route("/find_me")
+@http_auth.login_required
+def api_get_find_me():
+    where = request.args.get("where")
+    if where:
+        where = where.lower()
+
+    res = {"status": 200, "content": {}}
+    have_found = False
+    for i in conf['INTRODUCE_LINK']:
+        if where is None or where == i.lower():
+            res["content"][i] = conf['INTRODUCE_LINK'][i]
+            have_found = True
+
+    if not have_found:
+        abort(404)
+    return jsonify(res)
+
+
+@api.route("/archive_list")
+@http_auth.login_required
+@api_role_required("ReadBlog", "api get archive list")
+def api_get_archive_list():
+    archive_list = Archive.get_archive_list()
+    res = {"status": 200}
+    res_list = []
+    for i in archive_list:
+        res_list.append({
+            "name": i.name,
+            "describe": i.describe,
+            "count": i.count,
+            "id": i.id,
+        })
+
+    res["archive"] = res_list
+    return jsonify(res)
+
+
+@api.route("/archive/<int:archive_id>")
+@http_auth.login_required
+@api_role_required("ReadBlog", "api get archive")
+def get_get_archive(archive_id):
+    archive = Archive(archive_id)
+    if len(archive.name) == 0:
+        abort(404)
+    return {
+        "status": 200,
+        "archive": {
+            "name": archive.name,
+            "describe": archive.describe,
+            "count": archive.count,
+            "id": archive.id,
+        }
+    }
+
+
+@api.route("/archive_blog_list/<int:archive_id>/<int:page>")
+@http_auth.login_required
+@api_role_required("ReadBlog", "api get archive blog list")
+def api_get_archive_blog_list(archive_id: int, page: int):
+    blog_list = BlogArticle.get_blog_list(archive_id=archive_id, limit=20, offset=(page - 1) * 20)
+    res = {"status": 200}
+    res_list = []
+    for i in blog_list:
+        res_list.append({
+            "auth": i.user.id,
+            "title": i.title,
+            "subtitle": i.subtitle,
+            "update_time": datetime.timestamp(i.update_time),
+            "create_time": datetime.timestamp(i.create_time),
+            "top": i.top,
+            "id": i.id,
+        })
+
+    res["blog"] = res_list
+    return jsonify(res)
+
+
+@api.route("/blog_list/<int:page>")
+@http_auth.login_required
+@api_role_required("ReadBlog", "api get blog list")
+def api_get_blog_list(page: int):
+    blog_list = BlogArticle.get_blog_list(limit=20, offset=(page - 1) * 20)
+    res = {"status": 200}
+    res_list = []
+    for i in blog_list:
+        res_list.append({
+            "auth": i.user.id,
+            "title": i.title,
+            "subtitle": i.subtitle,
+            "update_time": datetime.timestamp(i.update_time),
+            "create_time": datetime.timestamp(i.create_time),
+            "top": i.top,
+            "id": i.id,
+        })
+
+    res["blog"] = res_list
+    return jsonify(res)
+
+
+
+@api.route("/blog/<int:blog_id>")
+@http_auth.login_required
+@api_role_required("ReadBlog", "api get blog")
+def api_get_blog(blog_id: int):
+    blog = BlogArticle(blog_id)
+    return {
+        "status": 200,
+        "blog": {
+            "auth": blog.user.id,
+            "title": blog.title,
+            "subtitle": blog.subtitle,
+            "update_time": datetime.timestamp(blog.update_time),
+            "create_time": datetime.timestamp(blog.create_time),
+            "top": blog.top,
+            "content": blog.content,
+            "id": blog.id,
+        }
+    }
+
+
+@api.route("/get_blog_comment/<int:blog_id>")
+@http_auth.login_required
+@api_role_required("ReadComment", "api get blog comment")
+def api_get_blog_comment(blog_id: int):
+    blog = BlogArticle(blog_id)
+    res = {"status": 200}
+    res_list = []
+    for i in blog.comment:
+        res_list.append({
+            "auth": i.auth.id,
+            "update_time": datetime.timestamp(i.update_time),
+            "id": i.id,
+        })
+
+    res["comment"] = res_list
+    return jsonify(res)
+
+
+@api.route("/comment/<int:comment_id>")
+@http_auth.login_required
+@api_role_required("ReadComment", "api get comment")
+def api_get_comment(comment_id: int):
+    comment = Comment(comment_id)
+    return {
+        "status": 200,
+        "blog": {
+            "auth": comment.auth.id,
+            "update_time": datetime.timestamp(comment.update_time),
+            "content": comment.content,
+            "id": comment.id,
+        }
+    }
+
+
+@api.route("/msg_list/<int:page>")
+@http_auth.login_required
+@api_role_required("ReadMsg", "api get msg list")
+def api_get_not_secret_msg_list(page: int):
+    msg_list = Message.get_message_list(20, (page - 1) * 20, request.args.get("secret", False))
+    res = {"status": 200}
+    res_list = []
+    for i in msg_list:
+        res_list.append({
+            "auth": i.auth.id,
+            "update_time": datetime.timestamp(i.update_time),
+            "id": i.id,
+        })
+
+    res["blog"] = res_list
+    return jsonify(res)
+
+
+@api.route("/s_msg_list/<int:page>")
+@http_auth.login_required
+@api_role_required("ReadMsg", "api get all msg secret list")
+@api_role_required("ReadSecretMsg", "api get all secret list")
+def api_get_secret_msg_list(page: int):
+    msg_list = Message.get_message_list(20, (page - 1) * 20, request.args.get("secret", True))
+    res = {"status": 200}
+    res_list = []
+    for i in msg_list:
+        res_list.append({
+            "auth": i.auth.id,
+            "update_time": datetime.timestamp(i.update_time),
+            "id": i.id,
+        })
+
+    res["blog"] = res_list
+    return jsonify(res)
+
+
+@api.route("/msg/<int:msg_id>")
+@http_auth.login_required
+@api_role_required("ReadMsg", "api get msg")
+def api_get_msg(msg_id: int):
+    msg = Message(msg_id)
+    if msg.secret:
+        abort(404)
+    return {
+        "status": 200,
+        "blog": {
+            "auth": msg.auth.id,
+            "update_time": datetime.timestamp(msg.update_time),
+            "content": msg.content,
+            "id": msg.id,
+        }
+    }
+
+
+@api.route("/s_msg/<int:msg_id>")
+@http_auth.login_required
+@api_role_required("ReadMsg", "api get secret msg")
+@api_role_required("ReadSecretMsg", "api get secret msg")
+def api_get_secret_msg(msg_id: int):
+    msg = Message(msg_id)
+    return {
+        "status": 200,
+        "blog": {
+            "auth": msg.auth.id,
+            "update_time": datetime.timestamp(msg.update_time),
+            "content": msg.content,
+            "id": msg.id,
+        }
+    }
+
+
+@api.route("/user/<int:user_id>")
+@http_auth.login_required
+@api_role_required("ReadUserInfo", "api get user info")
+def api_get_user(user_id: int):
+    user = User(user_id, is_id=True)
+    return {
+        "status": 200,
+        "blog": {
+            "role": user.role,
+            "email": user.email,
+            "id": user.id,
+        }
+    }
+

+ 2 - 1
app/app.py

@@ -15,6 +15,7 @@ from bs4 import BeautifulSoup
 from configure import conf
 from object.user import AnonymousUser, User
 from app.cache import cache
+from app.http_auth import http_auth
 
 if conf["DEBUG_PROFILE"]:
     from werkzeug.middleware.profiler import ProfilerMiddleware
@@ -38,6 +39,7 @@ class HBlogFlask(Flask):
         self.moment = Moment(self)
         self.cache = cache
         self.cache.init_app(self)
+        self.http_auth = http_auth
 
         self.logger.removeHandler(default_handler)
         self.logger.setLevel(conf["LOG_LEVEL"])
@@ -59,7 +61,6 @@ class HBlogFlask(Flask):
                 return None
             return user
 
-        res = []
         for i in [400, 401, 403, 404, 405, 408, 410, 413, 414, 423, 500, 501, 502]:
             def create_error_handle(status):
                 def error_handle(e):

+ 20 - 0
app/http_auth.py

@@ -0,0 +1,20 @@
+from flask import jsonify, g
+from flask_httpauth import HTTPBasicAuth
+from object.user import User
+
+
+http_auth = HTTPBasicAuth()
+
+
+@http_auth.verify_password
+def verify_passwd(email, passwd):
+    user = User(email)
+    g.user = user
+    return user.check_passwd(passwd)
+
+
+@http_auth.error_handler
+def unauthorized():
+    rsp = jsonify({"status": 403, 'error': 'Unauthorized access'})
+    rsp.status_code = 403
+    return rsp

+ 13 - 0
app/tool.py

@@ -18,6 +18,19 @@ def role_required(role: str, opt: str):
     return required
 
 
+
+def api_role_required(role: str, opt: str):
+    def required(func):
+        @wraps(func)
+        def new_func(*args, **kwargs):
+            if not g.user.check_role(role):  # 检查相应的权限
+                app.HBlogFlask.print_user_not_allow_opt_log(opt)
+                return abort(403)
+            return func(*args, **kwargs)
+        return new_func
+    return required
+
+
 def form_required(form: ClassVar[FlaskForm], opt: str, callback: Optional[Callable] = None, **kw):
     def required(func):
         @wraps(func)

+ 1 - 1
object/blog.py

@@ -67,7 +67,7 @@ class BlogArticle(_BlogArticle):
 
     @property
     def user(self):
-        return object.user.User(get_user_email(self.info.auth))
+        return object.user.User(self.info.auth, is_id=True)
 
     @property
     def title(self):

+ 7 - 3
object/user.py

@@ -15,7 +15,8 @@ from sql.user import (read_user,
                       delete_role,
                       set_user_role,
                       get_role_list,
-                      role_authority)
+                      role_authority,
+                      get_user_email)
 import object.blog
 import object.comment
 import object.msg
@@ -79,8 +80,11 @@ class _User(UserMixin):
 class User(_User):
     RoleAuthorize = role_authority
 
-    def __init__(self, email):
-        self.email = email
+    def __init__(self, email, is_id=False):
+        if is_id:
+            self.email = get_user_email(email)
+        else:
+            self.email = email
 
     def get_id(self):
         """Flask要求的方法"""

+ 1 - 1
sql/user.py

@@ -89,7 +89,7 @@ def get_user_email(user_id):
         return None
 
     res = cur.fetchone()[0]
-    write_user_email_to_cache(res)
+    write_user_email_to_cache(user_id, res)
     return res
 
 

+ 32 - 30
templates/archive/archive.html

@@ -47,38 +47,40 @@
         </div>
     {% endif %}
 
-    <section id="base" class="d-flex mt-3 container justify-content-center flex-wrap">
-        {% for archive in archive_list %}
-            <div class="archive mb-3 mx-2">
-                <h3> {{ archive.name }} </h3>
-                <hr>
-                <p class="archive_describe"> {{ archive.describe }} </p>
-                <p> 篇数: {{ archive.count }} </p>
-                <p> 点击量: {{ archive.clicks }} </p>
-                <a class="btn btn-primary" href="{{ url_for("docx.archive_page", archive=archive.id, page=1) }}"> 进入 </a>
-                {% if show_delete %}
-                    <div id="DeleteModal{{archive.id}}" class="modal fade" role="dialog" aria-hidden="true">
-                        <div class="modal-dialog">
-                            <div class="modal-content text-start">
-                                <div class="modal-header">
-                                    <h4 class="modal-title"> 确认删除归档? </h4>
-                                </div>
-                                <div class="modal-body">
-                                    <p> 是否确认删除归档 {{ archive.name }}? </p>
-                                </div>
-                                <div class="modal-footer">
-                                    <a class="btn btn-danger"
-                                       href="{{ url_for("archive.delete_archive_page", archive=archive.id) }}"> 删除 </a>
-                                    <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal"> 取消 </button>
+    {% if current_user.check_role("ReadBlog") %}
+        <section id="base" class="d-flex mt-3 container justify-content-center flex-wrap">
+            {% for archive in archive_list %}
+                <div class="archive mb-3 mx-2">
+                    <h3> {{ archive.name }} </h3>
+                    <hr>
+                    <p class="archive_describe"> {{ archive.describe }} </p>
+                    <p> 篇数: {{ archive.count }} </p>
+                    <p> 点击量: {{ archive.clicks }} </p>
+                    <a class="btn btn-primary" href="{{ url_for("docx.archive_page", archive=archive.id, page=1) }}"> 进入 </a>
+                    {% if show_delete %}
+                        <div id="DeleteModal{{archive.id}}" class="modal fade" role="dialog" aria-hidden="true">
+                            <div class="modal-dialog">
+                                <div class="modal-content text-start">
+                                    <div class="modal-header">
+                                        <h4 class="modal-title"> 确认删除归档? </h4>
+                                    </div>
+                                    <div class="modal-body">
+                                        <p> 是否确认删除归档 {{ archive.name }}? </p>
+                                    </div>
+                                    <div class="modal-footer">
+                                        <a class="btn btn-danger"
+                                           href="{{ url_for("archive.delete_archive_page", archive=archive.id) }}"> 删除 </a>
+                                        <button type="button" class="btn btn-outline-dark" data-bs-dismiss="modal"> 取消 </button>
+                                    </div>
                                 </div>
                             </div>
                         </div>
-                    </div>
 
-                    <button type="button" class="btn btn-danger"
-                            data-bs-toggle="modal" data-bs-target="#DeleteModal{{archive.id}}"> 删除归档 </button>
-                {% endif %}
-            </div>
-        {% endfor %}
-    </section>
+                        <button type="button" class="btn btn-danger"
+                                data-bs-toggle="modal" data-bs-target="#DeleteModal{{archive.id}}"> 删除归档 </button>
+                    {% endif %}
+                </div>
+            {% endfor %}
+        </section>
+    {% endif %}
 {% endblock %}