@ -9,19 +9,21 @@ from flask_migrate import Migrate
from sqlalchemy . sql . expression import func
from sqlalchemy . sql . expression import func
from mastodon import Mastodon
from mastodon import Mastodon
from models import db , User , Post , Comment , Attention , TagRecord , Syslog
from models import db , User , Post , Comment , Attention , TagRecord , Syslog , SearchDB
from utils import get_current_user , map_post , map_comment , map_syslog , check_attention , hash_name , look , get_num , tmp_token , is_admin
from utils import get_current_username , map_post , map_comments , map_syslog , check_attention , hash_name , look , get_num , tmp_token , is_admin , check_can_del , rds , RDS_KEY_POLL_OPTS , RDS_KEY_POLL_VOTES , gen_poll_dict , name_with_tmp_limit , RDS_KEY_BLOCK_SET , RDS_KEY_BLOCKED_COUNT , RDS_KEY_DANGEROUS_USERS , RDS_KEY_TITLE
app = Flask ( __name__ )
app = Flask ( __name__ )
app . config [ ' SQLALCHEMY_DATABASE_URI ' ] = ' sqlite:///hole.db '
app . config [ ' SQLALCHEMY_DATABASE_URI ' ] = ' sqlite:///hole.db '
app . config [ ' SQLALCHEMY_TRACK_MODIFICATIONS ' ] = False
app . config [ ' SQLALCHEMY_TRACK_MODIFICATIONS ' ] = False
app . config [ ' JSON_AS_ASCII ' ] = False
app . config [ ' JSON_AS_ASCII ' ] = False
app . config [ ' SALT ' ] = ' ' . join ( random . choices (
string . ascii_letters + string . digits , k = 32
) )
app . config . from_pyfile ( ' config.py ' )
app . config . from_pyfile ( ' config.py ' )
db . init_app ( app )
db . init_app ( app )
migrate = Migrate ( app , db )
migrate = Migrate ( app , db )
CS_LOGIN_URL = Mastodon ( api_base_url = app . config [ ' MASTODON_URL ' ] ) \
CS_LOGIN_URL = Mastodon ( api_base_url = app . config [ ' MASTODON_URL ' ] ) \
. auth_request_url (
. auth_request_url (
client_id = app . config [ ' CLIENT_ID ' ] ,
client_id = app . config [ ' CLIENT_ID ' ] ,
@ -36,6 +38,22 @@ limiter = Limiter(
)
)
PER_PAGE = 50
PER_PAGE = 50
DANGEROUS_USER_THRESHOLD = 10
class APIError ( Exception ) :
msg = ' 未知错误 '
def __init__ ( self , msg ) :
self . msg = msg
def __str__ ( self ) :
return str ( self . msg )
@app . errorhandler ( APIError )
def handle_api_error ( e ) :
return { ' code ' : 1 , ' msg ' : e . msg }
@app . route ( ' /_login ' )
@app . route ( ' /_login ' )
@ -86,7 +104,7 @@ def auth():
@app . route ( ' /_api/v1/getlist ' )
@app . route ( ' /_api/v1/getlist ' )
def get_list ( ) :
def get_list ( ) :
u = get_current_user ( )
username = get_current_username ( )
p = request . args . get ( ' p ' , type = int , default = 1 )
p = request . args . get ( ' p ' , type = int , default = 1 )
order_mode = request . args . get ( ' order_mode ' , type = int , default = 0 )
order_mode = request . args . get ( ' order_mode ' , type = int , default = 0 )
@ -111,11 +129,12 @@ def get_list():
posts = query . order_by ( order ) . paginate ( p , PER_PAGE )
posts = query . order_by ( order ) . paginate ( p , PER_PAGE )
data = list ( map ( map_post , posts . items , [ u . name ] * len ( posts . items ) ) )
data = list ( map ( map_post , posts . items , [ user name ] * len ( posts . items ) ) )
return {
return {
' code ' : 0 ,
' code ' : 0 ,
' tmp_token ' : tmp_token ( ) ,
' tmp_token ' : tmp_token ( ) ,
' custom_title ' : rds . hget ( RDS_KEY_TITLE , hash_name ( username ) ) ,
' count ' : len ( data ) ,
' count ' : len ( data ) ,
' data ' : data
' data ' : data
}
}
@ -123,15 +142,39 @@ def get_list():
@app . route ( ' /_api/v1/getone ' )
@app . route ( ' /_api/v1/getone ' )
def get_one ( ) :
def get_one ( ) :
u = get_current_user ( )
username = get_current_username ( )
pid = request . args . get ( ' pid ' , type = int )
pid = request . args . get ( ' pid ' , type = int )
post = Post . query . get_or_404 ( pid )
post = Post . query . get_or_404 ( pid )
if post . deleted or post . is_reported :
if post . deleted or post . is_reported and not (
check_can_del ( username , post . name_hash )
) :
abort ( 451 )
abort ( 451 )
data = map_post ( post , u . name )
data = map_post ( post , username )
return {
' code ' : 0 ,
' data ' : data
}
@app . route ( ' /_api/v1/getmulti ' )
def get_multi ( ) :
username = get_current_username ( )
pids = request . args . getlist ( ' pids ' )
pids = pids [ : 500 ] or [ 0 ]
posts = Post . query . filter (
Post . id . in_ ( pids )
) . filter_by (
deleted = False
) . order_by (
Post . id . desc ( )
) . all ( )
data = [ map_post ( post , username ) for post in posts ]
return {
return {
' code ' : 0 ,
' code ' : 0 ,
@ -141,45 +184,77 @@ def get_one():
@app . route ( ' /_api/v1/search ' )
@app . route ( ' /_api/v1/search ' )
def search ( ) :
def search ( ) :
u = get_current_user ( )
username = get_current_username ( )
page = request . args . get ( ' page ' , type = int , default = 1 )
page = request . args . get ( ' page ' , type = int , default = 1 )
pagesize = min ( request . args . get ( ' pagesize ' , type = int , default = 200 ) , 200 )
search_mode = request . args . get ( ' search_mode ' , type = int )
keywords = request . args . get ( ' keywords ' )
pagesize = min ( request . args . get ( ' pagesize ' , type = int , default = PER_PAGE ) , 2 * PER_PAGE )
keywords = request . args . get ( ' keywords ' , ' ' ) . strip ( )
if not keywords :
if not keywords :
abort ( 422 )
raise APIError ( " 搜索词不可为空 " )
if search_mode is None :
raise APIError ( " 请点击“强制检查更新”,更新网页到最新版 " )
tag_pids = TagRecord . query . with_entities (
data = [ ]
TagRecord . pid
) . filter_by (
tag = keywords
) . all ( )
tag_pids = [ tag_pid for tag_pid , in tag_pids ] or [ 0 ] # sql not allowed empty in
if search_mode == 0 : # tag 搜索
tag_pids = TagRecord . query . with_entities (
TagRecord . pid
) . filter_by ( tag = keywords ) . order_by (
TagRecord . pid . desc ( )
) . limit ( pagesize ) . offset ( ( page - 1 ) * pagesize ) . all ( )
posts = Post . query . filter (
tag_pids = [
Post . search_text . like ( " % {} % " . format ( keywords ) )
tag_pid for tag_pid , in tag_pids ] or [ 0 ] # sql not allowed empty in
) . filter (
Post . id . notin_ ( tag_pids )
) . filter_by (
deleted = False , is_reported = False
) . order_by (
Post . id . desc ( )
) . limit ( pagesize ) . offset ( ( page - 1 ) * pagesize ) . all ( )
if page == 1 :
posts = Post . query . filter ( Post . id . in_ ( tag_pids ) ) . filter_by (
posts = Post . query . filter (
deleted = False , is_reported = False
Post . id . in_ ( tag_pids )
) . order_by ( Post . id . desc ( ) ) . all ( )
) . filter_by (
data = [
map_post ( post , username )
for post in posts
]
elif search_mode == 1 : # 全文搜索
search_db = SearchDB ( )
for highlighted_content , obj_type , obj_id in search_db . query (
keywords , pagesize , ( page - 1 ) * pagesize
) :
if obj_type == ' post ' :
obj = Post . query . get ( obj_id )
else :
obj = Comment . query . get ( obj_id )
if not obj or obj . deleted :
continue
if obj_type == ' post ' :
post = obj
else :
post = obj . post
if not post or post . deleted or post . is_reported :
continue
obj . content = highlighted_content
if obj_type == ' post ' :
post_dict = map_post ( post , username )
else :
post_dict = map_post ( post , username , 1000 )
post_dict [ ' comments ' ] = [
c for c in post_dict [ ' comments ' ] if c [ ' cid ' ] == obj_id
]
post_dict [ ' key ' ] = " search_ %s _ %s " % ( obj_type , obj_id )
data . append ( post_dict )
del search_db
elif search_mode == 2 : # 头衔
posts = Post . query . filter_by ( author_title = keywords ) . filter_by (
deleted = False , is_reported = False
deleted = False , is_reported = False
) . order_by (
) . order_by (
Post . id . desc ( )
Post . id . desc ( )
) . all ( ) + posts
) . limit ( pagesize ) . offset ( ( page - 1 ) * pagesize ) . all ( )
data = [
data = [
map_post ( post , username )
map_post ( post , u . name )
for post in posts
for post in posts
]
]
return {
return {
' code ' : 0 ,
' code ' : 0 ,
@ -191,56 +266,61 @@ def search():
@app . route ( ' /_api/v1/dopost ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/dopost ' , methods = [ ' POST ' ] )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
def do_post ( ) :
def do_post ( ) :
u = get_current_user ( )
username = get_current_username ( )
allow_search = request . form . get ( ' allow_search ' )
allow_search = request . form . get ( ' allow_search ' )
print ( allow_search )
content = request . form . get ( ' text ' , ' ' ) . strip ( )
content = request . form . get ( ' text ' )
content = ( ' [tmp] \n ' if username [ : 4 ] == ' tmp_ ' else ' ' ) + content
content = content . strip ( ) if content else None
content = ' [tmp] \n ' + content if u . name [ : 4 ] == ' tmp_ ' else content
post_type = request . form . get ( ' type ' )
post_type = request . form . get ( ' type ' )
cw = request . form . get ( ' cw ' )
cw = request . form . get ( ' cw ' , ' ' ) . strip ( )
cw = cw . strip ( ) if cw else None
poll_options = request . form . getlist ( ' poll_options ' )
use_title = request . form . get ( ' use_title ' )
if not content or len ( content ) > 4096 :
abort ( 422 )
if not content or len ( content ) > 4096 or len ( cw ) > 32 :
if cw and len ( cw ) > 32 :
raise APIError ( ' 无内容或超长 ' )
abort ( 422 )
if poll_options and poll_options [ 0 ] :
search_text = content . replace (
if len ( poll_options ) != len ( set ( poll_options ) ) :
' \n ' , ' ' ) if allow_search else ' '
raise APIError ( ' 有重复的投票选项 ' )
if len ( poll_options ) > 8 :
raise APIError ( ' 选项过多 ' )
if max ( map ( len , poll_options ) ) > 32 :
raise APIError ( ' 选项过长 ' )
name_hash = hash_name ( username )
p = Post (
p = Post (
name_hash = hash_name ( u . name ) ,
name_hash = name_hash ,
author_title = rds . hget ( RDS_KEY_TITLE , name_hash ) if use_title else None ,
content = content ,
content = content ,
search_text = search_text ,
allow_search = bool ( allow_search ) ,
post_type = post_type ,
post_type = post_type ,
cw = cw or None ,
cw = cw or None ,
likenum = 1 ,
likenum = 1 ,
comments = [ ]
comments = [ ]
)
)
if post_type == ' text ' :
pass
elif post_type == ' image ' :
# TODO
p . file_url = ' foo bar '
else :
abort ( 422 )
db . session . add ( p )
db . session . add ( p )
db . session . commit ( )
db . session . commit ( )
tags = re . findall ( ' (^| \\ s)#([^# \\ s] { 1,32}) ' , content )
tags = re . findall ( ' (^| \\ s)#([^# \\ s] { 1,32}) ' , content )
# print(tags)
for t in tags :
for t in tags :
tag = t [ 1 ]
tag = t [ 1 ]
if not re . match ( ' \\ d+ ' , tag ) :
if not re . match ( ' \\ d+ ' , tag ) :
db . session . add ( TagRecord ( tag = tag , pid = p . id ) )
db . session . add ( TagRecord ( tag = tag , pid = p . id ) )
db . session . add ( Attention ( name_hash = hash_name ( u . name ) , pid = p . id ) )
db . session . add ( Attention ( name_hash = hash_name ( user name ) , pid = p . id ) )
db . session . commit ( )
db . session . commit ( )
if allow_search :
search_db = SearchDB ( )
search_db . insert ( content , ' post ' , p . id )
search_db . commit ( )
del search_db
rds . delete ( RDS_KEY_POLL_OPTS % p . id ) # 由于历史原因,现在的数据库里发布后删再发布可能导致id重复
if poll_options and poll_options [ 0 ] :
rds . rpush ( RDS_KEY_POLL_OPTS % p . id , * poll_options )
return {
return {
' code ' : 0 ,
' code ' : 0 ,
' date ' : p . id
' date ' : p . id
@ -250,7 +330,7 @@ def do_post():
@app . route ( ' /_api/v1/editcw ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/editcw ' , methods = [ ' POST ' ] )
@limiter . limit ( " 50 / hour; 1 / 2 second " )
@limiter . limit ( " 50 / hour; 1 / 2 second " )
def edit_cw ( ) :
def edit_cw ( ) :
u = get_current_user ( )
username = get_current_username ( )
cw = request . form . get ( ' cw ' )
cw = request . form . get ( ' cw ' )
pid = get_num ( request . form . get ( ' pid ' ) )
pid = get_num ( request . form . get ( ' pid ' ) )
@ -260,11 +340,8 @@ def edit_cw():
abort ( 422 )
abort ( 422 )
post = Post . query . get_or_404 ( pid )
post = Post . query . get_or_404 ( pid )
if post . deleted :
abort ( 451 )
if not ( u . name in app . config . get ( ' ADMINS ' )
if not check_can_del ( username , post . name_hash ) :
or hash_name ( u . name ) == post . name_hash ) :
abort ( 403 )
abort ( 403 )
post . cw = cw
post . cw = cw
@ -275,21 +352,19 @@ def edit_cw():
@app . route ( ' /_api/v1/getcomment ' )
@app . route ( ' /_api/v1/getcomment ' )
def get_comment ( ) :
def get_comment ( ) :
u = get_current_user ( )
username = get_current_username ( )
pid = get_num ( request . args . get ( ' pid ' ) )
pid = get_num ( request . args . get ( ' pid ' ) )
post = Post . query . get ( pid )
post = Post . query . get_or_404 ( pid )
if not post :
if post . deleted and not check_can_del ( username , post . name_hash ) :
abort ( 404 )
if post . deleted :
abort ( 451 )
abort ( 451 )
data = map_comment ( post , u . name )
data = map_comments ( post , user name )
return {
return {
' code ' : 0 ,
' code ' : 0 ,
' attention ' : check_attention ( u . name , pid ) ,
' attention ' : check_attention ( user name , pid ) ,
' likenum ' : post . likenum ,
' likenum ' : post . likenum ,
' data ' : data
' data ' : data
}
}
@ -298,38 +373,43 @@ def get_comment():
@app . route ( ' /_api/v1/docomment ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/docomment ' , methods = [ ' POST ' ] )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
def do_comment ( ) :
def do_comment ( ) :
u = get_current_user ( )
username = get_current_username ( )
pid = get_num ( request . form . get ( ' pid ' ) )
pid = get_num ( request . form . get ( ' pid ' ) )
post = Post . query . get ( pid )
post = Post . query . get ( pid )
if not post :
if not post :
abort ( 404 )
abort ( 404 )
if post . deleted :
if post . deleted and not check_can_del ( username , post . name_hash ) :
abort ( 451 )
abort ( 451 )
content = request . form . get ( ' text ' )
content = request . form . get ( ' text ' , ' ' ) . strip ( )
content = content . strip ( ) if content else None
if username . startswith ( ' tmp_ ' ) :
content = ' [tmp] \n ' + content if u . name [ : 4 ] == ' tmp_ ' else content
content = ' [tmp] \n ' + content
if not content or len ( content ) > 4096 :
if not content or len ( content ) > 4096 :
abort ( 422 )
abort ( 422 )
use_title = request . form . get ( ' use_title ' )
name_hash = hash_name ( username )
c = Comment (
c = Comment (
name_hash = hash_name ( u . name ) ,
name_hash = name_hash ,
author_title = rds . hget ( RDS_KEY_TITLE , name_hash ) if use_title else None ,
content = content ,
content = content ,
)
)
post . comments . append ( c )
post . comments . append ( c )
post . comment_timestamp = c . timestamp
post . comment_timestamp = c . timestamp
post . n_comments + = 1
if post . hot_score != - 1 :
if post . hot_score != - 1 :
post . hot_score + = 1
post . hot_score + = 1
at = Attention . query . filter_by (
at = Attention . query . filter_by (
name_hash = hash_name ( u . name ) , pid = pid
name_hash = hash_name ( user name ) , pid = pid
) . first ( )
) . first ( )
if not at :
if not at :
at = Attention ( name_hash = hash_name ( u . name ) , pid = pid , disabled = False )
at = Attention ( name_hash = hash_name ( user name ) , pid = pid , disabled = False )
db . session . add ( at )
db . session . add ( at )
post . likenum + = 1
post . likenum + = 1
if post . hot_score != - 1 :
if post . hot_score != - 1 :
@ -341,6 +421,12 @@ def do_comment():
db . session . commit ( )
db . session . commit ( )
if post . allow_search :
search_db = SearchDB ( )
search_db . insert ( content , ' comment ' , c . id )
search_db . commit ( )
del search_db
return {
return {
' code ' : 0 ,
' code ' : 0 ,
' data ' : pid
' data ' : pid
@ -350,51 +436,52 @@ def do_comment():
@app . route ( ' /_api/v1/attention ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/attention ' , methods = [ ' POST ' ] )
@limiter . limit ( " 200 / hour; 1 / second " )
@limiter . limit ( " 200 / hour; 1 / second " )
def attention ( ) :
def attention ( ) :
u = get_current_user ( )
username = get_current_username ( )
if u . name [ : 4 ] == ' tmp_ ' :
if user name [ : 4 ] == ' tmp_ ' :
abort ( 403 )
raise APIError ( ' 临时用户无法手动关注 ' )
s = request . form . get ( ' switch ' )
s = request . form . get ( ' switch ' , type = int )
if s not in [ ' 0 ' , ' 1 ' ] :
if s not in [ 0 , 1 ] :
abort ( 422 )
abort ( 422 )
pid = get_num ( request . form . get ( ' pid ' ) )
pid = request . form . get ( ' pid ' , type = int )
post = Post . query . get ( pid )
post = Post . query . get_or_404 ( pid )
if not post :
abort ( 404 )
at = Attention . query . filter_by (
at = Attention . query . filter_by (
name_hash = hash_name ( u . name ) , pid = pid
name_hash = hash_name ( user name ) , pid = pid
) . first ( )
) . first ( )
if not at :
if not at :
at = Attention ( name_hash = hash_name ( u . name ) , pid = pid , disabled = True )
at = Attention ( name_hash = hash_name ( user name ) , pid = pid , disabled = True )
db . session . add ( at )
db . session . add ( at )
if post . hot_score != - 1 :
if post . hot_score != - 1 :
post . hot_score + = 2
post . hot_score + = 2
if ( at . disabled != ( s == ' 0 ' ) ) :
if at . disabled == bool ( s ) :
at . disabled = ( s == ' 0 ' )
at . disabled = not bool ( s )
post . likenum + = 1 - 2 * int ( s == ' 0 ' )
post . likenum + = 2 * s - 1
if is_admin ( username ) and s :
post . is_reported = False
db . session . commit ( )
db . session . commit ( )
return {
return {
' code ' : 0 ,
' code ' : 0 ,
' likenum ' : post . likenum ,
' likenum ' : post . likenum ,
' attention ' : ( s == ' 1 ' )
' attention ' : bool ( s )
}
}
@app . route ( ' /_api/v1/getattention ' )
@app . route ( ' /_api/v1/getattention ' )
def get_attention ( ) :
def get_attention ( ) :
u = get_current_user ( )
username = get_current_username ( )
ats = Attention . query . with_entities (
ats = Attention . query . with_entities (
Attention . pid
Attention . pid
) . filter_by (
) . filter_by (
name_hash = hash_name ( u . name ) , disabled = False
name_hash = hash_name ( user name ) , disabled = False
) . all ( )
) . all ( )
pids = [ pid for pid , in ats ] or [ 0 ] # sql not allow empty in
pids = [ pid for pid , in ats ] or [ 0 ] # sql not allow empty in
@ -405,7 +492,7 @@ def get_attention():
) . order_by ( Post . id . desc ( ) ) . all ( )
) . order_by ( Post . id . desc ( ) ) . all ( )
data = [
data = [
map_post ( post , u . name , 10 )
map_post ( post , user name , 10 )
for post in posts
for post in posts
]
]
@ -419,7 +506,7 @@ def get_attention():
@app . route ( ' /_api/v1/delete ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/delete ' , methods = [ ' POST ' ] )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
@limiter . limit ( " 50 / hour; 1 / 3 second " )
def delete ( ) :
def delete ( ) :
u = get_current_user ( )
username = get_current_username ( )
obj_type = request . form . get ( ' type ' )
obj_type = request . form . get ( ' type ' )
obj_id = get_num ( request . form . get ( ' id ' ) )
obj_id = get_num ( request . form . get ( ' id ' ) )
@ -428,29 +515,36 @@ def delete():
if note and len ( note ) > 100 :
if note and len ( note ) > 100 :
abort ( 422 )
abort ( 422 )
obj = None
# 兼容
if obj_type == ' pid ' :
if obj_type == ' pid ' :
obj = Post . query . get ( obj_id )
obj_type = ' post '
elif obj_type == ' cid ' :
elif obj_type == ' cid ' :
obj_type = ' comment '
obj = None
if obj_type == ' post ' :
obj = Post . query . get ( obj_id )
elif obj_type == ' comment ' :
obj = Comment . query . get ( obj_id )
obj = Comment . query . get ( obj_id )
if not obj :
if not obj :
abort ( 404 )
abort ( 404 )
if obj . name_hash == hash_name ( u . name ) :
if obj . name_hash == hash_name ( user name ) :
if obj_type == ' pid ' :
if obj_type == ' post ' :
if len ( obj . comments ) :
if obj . n_ comments:
abort ( 403 )
abort ( " 已经有评论了 " )
Attention . query . filter_by ( pid = obj . id ) . delete ( )
Attention . query . filter_by ( pid = obj . id ) . delete ( )
TagRecord . query . filter_by ( pid = obj . id ) . delete ( )
TagRecord . query . filter_by ( pid = obj . id ) . delete ( )
db . session . delete ( obj )
db . session . delete ( obj )
else :
else :
obj . deleted = True
obj . deleted = True
elif u . name in app . config . get ( ' ADMINS ' ) :
elif username in app . config . get ( ' ADMINS ' ) :
obj . deleted = True
obj . deleted = True
db . session . add ( Syslog (
db . session . add ( Syslog (
log_type = ' ADMIN DELETE ' ,
log_type = ' ADMIN DELETE ' ,
log_detail = f " { obj_type } = { obj_id } \n { note } " ,
log_detail = f " { obj_type } = { obj_id } \n { note } " ,
name_hash = hash_name ( u . name )
name_hash = hash_name ( user name )
) )
) )
if note . startswith ( ' !ban ' ) :
if note . startswith ( ' !ban ' ) :
db . session . add ( Syslog (
db . session . add ( Syslog (
@ -461,13 +555,16 @@ def delete():
else :
else :
abort ( 403 )
abort ( 403 )
if obj_type == ' comment ' :
obj . post . n_comments - = 1
db . session . commit ( )
db . session . commit ( )
return { ' code ' : 0 }
return { ' code ' : 0 }
@app . route ( ' /_api/v1/systemlog ' )
@app . route ( ' /_api/v1/systemlog ' )
def system_log ( ) :
def system_log ( ) :
u = get_current_user ( )
username = get_current_username ( )
ss = Syslog . query . order_by ( db . desc ( ' timestamp ' ) ) . limit ( 100 ) . all ( )
ss = Syslog . query . order_by ( db . desc ( ' timestamp ' ) ) . limit ( 100 ) . all ( )
@ -475,23 +572,22 @@ def system_log():
' start_time ' : app . config [ ' START_TIME ' ] ,
' start_time ' : app . config [ ' START_TIME ' ] ,
' salt ' : look ( app . config [ ' SALT ' ] ) ,
' salt ' : look ( app . config [ ' SALT ' ] ) ,
' tmp_token ' : tmp_token ( ) ,
' tmp_token ' : tmp_token ( ) ,
' data ' : [ map_syslog ( s , u ) for s in ss ]
' custom_title ' : rds . hget ( RDS_KEY_TITLE , hash_name ( username ) ) ,
' data ' : [ map_syslog ( s , username ) for s in ss ]
}
}
@app . route ( ' /_api/v1/report ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/report ' , methods = [ ' POST ' ] )
@limiter . limit ( " 10 / hour; 1 / 3 second " )
@limiter . limit ( " 10 / hour; 1 / 3 second " )
def report ( ) :
def report ( ) :
u = get_current_user ( )
username = get_current_username ( )
pid = get_num ( request . form . get ( ' pid ' ) )
pid = get_num ( request . form . get ( ' pid ' ) )
reason = request . form . get ( ' reason ' , ' ' )
reason = request . form . get ( ' reason ' , ' ' )
db . session . add ( Syslog (
db . session . add ( Syslog (
log_type = ' REPORT ' ,
log_type = ' REPORT ' ,
log_detail = f " pid= { pid } \n { reason } " ,
log_detail = f " pid= { pid } \n { reason } " ,
name_hash = hash_name ( u . name )
name_hash = hash_name ( user name )
) )
) )
post = Post . query . get ( pid )
post = Post . query . get ( pid )
@ -505,9 +601,8 @@ def report():
@app . route ( ' /_api/v1/update_score ' , methods = [ ' POST ' ] )
@app . route ( ' /_api/v1/update_score ' , methods = [ ' POST ' ] )
def edit_hot_score ( ) :
def edit_hot_score ( ) :
u = get_current_user ( )
username = get_current_username ( )
if not is_admin ( u . name ) :
if not is_admin ( username ) :
print ( u . name )
abort ( 403 )
abort ( 403 )
pid = request . form . get ( ' pid ' , type = int )
pid = request . form . get ( ' pid ' , type = int )
@ -520,5 +615,90 @@ def edit_hot_score():
return { ' code ' : 0 }
return { ' code ' : 0 }
@app . route ( ' /_api/v1/vote ' , methods = [ ' POST ' ] )
@limiter . limit ( " 100 / hour; 1 / 2 second " )
def add_vote ( ) :
username = get_current_username ( )
username = name_with_tmp_limit ( username )
pid = request . form . get ( ' pid ' , type = int )
vote = request . form . get ( ' vote ' )
if not rds . exists ( RDS_KEY_POLL_OPTS % pid ) :
abort ( 404 )
opts = rds . lrange ( RDS_KEY_POLL_OPTS % pid , 0 , - 1 )
for idx , opt in enumerate ( opts ) :
if rds . sismember ( RDS_KEY_POLL_VOTES % ( pid , idx ) , hash_name ( username ) ) :
raise APIError ( ' 已经投过票了 ' )
if vote not in opts :
raise APIError ( ' 无效的选项 ' )
rds . sadd ( RDS_KEY_POLL_VOTES % ( pid , opts . index ( vote ) ) , hash_name ( username ) )
return {
' code ' : 0 ,
' data ' : gen_poll_dict ( pid , username )
}
@app . route ( ' /_api/v1/block ' , methods = [ ' POST ' ] )
@limiter . limit ( " 15 / hour; 1 / 2 second " )
def block_user_by_target ( ) :
username = get_current_username ( )
target_type = request . form . get ( ' type ' )
target_id = request . form . get ( ' id ' , type = int )
if username . startswith ( ' tmp_ ' ) :
raise APIError ( ' 临时用户无法拉黑 ' )
if target_type == ' post ' :
target = Post . query . get_or_404 ( target_id )
elif target_type == ' comment ' :
target = Comment . query . get_or_404 ( target_id )
else :
raise APIError ( ' 无效的type ' )
if hash_name ( username ) == target . name_hash :
raise APIError ( ' 不可拉黑自己 ' )
if is_admin ( username ) :
rds . sadd ( RDS_KEY_DANGEROUS_USERS , target . name_hash )
curr_cnt = rds . hget ( RDS_KEY_BLOCKED_COUNT , target . name_hash )
else :
if rds . sismember ( RDS_KEY_BLOCK_SET % username , target . name_hash ) :
raise APIError ( ' 已经拉黑了 ' )
rds . sadd ( RDS_KEY_BLOCK_SET % username , target . name_hash )
curr_cnt = rds . hincrby ( RDS_KEY_BLOCKED_COUNT , target . name_hash , 1 )
if curr_cnt > = DANGEROUS_USER_THRESHOLD :
rds . sadd ( RDS_KEY_DANGEROUS_USERS , target . name_hash )
return {
' code ' : 0 ,
' data ' : {
' curr ' : curr_cnt ,
' threshold ' : DANGEROUS_USER_THRESHOLD
}
}
@app . route ( ' /_api/v1/title ' , methods = [ ' POST ' ] )
@limiter . limit ( " 10 / hour; 1 / 2 second " )
def set_title ( ) :
username = get_current_username ( )
title = request . form . get ( ' title ' )
if not title :
rds . hdel ( RDS_KEY_TITLE , hash_name ( username ) )
else :
if len ( title ) > 10 :
raise APIError ( ' 自定义头衔太长 ' )
if title in rds . hvals ( RDS_KEY_TITLE ) : # 如果未来量大还是另外用个set维护
raise APIError ( ' 已经被使用了 ' )
rds . hset ( RDS_KEY_TITLE , hash_name ( username ) , title )
return { ' code ' : 0 }
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :
app . run ( debug = True )
app . run ( debug = True )