to_protobuf
This commit is contained in:
parent
07cb2ac2cc
commit
bf454e8f27
@ -9,9 +9,10 @@ from bcrypt import hashpw, gensalt, checkpw
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from utils import pgclass, setting_loader, logger
|
from utils import pgclass, setting_loader, logger
|
||||||
from utils.misc import error
|
from utils.misc import error, internal_json2protobuf, error_proto
|
||||||
from utils.dbhelper import db, solo_article_fetcher, multi_article_fetcher, solo_file_fetcher, solo_article_remover
|
from utils.dbhelper import db, solo_article_fetcher, multi_article_fetcher, solo_file_fetcher, solo_article_remover
|
||||||
from utils.platform_consts import PLIST, PLIST_ROOT
|
from utils.platform_consts import PLIST, PLIST_ROOT
|
||||||
|
from protobuf_files import niming_pb2
|
||||||
|
|
||||||
admin = Blueprint("admin", __name__)
|
admin = Blueprint("admin", __name__)
|
||||||
|
|
||||||
@ -180,14 +181,16 @@ def article_fileget(id:int):
|
|||||||
@admin.route('/article/list', methods = ["GET"])
|
@admin.route('/article/list', methods = ["GET"])
|
||||||
@role_required(["article.read"])
|
@role_required(["article.read"])
|
||||||
def article_list():
|
def article_list():
|
||||||
res, code = multi_article_fetcher("admin", request.args.get("page"), 30)
|
res, code = multi_article_fetcher("admin", request.args.get("page"), 80)
|
||||||
return jsonify(res), code
|
return res, code
|
||||||
|
|
||||||
@admin.route("/article/<int:id>", methods=["GET"])
|
@admin.route("/article/<int:id>", methods=["GET"])
|
||||||
@role_required(["article.read"])
|
@role_required(["article.read"])
|
||||||
def article_read(id:int):
|
def article_read(id:int):
|
||||||
res, code = solo_article_fetcher("admin", id)
|
res, code = solo_article_fetcher("admin", id)
|
||||||
return jsonify(res), code
|
if code == 200:
|
||||||
|
return internal_json2protobuf(res), code
|
||||||
|
return res, code
|
||||||
|
|
||||||
@admin.route("/article/<int:id>", methods=["DELETE"])
|
@admin.route("/article/<int:id>", methods=["DELETE"])
|
||||||
@role_required(["article.del"])
|
@role_required(["article.del"])
|
||||||
@ -195,10 +198,15 @@ def article_del(id:int):
|
|||||||
opuser = g.opuser
|
opuser = g.opuser
|
||||||
|
|
||||||
result, code = solo_article_remover("admin", id=id)
|
result, code = solo_article_remover("admin", id=id)
|
||||||
if "error" in result: return jsonify(result), code
|
if not code == 200:
|
||||||
|
return result, code
|
||||||
|
|
||||||
logger.logger("article.delete", "User:%s deleted post (id=%d with comments %s): last_status=%s"%(opuser.user, result["id"], result["rcl"], result["mark"]))
|
logger.logger("article.delete", "User:%s deleted post (id=%d): last_status=%s"%(opuser.user, result["id"], result["mark"]))
|
||||||
return jsonify({"result":"OK"}), 200
|
|
||||||
|
return niming_pb2.FetchResponse(
|
||||||
|
status = niming_pb2.Status.Success,
|
||||||
|
posts = [ niming_pb2.FetchResponse.Message(id = result["id"], mark = result["mark"]) ]
|
||||||
|
).SerializeToString(), 200
|
||||||
|
|
||||||
@admin.route("/article/<int:id>", methods=["PUT"])
|
@admin.route("/article/<int:id>", methods=["PUT"])
|
||||||
@role_required(["article.pend"])
|
@role_required(["article.pend"])
|
||||||
@ -208,20 +216,20 @@ def article_pend(id:int):
|
|||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# 確保文章存在
|
# 確保文章存在
|
||||||
res = session.query(table).filter(table.id==int(id)).first()
|
res = session.query(table).filter(table.id==int(id)).first()
|
||||||
if res is None: return error("Post not found"), 404
|
if res is None: return error_proto("fetch", "Post not found"), 404
|
||||||
|
|
||||||
# 如果文章已經公開
|
# 如果文章已經公開
|
||||||
if res.mark == "visible":
|
if res.mark == "visible":
|
||||||
return error("Post is already visible."), 400
|
return error_proto("fetch", "Post is already visible."), 400
|
||||||
elif res.mark == "pending":
|
elif res.mark == "pending":
|
||||||
res.mark = "visible"
|
res.mark = "visible"
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
# run IG Post
|
# run IG Post
|
||||||
|
|
||||||
return jsonify({"result":"OK"}), 200
|
return niming_pb2.FetchResponse(status=niming_pb2.Status.Success).SerializeToString(), 200
|
||||||
else:
|
else:
|
||||||
return error("Post mark error"), 500
|
return error_proto("fetch", "Post mark error"), 500
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# Setting Area #
|
# Setting Area #
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import secrets
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
@ -26,40 +27,54 @@ article = Blueprint('article', __name__)
|
|||||||
@article.route('/list', methods = ["GET"])
|
@article.route('/list', methods = ["GET"])
|
||||||
def listing():
|
def listing():
|
||||||
res, code = multi_article_fetcher("general", request.args.get("page"), 30)
|
res, code = multi_article_fetcher("general", request.args.get("page"), 30)
|
||||||
res = internal_json2protobuf(res)
|
|
||||||
return res, code
|
return res, code
|
||||||
|
|
||||||
|
|
||||||
# 獲取匿名文附檔
|
# 獲取匿名文附檔
|
||||||
@article.route("/file/<int:id>", methods=["GET"])
|
@article.route("/file/<int:id>", methods=["GET"])
|
||||||
def getfile(id:int):
|
def getfile(id:int):
|
||||||
resp, code = solo_file_fetcher("general", id)
|
resp, code = solo_file_fetcher("general", id)
|
||||||
return resp, code
|
return resp, code
|
||||||
|
|
||||||
|
|
||||||
# 只有發文者可以看到的獲取指定文章
|
# 只有發文者可以看到的獲取指定文章
|
||||||
# 只有發文者可以做到的刪除文章
|
# 只有發文者可以做到的刪除文章
|
||||||
@article.route("/own/<sha256>", methods = ["GET", "DELETE"])
|
@article.route("/own/<int:id>", methods = ["GET", "DELETE"])
|
||||||
def owner_getarticle(sha256:str):
|
def owner_getarticle(id:int):
|
||||||
table = pgclass.SQLarticle
|
# arguments
|
||||||
ftab = pgclass.SQLfile
|
sha256 = request.args.get("hash", None)
|
||||||
|
if not sha256:
|
||||||
|
return error("Arguments error"), 400
|
||||||
|
sha256 = str(sha256)
|
||||||
|
|
||||||
# 獲取指定文章
|
# 獲取指定文章
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
resfn, code = solo_article_fetcher("owner", key=sha256)
|
resfn, code = solo_article_fetcher("owner", key=(sha256, id))
|
||||||
return jsonify(resfn), code
|
if code == 200:
|
||||||
|
return internal_json2protobuf(resfn), code
|
||||||
|
return resfn, code
|
||||||
# 刪除指定文章跟他們的留言、檔案
|
# 刪除指定文章跟他們的留言、檔案
|
||||||
elif request.method == "DELETE":
|
elif request.method == "DELETE":
|
||||||
result, code = solo_article_remover("general", hash=sha256)
|
result, code = solo_article_remover("owner", hash=sha256, id=id)
|
||||||
if "error" in result: return jsonify(result), code
|
if not code == 200:
|
||||||
|
return result, code
|
||||||
|
|
||||||
|
logger.logger("delpost", "Delete post (id=%d): last_status=%s"
|
||||||
|
%(result["id"], str(result["mark"])))
|
||||||
|
return niming_pb2.FetchResponse(
|
||||||
|
status = niming_pb2.Status.Success,
|
||||||
|
posts = [ niming_pb2.FetchResponse.Message(id = result["id"], mark = result["mark"]) ]
|
||||||
|
).SerializeToString(), 200
|
||||||
|
|
||||||
logger.logger("delpost", "Delete post (id=%d with comments %s): last_status=%s"
|
|
||||||
%(result["id"], str(result["rcl"]), str(result["mark"])))
|
|
||||||
return jsonify({"result":"OK"}), code
|
|
||||||
|
|
||||||
# 獲取指定文章
|
# 獲取指定文章
|
||||||
@article.route("/<int:id>", methods = ["GET"])
|
@article.route("/<int:id>", methods = ["GET"])
|
||||||
def getarticle(id:int):
|
def getarticle(id:int):
|
||||||
resfn, code = solo_article_fetcher("general", key=id)
|
resfn, code = solo_article_fetcher("general", key=id)
|
||||||
return jsonify(resfn), code
|
if code == 200:
|
||||||
|
return internal_json2protobuf(resfn), code
|
||||||
|
return resfn, code
|
||||||
|
|
||||||
|
|
||||||
# 上傳文章 / 留言
|
# 上傳文章 / 留言
|
||||||
@article.route("/", methods = ["POST"])
|
@article.route("/", methods = ["POST"])
|
||||||
@ -70,92 +85,96 @@ def posting():
|
|||||||
opt = setting_loader.loadset()
|
opt = setting_loader.loadset()
|
||||||
chk_before_post = opt["Check_Before_Post"]
|
chk_before_post = opt["Check_Before_Post"]
|
||||||
maxword = opt["Niming_Max_Word"]
|
maxword = opt["Niming_Max_Word"]
|
||||||
|
|
||||||
# protobuf parse
|
# protobuf parse
|
||||||
recv = niming_pb2.Post()
|
recv = niming_pb2.Post()
|
||||||
try: recv.ParseFromString(request.data)
|
try: recv.ParseFromString(request.data)
|
||||||
except DecodeError: return error_proto("Protobuf decode error"), 400
|
except DecodeError: return error_proto("post", "Protobuf decode error"), 400
|
||||||
|
|
||||||
# content
|
# content and check
|
||||||
ctx = str(recv.content)
|
ctx = str(recv.content)
|
||||||
if len(ctx) == 0 or len(ctx) > maxword: # length check
|
if len(ctx) == 0 or len(ctx) > maxword: # length check
|
||||||
return error_proto("no content or too many words"), 400
|
return error_proto("post", "no content or too many words"), 400
|
||||||
|
|
||||||
# hash
|
# hash
|
||||||
seed = ctx + str(time.time())
|
seed = ctx + str(time.time()) + str(secrets.token_urlsafe(nbytes=16))
|
||||||
hash = hashlib.sha256(seed.encode()).hexdigest()
|
hash = hashlib.sha256(seed.encode()).hexdigest()
|
||||||
|
|
||||||
# SQL start
|
# reference and check
|
||||||
table = pgclass.SQLarticle
|
ref = int(recv.ref)
|
||||||
with db.getsession() as session:
|
if ref != 0:
|
||||||
# reference
|
# 檢查指向的文章是否也是留言
|
||||||
ref = int(recv.ref)
|
reftg, code = solo_article_fetcher(role="general", key=ref)
|
||||||
if not (ref == 0): # 如果ref不是0
|
if code != 200 or reftg["reference"]:
|
||||||
# 檢查是不是指向存在的文章
|
return error_proto("post", "Invalid Reference"), 400
|
||||||
chk = session.query(table).filter(table.id == ref, table.mark == "visible").first()
|
else:
|
||||||
if chk is None: return error_proto("Invalid Reference"), 400
|
ref = None
|
||||||
# 檢查指向的文章是否也是留言
|
|
||||||
if not(chk.reference is None): return error_proto("Invalid Reference"), 400
|
|
||||||
else:
|
|
||||||
ref = None
|
|
||||||
|
|
||||||
# file processing
|
# file processing and check
|
||||||
files = recv.files
|
files = recv.files
|
||||||
# check - size
|
# check - size
|
||||||
atts = opt["Attachment_Count"]
|
atts = opt["Attachment_Count"]
|
||||||
sizelimit = opt["Attachment_Size"]
|
sizelimit = opt["Attachment_Size"]
|
||||||
if len(files) > atts: return error_proto("Too many files"), 400
|
if len(files) > atts: return error_proto("post", "Too many files"), 400
|
||||||
for f in files:
|
for f in files:
|
||||||
if len(f) <= 0 or len(f) > sizelimit: return error_proto("File size error"), 400
|
if len(f) <= 0 or len(f) > sizelimit: return error_proto("post", "File size error"), 400
|
||||||
# check - mimetype
|
# check - mimetype
|
||||||
allowed_mime = opt["Allowed_MIME"]
|
allowed_mime = opt["Allowed_MIME"]
|
||||||
for f in files:
|
fmimes = []
|
||||||
mime = magic.Magic(mime=True)
|
for f in files:
|
||||||
type = mime.from_buffer(f)
|
mime = magic.Magic(mime=True)
|
||||||
if not(type in allowed_mime): return error_proto("File format error"), 400
|
type = mime.from_buffer(f)
|
||||||
# run processor
|
if not(type in allowed_mime): return error_proto("post", "File format error"), 400
|
||||||
ftab = pgclass.SQLfile
|
fmimes.append(type)
|
||||||
for f in files:
|
|
||||||
mime = magic.Magic(mime=True)
|
|
||||||
type = mime.from_buffer(f)
|
|
||||||
fsql = ftab(reference = hash, binary = f, type = type)
|
|
||||||
session.add(fsql)
|
|
||||||
|
|
||||||
# IP
|
# IP
|
||||||
ip = request.remote_addr
|
ip = request.remote_addr
|
||||||
|
|
||||||
# ig posting
|
# ig posting
|
||||||
if chk_before_post:
|
if chk_before_post:
|
||||||
igid = None
|
|
||||||
# Go posting
|
|
||||||
igid = None
|
igid = None
|
||||||
# Coming Soon...
|
# Go posting
|
||||||
|
igid = None
|
||||||
|
# Coming Soon...
|
||||||
|
|
||||||
# mark
|
# mark
|
||||||
if chk_before_post: mark = "pending"
|
if chk_before_post: mark = "pending"
|
||||||
else: mark = "visible"
|
else: mark = "visible"
|
||||||
|
|
||||||
# posting
|
# posting
|
||||||
data = table(hash = hash, content = ctx, igid = igid, mark = mark, reference = ref, ip = ip)
|
table = pgclass.SQLarticle
|
||||||
session.add(data)
|
ftab = pgclass.SQLfile
|
||||||
session.commit()
|
try:
|
||||||
|
with db.getsession() as session:
|
||||||
|
# post
|
||||||
|
data = table(hash = hash, content = ctx, igid = igid, mark = mark, reference = ref, ip = ip)
|
||||||
|
session.add(data)
|
||||||
|
# file processor
|
||||||
|
fmidx = 0
|
||||||
|
fidarr = []
|
||||||
|
for f in files:
|
||||||
|
fsql = ftab(reference = hash, binary = f, type = fmimes[fmidx])
|
||||||
|
fidarr.append(fsql)
|
||||||
|
session.add(fsql)
|
||||||
|
fmidx += 1
|
||||||
|
# first commit
|
||||||
|
session.commit()
|
||||||
|
# set file list
|
||||||
|
data.file_list = [ fid.id for fid in fidarr ]
|
||||||
|
session.commit() # second commit
|
||||||
|
result_id = data.id
|
||||||
|
except:
|
||||||
|
return error_proto("post", "Create new post failed"), 400
|
||||||
|
|
||||||
result, code = solo_article_fetcher(role="owner", key=hash)
|
|
||||||
# logger
|
# logger
|
||||||
logger.logger("newpost", "New post (id=%d point to %s): %s"%(result["id"], ref, mark))
|
logger.logger("newpost", "New post (id=%d point to %s): %s"%(result_id, ref, mark))
|
||||||
|
|
||||||
# to protobuf
|
# to protobuf & return
|
||||||
proto = niming_pb2.PostResponse()
|
proto_stres = niming_pb2.PostResponse(
|
||||||
proto.status = niming_pb2.PostStatus.Success
|
status = niming_pb2.Status.Success,
|
||||||
proto.hash = hash
|
hash = hash,
|
||||||
proto.id = int(result["id"])
|
id = int(result_id)
|
||||||
proto_stres = proto.SerializeToString()
|
).SerializeToString()
|
||||||
|
return proto_stres, 200
|
||||||
rr = niming_pb2.PostResponse()
|
|
||||||
rr.ParseFromString(proto_stres)
|
|
||||||
print(rr.hash)
|
|
||||||
print(proto_stres)
|
|
||||||
return proto_stres, code
|
|
||||||
|
|
||||||
# 介面全部改成protobuf傳輸
|
# 介面全部改成protobuf傳輸
|
||||||
# 檔案傳輸加低畫質版本(縮圖)
|
# 檔案傳輸加低畫質版本(縮圖)
|
@ -8,14 +8,14 @@ message Post {
|
|||||||
repeated bytes files = 3;
|
repeated bytes files = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PostStatus {
|
enum Status {
|
||||||
Failed = 0;
|
Failed = 0;
|
||||||
Success = 1;
|
Success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The response of the posting, defining what should return.
|
// The response of the posting, defining what should return.
|
||||||
message PostResponse {
|
message PostResponse {
|
||||||
PostStatus status = 1;
|
Status status = 1;
|
||||||
string hash = 2;
|
string hash = 2;
|
||||||
uint64 id = 3;
|
uint64 id = 3;
|
||||||
optional string failed_message = 4;
|
optional string failed_message = 4;
|
||||||
@ -30,7 +30,14 @@ message FetchResponse {
|
|||||||
// request files through /article/file/<id> with MIME type.
|
// request files through /article/file/<id> with MIME type.
|
||||||
// See it as a BLOB url;
|
// See it as a BLOB url;
|
||||||
repeated uint64 files_id = 4;
|
repeated uint64 files_id = 4;
|
||||||
|
optional string hash = 5;
|
||||||
|
string igid = 6;
|
||||||
|
string mark = 7;
|
||||||
|
optional string ip = 8;
|
||||||
|
repeated uint64 comments_id = 9;
|
||||||
}
|
}
|
||||||
// Several post info
|
// Several post info
|
||||||
repeated Message posts = 1;
|
Status status = 1;
|
||||||
|
repeated Message posts = 2;
|
||||||
|
optional string failed_message = 3;
|
||||||
}
|
}
|
@ -1,7 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
message DataMessage {
|
|
||||||
string content = 1;
|
|
||||||
optional int64 ref = 2;
|
|
||||||
repeated optional bytes files = 3;
|
|
||||||
}
|
|
@ -13,21 +13,21 @@ _sym_db = _symbol_database.Default()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cniming.proto\"@\n\x04Post\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x10\n\x03ref\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\r\n\x05\x66iles\x18\x03 \x03(\x0c\x42\x06\n\x04_ref\"u\n\x0cPostResponse\x12\x1b\n\x06status\x18\x01 \x01(\x0e\x32\x0b.PostStatus\x12\x0c\n\x04hash\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\x04\x12\x1b\n\x0e\x66\x61iled_message\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x11\n\x0f_failed_message\"\x8a\x01\n\rFetchResponse\x12%\n\x05posts\x18\x01 \x03(\x0b\x32\x16.FetchResponse.Message\x1aR\n\x07Message\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x10\n\x03ref\x18\x03 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x08\x66iles_id\x18\x04 \x03(\x04\x42\x06\n\x04_ref*%\n\nPostStatus\x12\n\n\x06\x46\x61iled\x10\x00\x12\x0b\n\x07Success\x10\x01\x62\x06proto3')
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cniming.proto\"@\n\x04Post\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\x12\x10\n\x03ref\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\r\n\x05\x66iles\x18\x03 \x03(\x0c\x42\x06\n\x04_ref\"q\n\x0cPostResponse\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12\x0c\n\x04hash\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\x04\x12\x1b\n\x0e\x66\x61iled_message\x18\x04 \x01(\tH\x00\x88\x01\x01\x42\x11\n\x0f_failed_message\"\xb9\x02\n\rFetchResponse\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12%\n\x05posts\x18\x02 \x03(\x0b\x32\x16.FetchResponse.Message\x12\x1b\n\x0e\x66\x61iled_message\x18\x03 \x01(\tH\x00\x88\x01\x01\x1a\xb7\x01\n\x07Message\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x10\n\x03ref\x18\x03 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x08\x66iles_id\x18\x04 \x03(\x04\x12\x11\n\x04hash\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x0c\n\x04igid\x18\x06 \x01(\t\x12\x0c\n\x04mark\x18\x07 \x01(\t\x12\x0f\n\x02ip\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x0b\x63omments_id\x18\t \x03(\x04\x42\x06\n\x04_refB\x07\n\x05_hashB\x05\n\x03_ipB\x11\n\x0f_failed_message*!\n\x06Status\x12\n\n\x06\x46\x61iled\x10\x00\x12\x0b\n\x07Success\x10\x01\x62\x06proto3')
|
||||||
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'niming_pb2', globals())
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'niming_pb2', globals())
|
||||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
DESCRIPTOR._options = None
|
DESCRIPTOR._options = None
|
||||||
_POSTSTATUS._serialized_start=342
|
_STATUS._serialized_start=513
|
||||||
_POSTSTATUS._serialized_end=379
|
_STATUS._serialized_end=546
|
||||||
_POST._serialized_start=16
|
_POST._serialized_start=16
|
||||||
_POST._serialized_end=80
|
_POST._serialized_end=80
|
||||||
_POSTRESPONSE._serialized_start=82
|
_POSTRESPONSE._serialized_start=82
|
||||||
_POSTRESPONSE._serialized_end=199
|
_POSTRESPONSE._serialized_end=195
|
||||||
_FETCHRESPONSE._serialized_start=202
|
_FETCHRESPONSE._serialized_start=198
|
||||||
_FETCHRESPONSE._serialized_end=340
|
_FETCHRESPONSE._serialized_end=511
|
||||||
_FETCHRESPONSE_MESSAGE._serialized_start=258
|
_FETCHRESPONSE_MESSAGE._serialized_start=309
|
||||||
_FETCHRESPONSE_MESSAGE._serialized_end=340
|
_FETCHRESPONSE_MESSAGE._serialized_end=492
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
||||||
# NO CHECKED-IN PROTOBUF GENCODE
|
|
||||||
# source: niming.proto
|
|
||||||
# Protobuf Python Version: 5.28.3
|
|
||||||
"""Generated protocol buffer code."""
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
|
||||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
||||||
from google.protobuf import runtime_version as _runtime_version
|
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
|
||||||
from google.protobuf.internal import builder as _builder
|
|
||||||
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
||||||
_runtime_version.Domain.PUBLIC,
|
|
||||||
5,
|
|
||||||
28,
|
|
||||||
3,
|
|
||||||
'',
|
|
||||||
'niming.proto'
|
|
||||||
)
|
|
||||||
# @@protoc_insertion_point(imports)
|
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cniming.proto\"6\n\x0b\x44\x61taMessage\x12\x0b\n\x03\x63tx\x18\x01 \x01(\t\x12\x0b\n\x03ref\x18\x02 \x01(\x03\x12\r\n\x05\x66iles\x18\x03 \x03(\x0c\x62\x06proto3')
|
|
||||||
|
|
||||||
_globals = globals()
|
|
||||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'niming_pb2', _globals)
|
|
||||||
if not _descriptor._USE_C_DESCRIPTORS:
|
|
||||||
DESCRIPTOR._loaded_options = None
|
|
||||||
_globals['_DATAMESSAGE']._serialized_start=16
|
|
||||||
_globals['_DATAMESSAGE']._serialized_end=70
|
|
||||||
# @@protoc_insertion_point(module_scope)
|
|
@ -1,11 +1,11 @@
|
|||||||
from typing import Tuple, Dict, List
|
from typing import Tuple, Dict, List
|
||||||
|
|
||||||
from flask import make_response, Response, jsonify
|
from flask import make_response, Response, jsonify
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker, aliased
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc, func, literal, and_
|
||||||
|
|
||||||
from utils import pgclass
|
from utils import pgclass
|
||||||
from utils.misc import error
|
from utils.misc import error, error_proto
|
||||||
from protobuf_files import niming_pb2
|
from protobuf_files import niming_pb2
|
||||||
|
|
||||||
class db:
|
class db:
|
||||||
@ -20,104 +20,118 @@ class db:
|
|||||||
Session = sessionmaker(bind=cls._engine)
|
Session = sessionmaker(bind=cls._engine)
|
||||||
return Session()
|
return Session()
|
||||||
|
|
||||||
|
|
||||||
# role (general) (owner) (admin)
|
# role (general) (owner) (admin)
|
||||||
# 獲取單一文章
|
# 獲取單一文章
|
||||||
def solo_article_fetcher(role:str, key) -> Tuple[Dict,int]: # admin, owner, general
|
def solo_article_fetcher(role:str, key) -> Tuple[Dict | bytes, int]: # admin, owner, general
|
||||||
table = pgclass.SQLarticle
|
table = pgclass.SQLarticle # main
|
||||||
ftab = pgclass.SQLfile
|
table2 = aliased(table) # comment
|
||||||
resfn = {}
|
|
||||||
|
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# query
|
# query
|
||||||
|
res = session.query(table.id,
|
||||||
|
table.content,
|
||||||
|
table.reference,
|
||||||
|
table.file_list,
|
||||||
|
table.hash,
|
||||||
|
table.igid,
|
||||||
|
table.mark,
|
||||||
|
table.ip,
|
||||||
|
func.coalesce(func.array_agg(table2.id), literal([])).label("comments"))
|
||||||
|
|
||||||
if role == "owner":
|
if role == "owner":
|
||||||
res = session.query(table).filter(table.hash == key).first()
|
res = res.join(table2, table2.reference == table.id, isouter=True) \
|
||||||
|
.filter(table.hash == key[0], table.id == key[1])
|
||||||
elif role == "admin":
|
elif role == "admin":
|
||||||
res = session.query(table).filter(table.id == key).first()
|
res = res.join(table2, table2.reference == table.id, isouter=True) \
|
||||||
|
.filter(table.id == key)
|
||||||
elif role == "general":
|
elif role == "general":
|
||||||
res = session.query(table).filter(table.id == key, table.mark == "visible").first()
|
res = res.join(table2, and_(table2.reference == table.id, table2.mark == "visible"), isouter=True) \
|
||||||
if res is None: return {"error":"Post not found"}, 404
|
.filter(table.id == key, table.mark == "visible")
|
||||||
|
|
||||||
|
res = res.group_by(table.id, table.content, table.reference, table.file_list,
|
||||||
|
table.hash, table.igid, table.mark, table.ip).first()
|
||||||
|
if res is None:
|
||||||
|
return error_proto("fetch", "Post not found"), 404
|
||||||
|
|
||||||
# mapping
|
# mapping
|
||||||
resfn.update({"id": res.id, "content": res.content, "igid": res.igid, "mark": res.mark, "reference": res.reference})
|
one = {
|
||||||
if role == "admin": resfn["ip"] = res.ip
|
"id": res[0],
|
||||||
elif role == "owner": resfn["hash"] = res.hash
|
"content":res[1],
|
||||||
|
"igid":res[5],
|
||||||
|
"mark":res[6],
|
||||||
|
"reference":res[2],
|
||||||
|
"files_id":res[3],
|
||||||
|
"comments":res[8]
|
||||||
|
}
|
||||||
|
|
||||||
# comment
|
if role == "admin":
|
||||||
|
one["ip"] = res[7]
|
||||||
if role == "owner" or role == "admin":
|
if role == "owner" or role == "admin":
|
||||||
resfn["comment"] = [ c[0] for c in session.query(table.id).filter(table.reference == int(res.id)).all() ]
|
one["hash"] = res[4]
|
||||||
elif role == "general":
|
|
||||||
resfn["comment"] = [ c[0] for c in session.query(table.id).filter(table.reference == int(res.id), table.mark == "visible").all() ]
|
|
||||||
|
|
||||||
# file
|
return one, 200
|
||||||
resfn["files"] = [ f[0] for f in session.query(ftab.id).filter(ftab.reference == res.hash).all() ]
|
|
||||||
|
|
||||||
return resfn, 200
|
|
||||||
|
|
||||||
# 獲取文章列表
|
# 獲取文章列表
|
||||||
def multi_article_fetcher(role:str, page:str, count:int) -> Tuple[List, int]: # general, admin
|
def multi_article_fetcher(role:str, page:str, count:int) -> Tuple[bytes, int]: # general, admin
|
||||||
# checker
|
# checker
|
||||||
if page is None or not page.isdigit():
|
if page is None or not page.isdigit():
|
||||||
return error("Arguments error"), 400
|
return error_proto("fetch", "Arguments error"), 400
|
||||||
page = int(page)*30
|
page = int(page)*count
|
||||||
|
|
||||||
table = pgclass.SQLarticle
|
table = pgclass.SQLarticle
|
||||||
ftab = pgclass.SQLfile
|
resfn = niming_pb2.FetchResponse(
|
||||||
resfn = []
|
status = niming_pb2.Status.Success
|
||||||
|
)
|
||||||
|
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# query
|
# query
|
||||||
|
res = session.query(table)
|
||||||
if role == "general":
|
if role == "general":
|
||||||
res = session.query(table).filter(table.mark == "visible", table.reference == None)
|
res = res.filter(table.mark == "visible", table.reference == None)
|
||||||
elif role == "admin":
|
elif role == "admin":
|
||||||
res = session.query(table).filter(table.reference == None)
|
res = res.filter(table.reference == None)
|
||||||
res = res.order_by(desc(table.id)).offset(page).limit(count).all()
|
res = res.order_by(desc(table.id)).offset(page).limit(count).all()
|
||||||
|
|
||||||
# mapping
|
# mapping
|
||||||
for r in res:
|
for r in res:
|
||||||
rup = {"id":r.id, "content":r.content, "igid":r.igid, "created_at":r.created_at, "mark":r.mark, "reference":r.reference}
|
one = niming_pb2.FetchResponse.Message(
|
||||||
if role == "admin": rup["ip"] = r.ip # 如果是管理員 多給ip
|
id = r.id,
|
||||||
rup["files"] = [ f[0] for f in session.query(ftab.id).filter(ftab.reference == r.hash).all() ] # 檔案
|
content = r.content,
|
||||||
resfn.append(rup)
|
files_id = r.file_list,
|
||||||
|
igid = r.igid,
|
||||||
|
mark = r.mark,
|
||||||
|
ref = r.reference
|
||||||
|
)
|
||||||
|
if role == "admin": # 如果是管理員 多給ip 跟 hash
|
||||||
|
one.hash = r.hash
|
||||||
|
one.ip = r.ip
|
||||||
|
resfn.posts.append(one)
|
||||||
|
|
||||||
|
return resfn.SerializeToString(), 200
|
||||||
|
|
||||||
return resfn, 200
|
|
||||||
|
|
||||||
# 刪除文章
|
# 刪除文章
|
||||||
def solo_article_remover(role:str, hash:str=None, id:int=None) -> Tuple[Dict, int]: # admin, general
|
def solo_article_remover(role:str, hash:str=None, id:int=None) -> Tuple[Dict | bytes, int]: # admin, owner
|
||||||
key = None
|
key = None
|
||||||
if role == "admin": key = id
|
if role == "admin": key = id
|
||||||
elif role == "general": key = hash
|
elif role == "owner": key = (hash, id)
|
||||||
|
|
||||||
table = pgclass.SQLarticle
|
table = pgclass.SQLarticle
|
||||||
ftab = pgclass.SQLfile
|
|
||||||
rcl = []
|
|
||||||
|
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# 獲取本體
|
# 獲取本體
|
||||||
if role == "admin":
|
if role == "admin":
|
||||||
res = session.query(table).filter(table.id == key).first()
|
res = session.query(table).filter(table.id == key).first()
|
||||||
elif role == "general":
|
elif role == "owner":
|
||||||
res = session.query(table).filter(table.hash == key).first()
|
res = session.query(table).filter(table.hash == key[0], table.id == key[1]).first()
|
||||||
if res is None: # 檢查本體是否存在
|
if res is None: # 檢查本體是否存在
|
||||||
return {"error":"Post not found"}, 404
|
return error_proto("fetch", "Post not found!"), 404
|
||||||
|
|
||||||
# 刪除本體檔案
|
|
||||||
session.query(ftab).filter(ftab.reference == res.hash).delete()
|
|
||||||
|
|
||||||
# 刪留言
|
|
||||||
resc = session.query(table).filter(table.reference == res.id).all() # 留言
|
|
||||||
for c in resc:
|
|
||||||
rcl.append(c.id)
|
|
||||||
# 刪留言的檔案
|
|
||||||
session.query(ftab).filter(ftab.reference == c.hash).delete()
|
|
||||||
# 刪留言
|
|
||||||
session.delete(c)
|
|
||||||
|
|
||||||
# 刪本體
|
# 刪本體
|
||||||
session.delete(res)
|
session.delete(res)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
return {"id":res.id, "mark":res.mark, "rcl":rcl}, 200
|
return {"id":res.id, "mark":res.mark}, 200
|
||||||
|
|
||||||
|
|
||||||
# 獲取檔案
|
# 獲取檔案
|
||||||
def solo_file_fetcher(role:str, id:int) -> Tuple[Response, int]: # general, admin
|
def solo_file_fetcher(role:str, id:int) -> Tuple[Response, int]: # general, admin
|
||||||
|
@ -6,11 +6,14 @@ def error(message:str) -> Response:
|
|||||||
return jsonify({"error":message})
|
return jsonify({"error":message})
|
||||||
|
|
||||||
|
|
||||||
def error_proto(message:str) -> Response:
|
def error_proto(type:str, message:str) -> Response:
|
||||||
proto = niming_pb2.PostResponse()
|
if type == "post":
|
||||||
proto.status = niming_pb2.PostStatus.Failed
|
proto = niming_pb2.PostResponse()
|
||||||
proto.hash = ""
|
proto.hash = ""
|
||||||
proto.id = 0
|
proto.id = 0
|
||||||
|
elif type == "fetch":
|
||||||
|
proto = niming_pb2.FetchResponse()
|
||||||
|
proto.status = niming_pb2.Status.Failed
|
||||||
proto.failed_message = message
|
proto.failed_message = message
|
||||||
return proto.SerializeToString()
|
return proto.SerializeToString()
|
||||||
|
|
||||||
@ -19,13 +22,22 @@ def internal_json2protobuf(original:list|dict) -> bytes:
|
|||||||
if isinstance(original, dict):
|
if isinstance(original, dict):
|
||||||
original = [original]
|
original = [original]
|
||||||
|
|
||||||
res = niming_pb2.FetchResponse()
|
res = niming_pb2.FetchResponse(status = niming_pb2.Status.Success)
|
||||||
for o in original:
|
for o in original:
|
||||||
ob = niming_pb2.FetchResponse.Message()
|
ob = niming_pb2.FetchResponse.Message(
|
||||||
ob.id = o["id"]
|
id = o["id"],
|
||||||
ob.content = o["content"]
|
content = o["content"],
|
||||||
|
igid = o["igid"],
|
||||||
|
mark = o["mark"],
|
||||||
|
files_id = o["files_id"]
|
||||||
|
)
|
||||||
|
if None not in o["comments"]:
|
||||||
|
ob.comments_id.extend(o["comments"])
|
||||||
if o["reference"]:
|
if o["reference"]:
|
||||||
ob.ref = o["reference"]
|
ob.ref = o["reference"]
|
||||||
ob.files_id.extend(o["files"])
|
if "ip" in o:
|
||||||
|
ob.ip = o["ip"]
|
||||||
|
if "hash" in o:
|
||||||
|
ob.hash = o["hash"]
|
||||||
res.posts.append(ob)
|
res.posts.append(ob)
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
@ -14,6 +14,7 @@ class SQLarticle(Base):
|
|||||||
mark = Column(String)
|
mark = Column(String)
|
||||||
ip = Column(String)
|
ip = Column(String)
|
||||||
reference = Column(BIGINT)
|
reference = Column(BIGINT)
|
||||||
|
file_list = Column(ARRAY(BIGINT))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<article(id={self.id}, hash={self.hash}, content={self.content}, igid={self.igid}, mark={self.mark}, created_at={self.created_at}, ip={self.ip}, reference={self.reference})>"
|
return f"<article(id={self.id}, hash={self.hash}, content={self.content}, igid={self.igid}, mark={self.mark}, created_at={self.created_at}, ip={self.ip}, reference={self.reference})>"
|
||||||
|
Loading…
Reference in New Issue
Block a user