testing and fix some bugs
This commit is contained in:
parent
05b1b18af8
commit
4c6efb3884
23
app.py
23
app.py
@ -22,32 +22,35 @@ PLATFORM_ROOT_PASSWORD = os.getenv("PLATFORM_ROOT_PASSWORD", None)
|
|||||||
|
|
||||||
# env checker
|
# env checker
|
||||||
errmsg = []
|
errmsg = []
|
||||||
if JWT_KEY is None or len(JWT_KEY) == 0:
|
if not JWT_KEY:
|
||||||
errmsg.append("Invalid JWT_KEY")
|
errmsg.append("Invalid JWT_KEY")
|
||||||
if PLATFORM_ROOT_PASSWORD is None or len(PLATFORM_ROOT_PASSWORD) == 0:
|
if not PLATFORM_ROOT_PASSWORD:
|
||||||
errmsg.append("Invalid PLATFORM_ROOT_PASSWORD")
|
errmsg.append("Invalid PLATFORM_ROOT_PASSWORD")
|
||||||
if len(errmsg):
|
if errmsg:
|
||||||
print(f"Env check failed: {errmsg}")
|
print(f"[X] Env check failed: {errmsg}")
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
# Postgresql
|
|
||||||
dbhelper.db = dbhelper.DB(create_engine('postgresql+psycopg2://%s:%s@%s:%s/%s'%(PG_USER, PG_PASS, PG_HOST, PG_PORT, PG_NAME)))
|
|
||||||
Base.metadata.create_all(dbhelper.db._engine)
|
|
||||||
|
|
||||||
# settings checker
|
# settings checker
|
||||||
settings = setting_loader.loadset()
|
settings = setting_loader.loadset()
|
||||||
for s in settings:
|
for s in settings:
|
||||||
if not setting_loader.typechecker(s, settings.get(s)):
|
if not setting_loader.typechecker(s, settings.get(s)):
|
||||||
print("Settings.json data type check failed: %s"%s)
|
print("[X] Settings.json data type check failed: %s"%(s))
|
||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
# Postgresql
|
||||||
|
print("[*] Connecting to Database")
|
||||||
|
dbhelper.db = dbhelper.DB(create_engine('postgresql+psycopg2://%s:%s@%s:%s/%s'%(PG_USER, PG_PASS, PG_HOST, PG_PORT, PG_NAME)))
|
||||||
|
Base.metadata.create_all(dbhelper.db._engine)
|
||||||
|
|
||||||
# root checker
|
# root checker
|
||||||
pwhash = hashpw(PLATFORM_ROOT_PASSWORD.encode("utf-8"), gensalt()).decode("utf-8") # if needed, new password
|
pwhash = hashpw(PLATFORM_ROOT_PASSWORD.encode("utf-8"), gensalt()).decode("utf-8") # if needed, new password
|
||||||
with dbhelper.db.getsession() as session:
|
with dbhelper.db.getsession() as session:
|
||||||
root = session.query(SQLuser).filter(SQLuser.user=="root").first()
|
root = session.query(SQLuser).filter(SQLuser.user=="root").first()
|
||||||
if root is None: # 沒有root
|
if root is None:
|
||||||
|
# no root user
|
||||||
session.add(SQLuser(user="root",password=pwhash, permission=PLIST_ROOT))
|
session.add(SQLuser(user="root",password=pwhash, permission=PLIST_ROOT))
|
||||||
elif (not checkpw(PLATFORM_ROOT_PASSWORD.encode("utf-8"), root.password.encode("utf-8"))) or root.permission != PLIST_ROOT:
|
elif (not checkpw(PLATFORM_ROOT_PASSWORD.encode("utf-8"), root.password.encode("utf-8"))) or root.permission != PLIST_ROOT:
|
||||||
|
# password / permission error
|
||||||
session.delete(root)
|
session.delete(root)
|
||||||
session.add(SQLuser(user="root",password=pwhash, permission=PLIST_ROOT))
|
session.add(SQLuser(user="root",password=pwhash, permission=PLIST_ROOT))
|
||||||
session.commit()
|
session.commit()
|
||||||
|
@ -8,7 +8,8 @@ from protobuf_files import niming_pb2
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
TODO:
|
TODO:
|
||||||
- 測試 rebuild 完成的功能
|
- 修復錯誤
|
||||||
|
- article fetch general 還是會顯示 pending 的留言
|
||||||
- IG post ( Po文、刪文、只PO本體文章 )
|
- IG post ( Po文、刪文、只PO本體文章 )
|
||||||
- 檔案傳輸加低畫質版本(縮圖)
|
- 檔案傳輸加低畫質版本(縮圖)
|
||||||
|
|
||||||
@ -58,25 +59,30 @@ def owner_getarticle(type:str, key:str):
|
|||||||
# 獲取指定文章/留言
|
# 獲取指定文章/留言
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
if type == 'a': # 文章
|
if type == 'a': # 文章
|
||||||
resfn, code = dbhelper.solo_article_fetcher("owner", key=(sha256, key))
|
resfn, code = dbhelper.solo_article_fetcher("owner", key=key, hash=sha256)
|
||||||
elif type == 'c': # 留言
|
elif type == 'c': # 留言
|
||||||
resfn, code = dbhelper.solo_comment_fetcher("owner", key=(sha256, key))
|
resfn, code = dbhelper.solo_comment_fetcher("owner", key=key, hash=sha256)
|
||||||
if code == 200:
|
if code == 200:
|
||||||
return internal_json2protobuf(resfn), code
|
return internal_json2protobuf({"type":type, "data":[resfn]}), code
|
||||||
return resfn, code
|
return resfn, code
|
||||||
# 刪除指定文章跟他們的留言、檔案
|
# 刪除指定文章/留言
|
||||||
elif request.method == "DELETE":
|
elif request.method == "DELETE":
|
||||||
if type == 'a':
|
if type == 'a':
|
||||||
|
rtype = niming_pb2.FetchPostResponse
|
||||||
result, code = dbhelper.solo_article_remover("owner", hash=sha256, id=key)
|
result, code = dbhelper.solo_article_remover("owner", hash=sha256, id=key)
|
||||||
elif type == 'c':
|
elif type == 'c':
|
||||||
|
rtype = niming_pb2.FetchCommentResponse
|
||||||
result, code = dbhelper.solo_comment_remover("owner", hash=sha256, sha1=key)
|
result, code = dbhelper.solo_comment_remover("owner", hash=sha256, sha1=key)
|
||||||
|
|
||||||
if not code == 200:
|
if not code == 200:
|
||||||
return result, code
|
return result, code
|
||||||
|
|
||||||
one = niming_pb2.FetchResponse.Message()
|
if type == 'a':
|
||||||
if "id" in result: one.id = result["id"]
|
ret = rtype(posts=[ rtype.Message(id=result["id"]) ])
|
||||||
|
elif type == 'c':
|
||||||
|
ret = rtype(posts=[ rtype.Message(sha1=result["sha1"]) ])
|
||||||
|
|
||||||
return niming_pb2.FetchResponse(posts=[one]).SerializeToString(), 200
|
return ret.SerializeToString(), 200
|
||||||
|
|
||||||
|
|
||||||
# 獲取指定文章
|
# 獲取指定文章
|
||||||
@ -84,7 +90,7 @@ def owner_getarticle(type:str, key:str):
|
|||||||
def getarticle(id:int):
|
def getarticle(id:int):
|
||||||
resfn, code = dbhelper.solo_article_fetcher("general", key=id)
|
resfn, code = dbhelper.solo_article_fetcher("general", key=id)
|
||||||
if code == 200:
|
if code == 200:
|
||||||
return internal_json2protobuf(resfn), code
|
return internal_json2protobuf({"type":'a', "data":[resfn]}), code
|
||||||
return resfn, code
|
return resfn, code
|
||||||
|
|
||||||
|
|
||||||
@ -93,7 +99,7 @@ def getarticle(id:int):
|
|||||||
def getcomment(sha1:str):
|
def getcomment(sha1:str):
|
||||||
resfn, code = dbhelper.solo_comment_fetcher("general", key=sha1)
|
resfn, code = dbhelper.solo_comment_fetcher("general", key=sha1)
|
||||||
if code == 200:
|
if code == 200:
|
||||||
return internal_json2protobuf(resfn), code
|
return internal_json2protobuf({"type":'c', "data":[resfn]}), code
|
||||||
return resfn, code
|
return resfn, code
|
||||||
|
|
||||||
|
|
||||||
@ -111,12 +117,12 @@ def posting():
|
|||||||
recv = niming_pb2.Post()
|
recv = niming_pb2.Post()
|
||||||
try: recv.ParseFromString(request.data)
|
try: recv.ParseFromString(request.data)
|
||||||
except DecodeError:
|
except DecodeError:
|
||||||
return error_proto("Failed to parse data."), 400
|
return error_proto("Failed to parse data"), 400
|
||||||
|
|
||||||
# content and check
|
# content and check
|
||||||
content = str(recv.content)
|
content = str(recv.content)
|
||||||
if len(content) == 0 or len(content) > maxword: # length check
|
if len(content) == 0 or len(content) > maxword: # length check
|
||||||
return error_proto("No content or too many words."), 400
|
return error_proto("No content or too many words"), 400
|
||||||
|
|
||||||
# reference and check
|
# reference and check
|
||||||
ref = int(recv.ref)
|
ref = int(recv.ref)
|
||||||
@ -128,7 +134,7 @@ def posting():
|
|||||||
tpid = session.query(article.id).join(article_mark, article.hash==article_mark.hash) \
|
tpid = session.query(article.id).join(article_mark, article.hash==article_mark.hash) \
|
||||||
.filter(article.id==ref, article_mark.mark=="visible").first()
|
.filter(article.id==ref, article_mark.mark=="visible").first()
|
||||||
if not tpid:
|
if not tpid:
|
||||||
return error_proto("Invalid Reference."), 400
|
return error_proto("Invalid Reference"), 400
|
||||||
else:
|
else:
|
||||||
ref = None
|
ref = None
|
||||||
|
|
||||||
@ -141,14 +147,14 @@ def posting():
|
|||||||
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("Too many files"), 400
|
||||||
for f in files:
|
for f in files:
|
||||||
if len(f) <= 0 or len(f) > sizelimit: return error_proto("Empty file or file too big."), 400
|
if len(f) <= 0 or len(f) > sizelimit: return error_proto("Empty file or file too big"), 400
|
||||||
# check - mimetype
|
# check - mimetype
|
||||||
allowed_mime = opt["Allowed_MIME"]
|
allowed_mime = opt["Allowed_MIME"]
|
||||||
fmimes = []
|
fmimes = []
|
||||||
for f in files:
|
for f in files:
|
||||||
mime = magic.Magic(mime=True)
|
mime = magic.Magic(mime=True)
|
||||||
type = mime.from_buffer(f)
|
type = mime.from_buffer(f)
|
||||||
if not(type in allowed_mime): return error_proto("File type not allowed."), 400
|
if not(type in allowed_mime): return error_proto("File type not allowed"), 400
|
||||||
fmimes.append(type)
|
fmimes.append(type)
|
||||||
|
|
||||||
# posting
|
# posting
|
||||||
|
@ -3,7 +3,6 @@ syntax = "proto3";
|
|||||||
// This is for posting a paragraph.
|
// This is for posting a paragraph.
|
||||||
message Post {
|
message Post {
|
||||||
string content = 1;
|
string content = 1;
|
||||||
// reply to a post, like a mail chat.
|
|
||||||
optional int64 ref = 2;
|
optional int64 ref = 2;
|
||||||
repeated bytes files = 3;
|
repeated bytes files = 3;
|
||||||
}
|
}
|
||||||
@ -21,7 +20,7 @@ message PostResponse {
|
|||||||
optional string failed_message = 4;
|
optional string failed_message = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FetchResponse {
|
message FetchPostResponse {
|
||||||
message Message {
|
message Message {
|
||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
string content = 2;
|
string content = 2;
|
||||||
@ -36,3 +35,12 @@ message FetchResponse {
|
|||||||
// Several post info
|
// Several post info
|
||||||
repeated Message posts = 1;
|
repeated Message posts = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message FetchCommentResponse {
|
||||||
|
message Message {
|
||||||
|
string sha1 = 1;
|
||||||
|
string content = 2;
|
||||||
|
}
|
||||||
|
// Several post info
|
||||||
|
repeated Message posts = 1;
|
||||||
|
}
|
@ -13,21 +13,25 @@ _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\"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\"\xa5\x01\n\rFetchResponse\x12%\n\x05posts\x18\x01 \x03(\x0b\x32\x16.FetchResponse.Message\x1am\n\x07Message\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x12\n\nfiles_hash\x18\x03 \x03(\t\x12\x11\n\x04igid\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x15\n\rcomments_hash\x18\x05 \x03(\tB\x07\n\x05_igid*!\n\x06Status\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\"\xad\x01\n\x11\x46\x65tchPostResponse\x12)\n\x05posts\x18\x01 \x03(\x0b\x32\x1a.FetchPostResponse.Message\x1am\n\x07Message\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x12\n\nfiles_hash\x18\x03 \x03(\t\x12\x11\n\x04igid\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x15\n\rcomments_hash\x18\x05 \x03(\tB\x07\n\x05_igid\"n\n\x14\x46\x65tchCommentResponse\x12,\n\x05posts\x18\x01 \x03(\x0b\x32\x1d.FetchCommentResponse.Message\x1a(\n\x07Message\x12\x0c\n\x04sha1\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t*!\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
|
||||||
_STATUS._serialized_start=365
|
_STATUS._serialized_start=485
|
||||||
_STATUS._serialized_end=398
|
_STATUS._serialized_end=518
|
||||||
_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=195
|
_POSTRESPONSE._serialized_end=195
|
||||||
_FETCHRESPONSE._serialized_start=198
|
_FETCHPOSTRESPONSE._serialized_start=198
|
||||||
_FETCHRESPONSE._serialized_end=363
|
_FETCHPOSTRESPONSE._serialized_end=371
|
||||||
_FETCHRESPONSE_MESSAGE._serialized_start=254
|
_FETCHPOSTRESPONSE_MESSAGE._serialized_start=262
|
||||||
_FETCHRESPONSE_MESSAGE._serialized_end=363
|
_FETCHPOSTRESPONSE_MESSAGE._serialized_end=371
|
||||||
|
_FETCHCOMMENTRESPONSE._serialized_start=373
|
||||||
|
_FETCHCOMMENTRESPONSE._serialized_end=483
|
||||||
|
_FETCHCOMMENTRESPONSE_MESSAGE._serialized_start=443
|
||||||
|
_FETCHCOMMENTRESPONSE_MESSAGE._serialized_end=483
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
@ -1 +1 @@
|
|||||||
{"Check_Before_Post": false, "JWT_Valid_Time": 604800, "Niming_Max_Word": 500, "Attachment_Count": 5, "Attachment_Size": 209715200, "Allowed_MIME": ["image/jpeg", "image/pjpeg", "image/png", "image/heic", "image/heif", "video/mp4", "video/quicktime", "video/hevc", "image/webp"]}
|
{"Check_Before_Post": true, "JWT_Valid_Time": 604800, "Niming_Max_Word": 500, "Attachment_Count": 5, "Attachment_Size": 209715200, "Allowed_MIME": ["image/jpeg", "image/pjpeg", "image/png", "image/heic", "image/heif", "video/mp4", "video/quicktime", "video/hevc", "image/webp"]}
|
@ -7,7 +7,7 @@ import os
|
|||||||
|
|
||||||
from flask import make_response, Response, abort, request
|
from flask import make_response, Response, abort, request
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from sqlalchemy import desc, func, update, Engine, text, delete
|
from sqlalchemy import desc, update, Engine, text, delete
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from utils import pgclass, setting_loader, s3helper, logger
|
from utils import pgclass, setting_loader, s3helper, logger
|
||||||
@ -27,6 +27,7 @@ class DB:
|
|||||||
return Session()
|
return Session()
|
||||||
|
|
||||||
db:DB = None
|
db:DB = None
|
||||||
|
TIMEZONE:str = os.getenv("TIMEZONE")
|
||||||
|
|
||||||
|
|
||||||
# 上傳單一文章
|
# 上傳單一文章
|
||||||
@ -65,21 +66,13 @@ def solo_article_uploader(content:str, file_list, fmimes:List[str]) -> Tuple[int
|
|||||||
if err:
|
if err:
|
||||||
return 0, ""
|
return 0, ""
|
||||||
|
|
||||||
# meta processor
|
# db processor (meta, article, mark)
|
||||||
metaa = article_metadata(ip=ip,
|
metaa = article_metadata(ip=ip, igid=igid, hash=hash)
|
||||||
igid=igid,
|
posta = article(content=content, hash=hash, file_list=fnlist)
|
||||||
hash=hash)
|
marka = article_mark(hash=hash, mark=mark)
|
||||||
|
|
||||||
session.add(metaa)
|
session.add(metaa)
|
||||||
|
|
||||||
# article processor
|
|
||||||
posta = article(content=content,
|
|
||||||
hash=hash,
|
|
||||||
file_list=fnlist)
|
|
||||||
session.add(posta)
|
session.add(posta)
|
||||||
|
|
||||||
# mark processor
|
|
||||||
marka = article_mark(hash=hash,
|
|
||||||
mark=mark)
|
|
||||||
session.add(marka)
|
session.add(marka)
|
||||||
|
|
||||||
# commit
|
# commit
|
||||||
@ -90,8 +83,7 @@ def solo_article_uploader(content:str, file_list, fmimes:List[str]) -> Tuple[int
|
|||||||
logger.logger("newpost", "New post (id=%d): %s"%(result_id, mark))
|
logger.logger("newpost", "New post (id=%d): %s"%(result_id, mark))
|
||||||
|
|
||||||
return result_id, hash
|
return result_id, hash
|
||||||
except Exception as e:
|
except:
|
||||||
print(e)
|
|
||||||
return 0, ""
|
return 0, ""
|
||||||
|
|
||||||
|
|
||||||
@ -123,7 +115,7 @@ def solo_comment_uploader(content:str, ref:int) -> Tuple[int | str, str]:
|
|||||||
"content":content,
|
"content":content,
|
||||||
"ip":ip,
|
"ip":ip,
|
||||||
"hash":hash,
|
"hash":hash,
|
||||||
"created_at":datetime.now(pytz.timezone(os.getenv("TIMEZONE"))),
|
"created_at":datetime.now(pytz.timezone(TIMEZONE)),
|
||||||
"sha1":sha1
|
"sha1":sha1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,85 +143,89 @@ def solo_comment_uploader(content:str, ref:int) -> Tuple[int | str, str]:
|
|||||||
|
|
||||||
# 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:int, hash:str=None) -> Tuple[Dict, int]: # admin, owner, general
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# query
|
# article fetch
|
||||||
stmt = "SELECT posts.id AS posts_id, \
|
stmt="SELECT posts.id, posts.content, posts.file_list, meta.igid, posts.hash, meta.ip " \
|
||||||
posts.content AS posts_content, \
|
+"FROM posts " \
|
||||||
posts.file_list AS posts_file_list, \
|
+"INNER JOIN mark AS pmark ON posts.hash=pmark.hash " \
|
||||||
article_meta.igid AS article_meta_igid, \
|
+"INNER JOIN article_meta AS meta ON posts.hash=meta.hash "
|
||||||
posts.comment_list AS posts_comment_list, \
|
if role == "owner": # 驗證id/hash,可以看到本體(無驗證)
|
||||||
posts.hash AS posts_hash, \
|
|
||||||
article_meta.ip AS article_meta_ip \
|
|
||||||
FROM posts \
|
|
||||||
JOIN mark ON mark.hash = posts.hash \
|
|
||||||
JOIN article_meta ON article_meta.hash = posts.hash "
|
|
||||||
|
|
||||||
if role == "owner":
|
|
||||||
stmt += "WHERE posts.id = :id AND posts.hash = :hash"
|
stmt += "WHERE posts.id = :id AND posts.hash = :hash"
|
||||||
result = session.execute(text(stmt), {"id":key[1], "hash":key[0]})
|
elif role == "admin": # 驗證id,可以看到本體(無驗證)
|
||||||
elif role == "admin":
|
|
||||||
stmt += "WHERE posts.id = :id"
|
stmt += "WHERE posts.id = :id"
|
||||||
result = session.execute(text(stmt), {"id":key})
|
elif role == "general": # 驗證id,可以看到本體(visible)
|
||||||
elif role == "general":
|
stmt += "WHERE posts.id=:id AND pmark.mark='visible'"
|
||||||
stmt += "WHERE posts.id = :id AND mark.mark = 'visible'"
|
result = session.execute(text(stmt), {"id":key, "hash":hash})
|
||||||
result = session.execute(text(stmt), {"id":key})
|
|
||||||
res = result.first()
|
res = result.first()
|
||||||
if res is None:
|
if res is None:
|
||||||
return abort(404)
|
return abort(404)
|
||||||
|
|
||||||
|
# comment fetch
|
||||||
|
stmt="SELECT c.sha1 " \
|
||||||
|
+"FROM posts " \
|
||||||
|
+"INNER JOIN unnest(posts.comment_list) AS c ON c=ANY(posts.comment_list) " \
|
||||||
|
+"INNER JOIN mark AS cmark ON c.hash=cmark.hash " \
|
||||||
|
+"WHERE posts.id=:id"
|
||||||
|
if role == "general": # 留言sha1(visible)
|
||||||
|
stmt+=" AND cmark.mark='visible'"
|
||||||
|
result = session.execute(text(stmt), {"id":res[0]})
|
||||||
|
cres = result.all()
|
||||||
|
|
||||||
# mapping
|
# mapping
|
||||||
one = {
|
one = {
|
||||||
"id": res[0],
|
"id": res[0],
|
||||||
"content": res[1],
|
"content": res[1],
|
||||||
"files_hash": res[2],
|
|
||||||
"igid": res[3],
|
"igid": res[3],
|
||||||
}
|
}
|
||||||
if res[4]:
|
if res[2]: # files
|
||||||
one["comments_hash"] = [ c.sha1 for c in res[4] ]
|
one["files_hash"] = res[2]
|
||||||
|
if res[4]: # comments
|
||||||
|
one["comments_hash"] = [ c[0] for c in cres ]
|
||||||
|
|
||||||
if role == "admin":
|
if role == "admin":
|
||||||
one["ip"] = res[6]
|
one["ip"] = res[5]
|
||||||
if role == "owner" or role == "admin":
|
if role == "owner" or role == "admin":
|
||||||
one["hash"] = res[5]
|
one["hash"] = res[4]
|
||||||
|
|
||||||
return one, 200
|
return one, 200
|
||||||
|
|
||||||
|
|
||||||
# role (general) (owner) (admin)
|
# role (general) (owner) (admin)
|
||||||
# 獲取單一留言
|
# 獲取單一留言
|
||||||
def solo_comment_fetcher(role:str, key) -> Tuple[Dict, int]: # admin, owner, general
|
def solo_comment_fetcher(role:str, key:str, hash:str=None) -> Tuple[Dict, int]: # admin, owner, general
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# query
|
# query
|
||||||
stmt = "SELECT posts.id AS parent, c.* \
|
stmt="SELECT posts.id AS parent_id, posts.hash AS parent_hash, pmark.mark AS parent_mark, cmark.mark AS comment_mark, c.* " \
|
||||||
FROM posts \
|
+"FROM posts " \
|
||||||
JOIN mark ON mark.hash = posts.hash \
|
+"INNER JOIN unnest(posts.comment_list) AS c ON c=ANY(posts.comment_list) " \
|
||||||
JOIN unnest(posts.comment_list) AS c ON 1=1 "
|
+"JOIN mark AS pmark ON posts.hash=pmark.hash " \
|
||||||
|
+"JOIN mark AS cmark ON c.hash=cmark.hash " \
|
||||||
|
+"WHERE c.sha1=:sha1 "
|
||||||
if role == "general":
|
if role == "general":
|
||||||
# 對一般用戶,sha1查詢,確保本體可見
|
# 對一般用戶,sha1查詢,確保本體跟留言可見
|
||||||
stmt += " WHERE c.sha1 = :key AND mark.mark = 'visible'"
|
stmt += "AND pmark.mark='visible' AND cmark.mark='visible'"
|
||||||
arta = session.execute(text(stmt), {'key':key}).first()
|
arta = session.execute(text(stmt), {'sha1':key}).first()
|
||||||
elif role == "owner":
|
elif role == "owner":
|
||||||
# 對發文者,sha256查詢
|
# 對發文者,sha1查詢,sha256查詢,不設檢查
|
||||||
stmt += " WHERE c.hash = :key AND c.sha1 = :sha1"
|
stmt += "AND c.hash=:hash"
|
||||||
arta = session.execute(text(stmt), {'key':key[0], 'sha1':key[1]}).first()
|
arta = session.execute(text(stmt), {'sha1':key, 'hash':hash}).first()
|
||||||
elif role == "admin":
|
elif role == "admin":
|
||||||
# 對管理員,sha1查詢
|
# 對管理員,sha1查詢,不設檢查
|
||||||
stmt += " WHERE c.sha1 = :key"
|
arta = session.execute(text(stmt), {'sha1':key}).first()
|
||||||
arta = session.execute(text(stmt), {'key':key}).first()
|
|
||||||
if arta is None:
|
if arta is None:
|
||||||
return abort(404)
|
return abort(404)
|
||||||
|
|
||||||
# mapping
|
# mapping
|
||||||
one = {
|
one = {
|
||||||
"content": arta[1],
|
"content": arta[4],
|
||||||
"sha1": arta[5]
|
"sha1": arta[8]
|
||||||
}
|
}
|
||||||
|
|
||||||
if role == "admin":
|
if role == "admin":
|
||||||
one["ip"] = arta[2]
|
one["ip"] = arta[5]
|
||||||
if role == "owner" or role == "admin":
|
if role == "owner" or role == "admin":
|
||||||
one["hash"] = arta[3]
|
one["hash"] = arta[6]
|
||||||
|
|
||||||
return one, 200
|
return one, 200
|
||||||
|
|
||||||
@ -244,7 +240,7 @@ def multi_article_fetcher(role:str, page:str, count:int) -> Tuple[bytes, int]: #
|
|||||||
article = pgclass.SQLarticle
|
article = pgclass.SQLarticle
|
||||||
article_meta = pgclass.SQLmeta
|
article_meta = pgclass.SQLmeta
|
||||||
article_mark = pgclass.SQLmark
|
article_mark = pgclass.SQLmark
|
||||||
resfn = niming_pb2.FetchResponse()
|
resfn = niming_pb2.FetchPostResponse()
|
||||||
|
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# query
|
# query
|
||||||
@ -257,12 +253,13 @@ def multi_article_fetcher(role:str, page:str, count:int) -> Tuple[bytes, int]: #
|
|||||||
|
|
||||||
# mapping
|
# mapping
|
||||||
for r in res:
|
for r in res:
|
||||||
one = niming_pb2.FetchResponse.Message(
|
one = niming_pb2.FetchPostResponse.Message(
|
||||||
id = r[0],
|
id = r[0],
|
||||||
content = r[1],
|
content = r[1],
|
||||||
files_hash = r[2],
|
|
||||||
igid = r[3],
|
igid = r[3],
|
||||||
)
|
)
|
||||||
|
if r[2]: # files
|
||||||
|
one.files_hash.extend(r[2])
|
||||||
if role == "admin": # 如果是管理員 多給ip 跟 hash # proto那邊沒支援
|
if role == "admin": # 如果是管理員 多給ip 跟 hash # proto那邊沒支援
|
||||||
one.hash = r[4]
|
one.hash = r[4]
|
||||||
one.ip = r[5]
|
one.ip = r[5]
|
||||||
@ -273,26 +270,23 @@ def multi_article_fetcher(role:str, page:str, count:int) -> Tuple[bytes, int]: #
|
|||||||
|
|
||||||
# 刪除單一文章
|
# 刪除單一文章
|
||||||
def solo_article_remover(role:str, hash:str=None, id:int=None) -> Tuple[Dict, int]: # admin, owner
|
def solo_article_remover(role:str, hash:str=None, id:int=None) -> Tuple[Dict, int]: # admin, owner
|
||||||
key = None
|
|
||||||
if role == "admin": key = id
|
|
||||||
elif role == "owner": key = (hash, id)
|
|
||||||
|
|
||||||
article = pgclass.SQLarticle
|
article = pgclass.SQLarticle
|
||||||
article_mark = pgclass.SQLmark
|
article_mark = pgclass.SQLmark
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# 獲取本體
|
# 獲取本體
|
||||||
pres = session.query(article.id, article.hash, article_mark.mark, article.file_list).join(article_mark, article_mark.hash==article.hash)
|
pres = session.query(article.id, article.hash, article_mark.mark, article.file_list) \
|
||||||
|
.join(article_mark, article_mark.hash==article.hash)
|
||||||
if role == "admin":
|
if role == "admin":
|
||||||
pres = pres.filter(article.id == key).first()
|
pres = pres.filter(article.id == id).first()
|
||||||
elif role == "owner":
|
elif role == "owner":
|
||||||
pres = pres.filter(article.id == key[1], article.hash == key[0]).first()
|
pres = pres.filter(article.id == id, article.hash == hash).first()
|
||||||
if pres is None: # 如果本體不存在
|
if pres is None: # 如果本體不存在
|
||||||
return abort(404)
|
return abort(404)
|
||||||
|
|
||||||
# 獲取本體的留言們(hash)
|
# 獲取本體的留言們(hash)
|
||||||
stmt="SELECT c.hash as chash " \
|
stmt="SELECT c.hash as chash " \
|
||||||
+ "FROM posts, unnest(posts.comment_list) AS c " \
|
+"FROM posts, unnest(posts.comment_list) AS c " \
|
||||||
+ "WHERE posts.id = :id"
|
+"WHERE posts.id = :id"
|
||||||
cres = session.execute(text(stmt), {'id':pres[0]}).all()
|
cres = session.execute(text(stmt), {'id':pres[0]}).all()
|
||||||
|
|
||||||
# 刪除本體
|
# 刪除本體
|
||||||
@ -322,25 +316,21 @@ def solo_article_remover(role:str, hash:str=None, id:int=None) -> Tuple[Dict, in
|
|||||||
|
|
||||||
# 刪除單一留言
|
# 刪除單一留言
|
||||||
def solo_comment_remover(role:str, hash:str=None, sha1:str=None) -> Tuple[Dict, int]:
|
def solo_comment_remover(role:str, hash:str=None, sha1:str=None) -> Tuple[Dict, int]:
|
||||||
key = None
|
|
||||||
if role == "admin": key = sha1
|
|
||||||
elif role == "owner": key = (hash, sha1)
|
|
||||||
|
|
||||||
article_mark = pgclass.SQLmark
|
article_mark = pgclass.SQLmark
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
# 獲取留言本體
|
# 獲取留言本體
|
||||||
stmt="SELECT posts.id AS parent, c.sha1, c.hash " \
|
stmt="SELECT posts.id AS parent, c.sha1, c.hash " \
|
||||||
+ "FROM posts, unnest(posts.comment_list) AS c "
|
+"FROM posts, unnest(posts.comment_list) AS c "
|
||||||
if role == "admin":
|
if role == "admin":
|
||||||
stmt += "WHERE c.sha1 = :sha1"
|
stmt += "WHERE c.sha1 = :sha1"
|
||||||
cres = session.execute(text(stmt), {'sha1':key}).first()
|
cres = session.execute(text(stmt), {'sha1':sha1}).first()
|
||||||
elif role == 'owner':
|
elif role == 'owner':
|
||||||
stmt += "WHERE c.sha1 = :sha1 AND c.hash = :hash"
|
stmt += "WHERE c.sha1 = :sha1 AND c.hash = :hash"
|
||||||
cres = session.execute(text(stmt), {'sha1':key[1], 'hash':key[0]}).first()
|
cres = session.execute(text(stmt), {'sha1':sha1, 'hash':hash}).first()
|
||||||
if cres is None: # 如果不存在
|
if cres is None: # 如果不存在
|
||||||
return abort(404)
|
return abort(404)
|
||||||
|
|
||||||
# 刪除本體
|
# 刪除留言本體
|
||||||
stmt="UPDATE posts " \
|
stmt="UPDATE posts " \
|
||||||
+"SET comment_list = ARRAY(" \
|
+"SET comment_list = ARRAY(" \
|
||||||
+"SELECT c " \
|
+"SELECT c " \
|
||||||
@ -349,7 +339,7 @@ def solo_comment_remover(role:str, hash:str=None, sha1:str=None) -> Tuple[Dict,
|
|||||||
+")"
|
+")"
|
||||||
session.execute(text(stmt), {'sha1':cres[1], 'hash':cres[2]})
|
session.execute(text(stmt), {'sha1':cres[1], 'hash':cres[2]})
|
||||||
|
|
||||||
# 刪除 mark (本體 & 留言)
|
# 刪除留言mark
|
||||||
mark = session.query(article_mark.mark).filter(article_mark.hash == cres[2])
|
mark = session.query(article_mark.mark).filter(article_mark.hash == cres[2])
|
||||||
stmt = delete(article_mark).where(article_mark.hash == cres[2])
|
stmt = delete(article_mark).where(article_mark.hash == cres[2])
|
||||||
session.execute(stmt)
|
session.execute(stmt)
|
||||||
@ -365,10 +355,9 @@ def solo_comment_remover(role:str, hash:str=None, sha1:str=None) -> Tuple[Dict,
|
|||||||
# 獲取檔案
|
# 獲取檔案
|
||||||
def solo_file_fetcher(role:str, fnhash:str) -> Tuple[Response, int]: # general, admin
|
def solo_file_fetcher(role:str, fnhash:str) -> Tuple[Response, int]: # general, admin
|
||||||
with db.getsession() as session:
|
with db.getsession() as session:
|
||||||
arta="SELECT posts.id, posts.hash, mark.mark, f FROM posts " \
|
arta="SELECT posts.id FROM posts " \
|
||||||
+"JOIN unnest(file_list) AS f ON 1=1 " \
|
+"INNER JOIN mark ON posts.hash=mark.hash " \
|
||||||
+"JOIN mark ON posts.hash = mark.hash " \
|
+"WHERE :fnhash=ANY (posts.file_list) "
|
||||||
+"WHERE f = :fnhash "
|
|
||||||
if role == "general":
|
if role == "general":
|
||||||
arta += "AND mark.mark = 'visible'"
|
arta += "AND mark.mark = 'visible'"
|
||||||
arta = session.execute(text(arta), {'fnhash':fnhash}).first()
|
arta = session.execute(text(arta), {'fnhash':fnhash}).first()
|
||||||
|
@ -13,25 +13,29 @@ def error_proto(message:str) -> bytes:
|
|||||||
).SerializeToString()
|
).SerializeToString()
|
||||||
|
|
||||||
|
|
||||||
def internal_json2protobuf(original:list|dict) -> bytes:
|
def internal_json2protobuf(original:dict) -> bytes:
|
||||||
if isinstance(original, dict):
|
otype = original["type"]
|
||||||
original = [original]
|
if otype == 'a':
|
||||||
|
rtype = niming_pb2.FetchPostResponse
|
||||||
|
elif otype == 'c':
|
||||||
|
rtype = niming_pb2.FetchCommentResponse
|
||||||
|
|
||||||
|
original = original["data"]
|
||||||
|
res = rtype()
|
||||||
|
|
||||||
res = niming_pb2.FetchResponse()
|
|
||||||
for o in original:
|
for o in original:
|
||||||
# drop null object
|
# drop null object
|
||||||
newo = {}
|
o = {k:v for k, v in o.items() if v is not None}
|
||||||
for oc in o:
|
|
||||||
if o[oc] is not None:
|
|
||||||
newo[oc] = o[oc]
|
|
||||||
o = newo
|
|
||||||
|
|
||||||
ob = niming_pb2.FetchResponse.Message()
|
if otype == "a":
|
||||||
|
ob = rtype.Message(id=o["id"])
|
||||||
if "id" in o: ob.id = o["id"]
|
|
||||||
if "content" in o: ob.content = o["content"]
|
|
||||||
if "igid" in o: ob.igid = o["igid"]
|
if "igid" in o: ob.igid = o["igid"]
|
||||||
if "files_hash" in o: ob.files_hash.extend(o["files_hash"])
|
if "files_hash" in o: ob.files_hash.extend(o["files_hash"])
|
||||||
if "comments_hash" in o: ob.comments_hash.extend(o["comments_hash"])
|
if "comments_hash" in o: ob.comments_hash.extend(o["comments_hash"])
|
||||||
|
elif otype == "c":
|
||||||
|
ob = rtype.Message(sha1=o["sha1"])
|
||||||
|
|
||||||
|
ob.content = o["content"]
|
||||||
|
|
||||||
res.posts.append(ob)
|
res.posts.append(ob)
|
||||||
return res.SerializeToString()
|
return res.SerializeToString()
|
||||||
|
@ -9,7 +9,7 @@ import sys
|
|||||||
import minio
|
import minio
|
||||||
from minio.deleteobjects import DeleteObject
|
from minio.deleteobjects import DeleteObject
|
||||||
|
|
||||||
S3_BUCKET = os.getenv("S3_BUCKET")
|
S3_BUCKET:str = os.getenv("S3_BUCKET")
|
||||||
|
|
||||||
s3 = minio.Minio(endpoint=os.getenv("S3_ENDPOINT"),
|
s3 = minio.Minio(endpoint=os.getenv("S3_ENDPOINT"),
|
||||||
access_key=os.getenv("S3_ACCESS_KEY"),
|
access_key=os.getenv("S3_ACCESS_KEY"),
|
||||||
@ -18,7 +18,7 @@ s3 = minio.Minio(endpoint=os.getenv("S3_ENDPOINT"),
|
|||||||
|
|
||||||
# check exist
|
# check exist
|
||||||
if not s3.bucket_exists(S3_BUCKET):
|
if not s3.bucket_exists(S3_BUCKET):
|
||||||
print("Where is S3 bucket \"%s\"?"%S3_BUCKET)
|
print("[X] Where is S3 bucket \"%s\"?"%S3_BUCKET)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# methods
|
# methods
|
||||||
@ -61,7 +61,9 @@ def solo_file_fetcher(fnhash:str) -> Tuple[dict | None, int]:
|
|||||||
|
|
||||||
def multi_file_remover(file_list) -> int:
|
def multi_file_remover(file_list) -> int:
|
||||||
try:
|
try:
|
||||||
s3.remove_objects(S3_BUCKET, [ DeleteObject(f) for f in file_list ])
|
if isinstance(file_list, list):
|
||||||
|
for f in file_list:
|
||||||
|
s3.remove_object(S3_BUCKET, f)
|
||||||
return 0
|
return 0
|
||||||
except:
|
except:
|
||||||
return 1
|
return 1
|
@ -11,16 +11,8 @@ def typechecker(name:str, value):
|
|||||||
if not(name in PLATFORM_SETTING_MODEL.keys()) or not(isinstance(value, PLATFORM_SETTING_MODEL.get(name)[0])):
|
if not(name in PLATFORM_SETTING_MODEL.keys()) or not(isinstance(value, PLATFORM_SETTING_MODEL.get(name)[0])):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# 確定是否是可迭代物件
|
# 如果是list: 檢查內容
|
||||||
iterable = False
|
if PLATFORM_SETTING_MODEL.get(name)[0] == list:
|
||||||
try:
|
|
||||||
iter(PLATFORM_SETTING_MODEL.get(name)[0])
|
|
||||||
iterable = True
|
|
||||||
except:
|
|
||||||
iterable = False
|
|
||||||
|
|
||||||
# 如果可迭代,就把裡面每個元素都檢查型別
|
|
||||||
if iterable:
|
|
||||||
for v in value:
|
for v in value:
|
||||||
if not(isinstance(v, PLATFORM_SETTING_MODEL.get(name)[1])): return 0
|
if not(isinstance(v, PLATFORM_SETTING_MODEL.get(name)[1])): return 0
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user