niming_backend/blueprints/article.py

173 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import magic
from flask import Blueprint, request, abort
from google.protobuf.message import DecodeError
from utils import pgclass, setting_loader, dbhelper
from utils.misc import internal_json2protobuf, error_proto
from protobuf_files import niming_pb2
"""
TODO:
- 測試 rebuild 完成的功能
- IG post ( Po文、刪文、只PO本體文章 )
- 檔案傳輸加低畫質版本(縮圖)
- log 的方式之後要重新設計 > 正規化
- IP Record (deploy之前配合rev proxy)
- gunicorn
- 檔案完成但是再看看要不要讓發文者持sha256存取自己發的文的檔案
"""
article = Blueprint('article', __name__)
# 匿名文列表
@article.route('/list', methods = ["GET"])
def listing():
res, code = dbhelper.multi_article_fetcher("general", request.args.get("page"), 30)
return res, code
# 獲取匿名文附檔
@article.route("/file/<fnhash>", methods=["GET"])
def getfile(fnhash:str):
resp, code = dbhelper.solo_file_fetcher("general", fnhash)
return resp, code
# 只有發文者可以看到的獲取指定文章
# 只有發文者可以做到的刪除文章
@article.route("/own/<type>/<key>", methods = ["GET", "DELETE"])
def owner_getarticle(type:str, key:str):
# arguments
sha256 = request.args.get("hash", None)
if not sha256:
return abort(400)
sha256 = str(sha256)
if type == 'a':
if not (len(key) > 0 and key.isdigit()):
return abort(400)
key = int(key) # id
elif type == 'c':
if not (len(key) > 0):
return abort(400)
key = str(key) # sha1
else:
return abort(400)
# 獲取指定文章/留言
if request.method == "GET":
if type == 'a': # 文章
resfn, code = dbhelper.solo_article_fetcher("owner", key=(sha256, key))
elif type == 'c': # 留言
resfn, code = dbhelper.solo_comment_fetcher("owner", key=(sha256, key))
if code == 200:
return internal_json2protobuf(resfn), code
return resfn, code
# 刪除指定文章跟他們的留言、檔案
elif request.method == "DELETE":
if type == 'a':
result, code = dbhelper.solo_article_remover("owner", hash=sha256, id=key)
elif type == 'c':
result, code = dbhelper.solo_comment_remover("owner", hash=sha256, sha1=key)
if not code == 200:
return result, code
one = niming_pb2.FetchResponse.Message()
if "id" in result: one.id = result["id"]
return niming_pb2.FetchResponse(posts=[one]).SerializeToString(), 200
# 獲取指定文章
@article.route("/a/<int:id>", methods = ["GET"])
def getarticle(id:int):
resfn, code = dbhelper.solo_article_fetcher("general", key=id)
if code == 200:
return internal_json2protobuf(resfn), code
return resfn, code
# 獲取指定文章的留言
@article.route("/c/<sha1>", methods = ["GET"])
def getcomment(sha1:str):
resfn, code = dbhelper.solo_comment_fetcher("general", key=sha1)
if code == 200:
return internal_json2protobuf(resfn), code
return resfn, code
# 上傳文章 / 留言
@article.route("/", methods = ["POST"])
def posting():
"""
Work Flow:
ctx -> reference -> file -> post( hash -> IP -> IG -> mark ) | -> log
"""
# loadset
opt = setting_loader.loadset()
maxword = opt["Niming_Max_Word"]
# protobuf parse
recv = niming_pb2.Post()
try: recv.ParseFromString(request.data)
except DecodeError:
return error_proto("Failed to parse data."), 400
# content and check
content = str(recv.content)
if len(content) == 0 or len(content) > maxword: # length check
return error_proto("No content or too many words."), 400
# reference and check
ref = int(recv.ref)
if ref != 0:
# 檢查指向的文章是否存在 且 可訪問
with dbhelper.db.getsession() as session:
article = pgclass.SQLarticle
article_mark = pgclass.SQLmark
tpid = session.query(article.id).join(article_mark, article.hash==article_mark.hash) \
.filter(article.id==ref, article_mark.mark=="visible").first()
if not tpid:
return error_proto("Invalid Reference."), 400
else:
ref = None
result_id, sha1, hash = 0, "", ""
if ref is None: # only article (comment dont have files)
# file processing & check
files = recv.files
# check - size
atts = opt["Attachment_Count"]
sizelimit = opt["Attachment_Size"]
if len(files) > atts: return error_proto("Too many files"), 400
for f in files:
if len(f) <= 0 or len(f) > sizelimit: return error_proto("Empty file or file too big."), 400
# check - mimetype
allowed_mime = opt["Allowed_MIME"]
fmimes = []
for f in files:
mime = magic.Magic(mime=True)
type = mime.from_buffer(f)
if not(type in allowed_mime): return error_proto("File type not allowed."), 400
fmimes.append(type)
# posting
result_id, hash = dbhelper.solo_article_uploader(content=content,
file_list=files,
fmimes=fmimes)
if not result_id:
return error_proto("Failed to Post"), 400
else: # comments
sha1, hash = dbhelper.solo_comment_uploader(content=content,
ref=ref)
if not sha1:
return error_proto("Failed to Post"), 400
# to protobuf & return
proto_stres = niming_pb2.PostResponse(
status = niming_pb2.Status.Success,
hash = hash,
id = int(result_id)
).SerializeToString()
return proto_stres, 200