From 3978af5d09251e7d5e9127975e64ba610688445c Mon Sep 17 00:00:00 2001 From: hole-thu Date: Sun, 19 Dec 2021 03:51:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.sample.py | 3 -- hole.py | 108 +++++++++++++++++++++++------------------------ models.py | 3 +- requirements.txt | 15 ++++--- utils.py | 21 +++++---- 5 files changed, 74 insertions(+), 76 deletions(-) diff --git a/config.sample.py b/config.sample.py index 41b2db0..cdf647c 100644 --- a/config.sample.py +++ b/config.sample.py @@ -1,5 +1,3 @@ -import random -import string import time SQLALCHEMY_DATABASE_URI = 'sqlite:///hole.db' @@ -9,7 +7,6 @@ CLIENT_ID = '' CLIENT_SECRET = '' MASTODON_URL = 'https://mastodon.social' REDIRECT_URI = 'http://hole.thu.monster/_auth' -SALT = ''.join(random.choices(string.ascii_letters + string.digits, k=32)) ADMINS = ['cs_114514'] START_TIME = int(time.time()) ENABLE_TMP = True diff --git a/hole.py b/hole.py index 98af0be..7ae9bd7 100644 --- a/hole.py +++ b/hole.py @@ -10,12 +10,15 @@ from sqlalchemy.sql.expression import func from mastodon import Mastodon from models import db, User, Post, Comment, Attention, TagRecord, Syslog -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_comment, map_syslog, check_attention, hash_name, look, get_num, tmp_token, is_admin, check_can_del app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hole.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = 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') db.init_app(app) @@ -86,7 +89,7 @@ def auth(): @app.route('/_api/v1/getlist') def get_list(): - u = get_current_user() + username = get_current_username() p = request.args.get('p', type=int, default=1) order_mode = request.args.get('order_mode', type=int, default=0) @@ -111,7 +114,7 @@ def get_list(): 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, [username] * len(posts.items))) return { 'code': 0, @@ -123,15 +126,17 @@ def get_list(): @app.route('/_api/v1/getone') def get_one(): - u = get_current_user() + username = get_current_username() pid = request.args.get('pid', type=int) 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) - data = map_post(post, u.name) + data = map_post(post, username) return { 'code': 0, @@ -141,7 +146,7 @@ def get_one(): @app.route('/_api/v1/search') def search(): - u = get_current_user() + username = get_current_username() page = request.args.get('page', type=int, default=1) pagesize = min(request.args.get('pagesize', type=int, default=200), 200) @@ -155,7 +160,8 @@ def search(): tag=keywords ).all() - tag_pids = [tag_pid for tag_pid, in tag_pids] or [0] # sql not allowed empty in + tag_pids = [ + tag_pid for tag_pid, in tag_pids] or [0] # sql not allowed empty in posts = Post.query.filter( Post.search_text.like("%{}%".format(keywords)) @@ -177,7 +183,7 @@ def search(): ).all() + posts data = [ - map_post(post, u.name) + map_post(post, username) for post in posts ] @@ -191,27 +197,23 @@ def search(): @app.route('/_api/v1/dopost', methods=['POST']) @limiter.limit("50 / hour; 1 / 3 second") def do_post(): - u = get_current_user() + username = get_current_username() allow_search = request.form.get('allow_search') print(allow_search) - content = request.form.get('text') - content = content.strip() if content else None - content = '[tmp]\n' + content if u.name[:4] == 'tmp_' else content + content = request.form.get('text', '').strip() + content = ('[tmp]\n' if username[:4] == 'tmp_' else '') + content post_type = request.form.get('type') - cw = request.form.get('cw') - cw = cw.strip() if cw else None + cw = request.form.get('cw', '').strip() - if not content or len(content) > 4096: - abort(422) - if cw and len(cw) > 32: + if not content or len(content) > 4096 or len(cw) > 32: abort(422) search_text = content.replace( '\n', '') if allow_search else '' p = Post( - name_hash=hash_name(u.name), + name_hash=hash_name(username), content=content, search_text=search_text, post_type=post_type, @@ -238,7 +240,7 @@ def do_post(): if not re.match('\\d+', tag): 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(username), pid=p.id)) db.session.commit() return { @@ -250,7 +252,7 @@ def do_post(): @app.route('/_api/v1/editcw', methods=['POST']) @limiter.limit("50 / hour; 1 / 2 second") def edit_cw(): - u = get_current_user() + username = get_current_username() cw = request.form.get('cw') pid = get_num(request.form.get('pid')) @@ -260,11 +262,8 @@ def edit_cw(): abort(422) post = Post.query.get_or_404(pid) - if post.deleted: - abort(451) - if not (u.name in app.config.get('ADMINS') - or hash_name(u.name) == post.name_hash): + if not check_can_del(username, post.name_hash): abort(403) post.cw = cw @@ -275,21 +274,19 @@ def edit_cw(): @app.route('/_api/v1/getcomment') def get_comment(): - u = get_current_user() + username = get_current_username() pid = get_num(request.args.get('pid')) - post = Post.query.get(pid) - if not post: - abort(404) - if post.deleted: + post = Post.query.get_or_404(pid) + if post.deleted and not check_can_del(username, post.name_hash): abort(451) - data = map_comment(post, u.name) + data = map_comment(post, username) return { 'code': 0, - 'attention': check_attention(u.name, pid), + 'attention': check_attention(username, pid), 'likenum': post.likenum, 'data': data } @@ -298,24 +295,24 @@ def get_comment(): @app.route('/_api/v1/docomment', methods=['POST']) @limiter.limit("50 / hour; 1 / 3 second") def do_comment(): - u = get_current_user() + username = get_current_username() pid = get_num(request.form.get('pid')) post = Post.query.get(pid) if not post: abort(404) - if post.deleted: + if post.deleted and not check_can_del(username, post.name_hash): abort(451) content = request.form.get('text') content = content.strip() if content else None - content = '[tmp]\n' + content if u.name[:4] == 'tmp_' else content + content = '[tmp]\n' + content if username[:4] == 'tmp_' else content if not content or len(content) > 4096: abort(422) c = Comment( - name_hash=hash_name(u.name), + name_hash=hash_name(username), content=content, ) post.comments.append(c) @@ -325,11 +322,11 @@ def do_comment(): post.hot_score += 1 at = Attention.query.filter_by( - name_hash=hash_name(u.name), pid=pid + name_hash=hash_name(username), pid=pid ).first() if not at: - at = Attention(name_hash=hash_name(u.name), pid=pid, disabled=False) + at = Attention(name_hash=hash_name(username), pid=pid, disabled=False) db.session.add(at) post.likenum += 1 if post.hot_score != -1: @@ -350,8 +347,8 @@ def do_comment(): @app.route('/_api/v1/attention', methods=['POST']) @limiter.limit("200 / hour; 1 / second") def attention(): - u = get_current_user() - if u.name[:4] == 'tmp_': + username = get_current_username() + if username[:4] == 'tmp_': abort(403) s = request.form.get('switch') @@ -365,11 +362,11 @@ def attention(): abort(404) at = Attention.query.filter_by( - name_hash=hash_name(u.name), pid=pid + name_hash=hash_name(username), pid=pid ).first() if not at: - at = Attention(name_hash=hash_name(u.name), pid=pid, disabled=True) + at = Attention(name_hash=hash_name(username), pid=pid, disabled=True) db.session.add(at) if post.hot_score != -1: post.hot_score += 2 @@ -389,12 +386,12 @@ def attention(): @app.route('/_api/v1/getattention') def get_attention(): - u = get_current_user() + username = get_current_username() ats = Attention.query.with_entities( Attention.pid ).filter_by( - name_hash=hash_name(u.name), disabled=False + name_hash=hash_name(username), disabled=False ).all() pids = [pid for pid, in ats] or [0] # sql not allow empty in @@ -405,7 +402,7 @@ def get_attention(): ).order_by(Post.id.desc()).all() data = [ - map_post(post, u.name, 10) + map_post(post, username, 10) for post in posts ] @@ -419,7 +416,7 @@ def get_attention(): @app.route('/_api/v1/delete', methods=['POST']) @limiter.limit("50 / hour; 1 / 3 second") def delete(): - u = get_current_user() + username = get_current_username() obj_type = request.form.get('type') obj_id = get_num(request.form.get('id')) @@ -436,7 +433,7 @@ def delete(): if not obj: abort(404) - if obj.name_hash == hash_name(u.name): + if obj.name_hash == hash_name(username): if obj_type == 'pid': if len(obj.comments): abort(403) @@ -445,12 +442,12 @@ def delete(): db.session.delete(obj) else: obj.deleted = True - elif u.name in app.config.get('ADMINS'): + elif username in app.config.get('ADMINS'): obj.deleted = True db.session.add(Syslog( log_type='ADMIN DELETE', log_detail=f"{obj_type}={obj_id}\n{note}", - name_hash=hash_name(u.name) + name_hash=hash_name(username) )) if note.startswith('!ban'): db.session.add(Syslog( @@ -467,7 +464,7 @@ def delete(): @app.route('/_api/v1/systemlog') def system_log(): - u = get_current_user() + username = get_current_username() ss = Syslog.query.order_by(db.desc('timestamp')).limit(100).all() @@ -475,14 +472,14 @@ def system_log(): 'start_time': app.config['START_TIME'], 'salt': look(app.config['SALT']), 'tmp_token': tmp_token(), - 'data': [map_syslog(s, u) for s in ss] + 'data': [map_syslog(s, username) for s in ss] } @app.route('/_api/v1/report', methods=['POST']) @limiter.limit("10 / hour; 1 / 3 second") def report(): - u = get_current_user() + username = get_current_username() pid = get_num(request.form.get('pid')) @@ -491,7 +488,7 @@ def report(): db.session.add(Syslog( log_type='REPORT', log_detail=f"pid={pid}\n{reason}", - name_hash=hash_name(u.name) + name_hash=hash_name(username) )) post = Post.query.get(pid) @@ -505,9 +502,8 @@ def report(): @app.route('/_api/v1/update_score', methods=['POST']) def edit_hot_score(): - u = get_current_user() - if not is_admin(u.name): - print(u.name) + username = get_current_username() + if not is_admin(username): abort(403) pid = request.form.get('pid', type=int) diff --git a/models.py b/models.py index e3adcf1..bae0a90 100644 --- a/models.py +++ b/models.py @@ -26,7 +26,8 @@ class Post(db.Model): deleted = db.Column(db.Boolean, default=False) is_reported = db.Column(db.Boolean, default=False) comment_timestamp = db.Column(db.Integer, default=0, index=True) - hot_score = db.Column(db.Integer, default=0, nullable=False, server_default="0") + hot_score = db.Column(db.Integer, default=0, + nullable=False, server_default="0") comments = db.relationship('Comment', backref='post', lazy=True) diff --git a/requirements.txt b/requirements.txt index 09a3901..34452d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -Flask>=1.1.2 -Flask-Limit>=1.0.2 -Flask-Limiter>=1.3.1 -Flask-Login>=0.5.0 -Flask-Migrate>=2.5.3 -Flask-SQLAlchemy>=2.4.4 -Mastodon.py>=1.5.1 +Flask +Flask-Limit +Flask-Limiter +Flask-Login +Flask-Migrate +Flask-SQLAlchemy +Mastodon.py +redis diff --git a/utils.py b/utils.py index 4cd21b8..020ce2e 100644 --- a/utils.py +++ b/utils.py @@ -2,6 +2,7 @@ import hashlib import time from flask import request, abort, current_app from models import User, Attention, Syslog +from config import ADMINS, ENABLE_TMP def get_config(key): @@ -9,7 +10,7 @@ def get_config(key): def is_admin(name): - return name in get_config('ADMINS') + return name in ADMINS def tmp_token(): @@ -18,27 +19,29 @@ def tmp_token(): )[5:21] -def get_current_user(): +def get_current_username(): token = request.headers.get('User-Token') or request.args.get('user_token') if not token: abort(401) - if len(token.split('_')) == 2 and get_config('ENABLE_TMP'): + if len(token.split('_')) == 2 and ENABLE_TMP: tt, suf = token.split('_') if tt != tmp_token(): abort(401) - return User(name='tmp_' + suf) + return 'tmp_' + suf u = User.query.filter_by(token=token).first() if not u or Syslog.query.filter_by( log_type='BANNED', name_hash=hash_name(u.name)).first(): abort(401) - return u + return u.name def hash_name(name): + print(name) return hashlib.sha256( - (get_config('SALT') + name).encode('utf-8')).hexdigest() + (get_config('SALT') + name).encode('utf-8') + ).hexdigest() def map_post(p, name, mc=50): @@ -81,10 +84,10 @@ def map_comment(p, name): ] -def map_syslog(s, u=None): +def map_syslog(s, username): return { 'type': s.log_type, - 'detail': s.log_detail if check_can_del(u.name, s.name_hash) else '', + 'detail': s.log_detail if check_can_del(username, s.name_hash) else '', 'user': look(s.name_hash), 'timestamp': s.timestamp } @@ -99,7 +102,7 @@ def check_attention(name, pid): def check_can_del(name, author_hash): - return int(hash_name(name) == author_hash or is_admin(name)) + return hash_name(name) == author_hash or is_admin(name) def look(s):