|
|
@ -7,7 +7,7 @@ |
|
|
|
# create or refresh the database |
|
|
|
# $ python3 app.py |
|
|
|
|
|
|
|
from flask import Flask, request, render_template, send_from_directory, abort, redirect, session, url_for |
|
|
|
from flask import Flask, request, render_template, send_from_directory, abort, redirect, session, url_for, send_file |
|
|
|
from flask_sqlalchemy import SQLAlchemy |
|
|
|
from flask_limiter import Limiter |
|
|
|
from flask_limiter.util import get_remote_address |
|
|
@ -18,8 +18,10 @@ from mastodon import Mastodon |
|
|
|
from datetime import date, datetime |
|
|
|
from functools import wraps |
|
|
|
import hashlib |
|
|
|
from zipfile import ZipFile |
|
|
|
import random |
|
|
|
import os |
|
|
|
import re |
|
|
|
from config import C |
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
@ -56,6 +58,9 @@ class Paper(db.Model): |
|
|
|
down_num = db.Column(db.Integer, index=True, default=0) |
|
|
|
file_hash = db.Column(db.String(64)) |
|
|
|
|
|
|
|
# always increment 1 for the id of a new record |
|
|
|
__table_args__ = { 'sqlite_autoincrement': True } |
|
|
|
|
|
|
|
def is_downloaded(self): |
|
|
|
return bool(DownloadRelation.query.filter_by(paper_id=self.id, username=session.get('username')).count()) |
|
|
|
|
|
|
@ -94,12 +99,17 @@ def login_required(allow_guest=True): |
|
|
|
def login(): |
|
|
|
return app.send_static_file('login/index.html') |
|
|
|
|
|
|
|
@app.route('/pastExam/logout') |
|
|
|
def logout(): |
|
|
|
session.pop('username', None) |
|
|
|
return redirect('login') |
|
|
|
|
|
|
|
@app.route('/pastExam/login/guest/') |
|
|
|
def guest_login(): |
|
|
|
return render_template('guest-login.html', vs=C.verify, allow_guest_upload=C.allow_guest_upload) |
|
|
|
|
|
|
|
@app.route('/pastExam/login/guest/verify', methods=['POST']) |
|
|
|
@limiter.limit("10 / hour") |
|
|
|
@limiter.limit("5 / hour") |
|
|
|
def guest_login_verify(): |
|
|
|
for name, ques, hint, ans in C.verify: |
|
|
|
if request.form.get(name) != ans: |
|
|
@ -109,7 +119,7 @@ def guest_login_verify(): |
|
|
|
session['uid'] = random.randint(0, 10000000) |
|
|
|
|
|
|
|
session['username'] = 'guest<%s>' % session['uid'] |
|
|
|
session['avatar'] = None |
|
|
|
session.pop('avatar', None) |
|
|
|
session.permanent = True |
|
|
|
return {'r':0} |
|
|
|
|
|
|
@ -136,7 +146,7 @@ def mast_login_auth(): |
|
|
|
@app.route('/pastExam/') |
|
|
|
@login_required() |
|
|
|
def list(username): |
|
|
|
avatar = session.get('avatar') or C.guest_avatar |
|
|
|
avatar = session.get('avatar', C.guest_avatar) |
|
|
|
course = request.args.get('course') |
|
|
|
teacher = request.args.get('teacher') |
|
|
|
year = request.args.get('year') |
|
|
@ -226,6 +236,7 @@ def upload(username): |
|
|
|
return redirect('.#part2') |
|
|
|
|
|
|
|
@app.route('/pastExam/<pid>/download') |
|
|
|
@limiter.limit("100 / hour") |
|
|
|
@login_required() |
|
|
|
def download(pid, username): |
|
|
|
p = Paper.query.get_or_404(pid) |
|
|
@ -238,9 +249,22 @@ def download(pid, username): |
|
|
|
p.down_num += 1 |
|
|
|
db.session.commit() |
|
|
|
|
|
|
|
if request.args.get('type') == 'zip': |
|
|
|
target_file = '/tmp/%s.zip' % pid |
|
|
|
|
|
|
|
if not os.path.exists(target_file): |
|
|
|
ipfs_client.get(p.file_hash, target='/tmp') |
|
|
|
with ZipFile(target_file, 'w') as z: |
|
|
|
for fname in os.listdir(os.path.join('/tmp', p.file_hash)): |
|
|
|
z.write(os.path.join('/tmp', p.file_hash, fname), fname) |
|
|
|
|
|
|
|
filename = re.sub('[^\w@_()()-]', '_', '%s_%s_共享计划_%d' %(p.course, p.teacher, p.id)) + '.zip' |
|
|
|
return send_file(target_file, as_attachment=True, attachment_filename=filename) |
|
|
|
|
|
|
|
return redirect(C.ipfs_base_url + p.file_hash, code=301) # 301减少不必要的请求 |
|
|
|
|
|
|
|
@app.route('/pastExam/<pid>/like', methods=['POST', 'DELETE']) |
|
|
|
@limiter.limit("100 / hour") |
|
|
|
@login_required() |
|
|
|
def like(pid, username): |
|
|
|
p = Paper.query.get_or_404(pid) |
|
|
|