mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2025-01-26 07:05:13 +00:00
Redo nuke functionality (#459)
This started out as a simple rebase, but then I rebased the wrong branches and it all got confusing, so here it is as a new dank commit. We now have an @admin_only decorator, and we ask for confirmation before we nuke. We can also see the nuke button when users are banned, and nuking is a separate endpoint with a separate form. Additionally, it now uses the new tracker API.
This commit is contained in:
parent
81806d7bc9
commit
c405f49eb6
|
@ -284,7 +284,6 @@ class DeleteForm(FlaskForm):
|
||||||
class BanForm(FlaskForm):
|
class BanForm(FlaskForm):
|
||||||
ban_user = SubmitField("Delete & Ban and Ban User")
|
ban_user = SubmitField("Delete & Ban and Ban User")
|
||||||
ban_userip = SubmitField("Delete & Ban and Ban User+IP")
|
ban_userip = SubmitField("Delete & Ban and Ban User+IP")
|
||||||
nuke = SubmitField("Delete & Ban all torrents")
|
|
||||||
unban = SubmitField("Unban")
|
unban = SubmitField("Unban")
|
||||||
|
|
||||||
_validator = DataRequired()
|
_validator = DataRequired()
|
||||||
|
@ -299,6 +298,10 @@ class BanForm(FlaskForm):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class NukeForm(FlaskForm):
|
||||||
|
nuke_torrents = SubmitField("\U0001F4A3 Nuke Torrents")
|
||||||
|
|
||||||
|
|
||||||
class UploadForm(FlaskForm):
|
class UploadForm(FlaskForm):
|
||||||
torrent_file = FileField('Torrent file', [
|
torrent_file = FileField('Torrent file', [
|
||||||
FileRequired()
|
FileRequired()
|
||||||
|
|
|
@ -104,30 +104,34 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 text-left">
|
<div class="col-md-6 text-left">
|
||||||
{% if not user.is_banned %}
|
{% if not user.is_banned %}
|
||||||
{{ ban_form.ban_user(value="Ban User", class="btn btn-danger") }}
|
{{ ban_form.ban_user(value="Ban User", class="btn btn-danger") }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="btn btn-danger disabled">Already banned</button>
|
<button type="button" class="btn btn-danger disabled">Already banned</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 text-center">
|
<div class="col-md-6 text-right">
|
||||||
{% if not ipbanned %}
|
{% if not ipbanned %}
|
||||||
{{ ban_form.ban_userip(value="Ban User+IP", class="btn btn-danger") }}
|
{{ ban_form.ban_userip(value="Ban User+IP", class="btn btn-danger") }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="btn btn-danger disabled">Already IP banned</button>
|
<button type="button" class="btn btn-danger disabled">Already IP banned</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
|
||||||
{% if g.user.is_superadmin %}
|
|
||||||
{{ ban_form.nuke(value="\U0001F4A3 Nuke Torrents", class="btn btn-danger") }}
|
|
||||||
{% else %}
|
|
||||||
<button type="button" class="btn btn-danger disabled">💣 Nuke Torrents</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
|
{% if g.user.is_superadmin %}
|
||||||
|
<hr>
|
||||||
|
<form method="POST" onsubmit="return prompt('Please type {{ user.username }} to confirm').toLowerCase() == '{{ user.username }}'.toLowerCase()">
|
||||||
|
{{ nuke_form.csrf_token }}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 text-left">
|
||||||
|
{{ nuke_form.nuke_torrents(class="btn btn-danger", formaction=url_for('users.nuke_user_torrents', user_name=user.username)) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,8 @@ import random
|
||||||
import string
|
import string
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import flask
|
||||||
|
|
||||||
|
|
||||||
def sha1_hash(input_bytes):
|
def sha1_hash(input_bytes):
|
||||||
""" Hash given bytes with hashlib.sha1 and return the digest (as bytes) """
|
""" Hash given bytes with hashlib.sha1 and return the digest (as bytes) """
|
||||||
|
@ -78,3 +80,13 @@ def chain_get(source, *args):
|
||||||
if value is not sentinel:
|
if value is not sentinel:
|
||||||
return value
|
return value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def admin_only(f):
|
||||||
|
@functools.wraps(f)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if flask.g.user and flask.g.user.is_superadmin:
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
flask.abort(401)
|
||||||
|
return wrapper
|
||||||
|
|
|
@ -13,7 +13,7 @@ from nyaa import forms, models
|
||||||
from nyaa.extensions import db
|
from nyaa.extensions import db
|
||||||
from nyaa.search import (DEFAULT_MAX_SEARCH_RESULT, DEFAULT_PER_PAGE, SERACH_PAGINATE_DISPLAY_MSG,
|
from nyaa.search import (DEFAULT_MAX_SEARCH_RESULT, DEFAULT_PER_PAGE, SERACH_PAGINATE_DISPLAY_MSG,
|
||||||
_generate_query_string, search_db, search_elastic)
|
_generate_query_string, search_db, search_elastic)
|
||||||
from nyaa.utils import chain_get, sha1_hash
|
from nyaa.utils import admin_only, chain_get, sha1_hash
|
||||||
|
|
||||||
app = flask.current_app
|
app = flask.current_app
|
||||||
bp = flask.Blueprint('users', __name__)
|
bp = flask.Blueprint('users', __name__)
|
||||||
|
@ -30,6 +30,7 @@ def view_user(user_name):
|
||||||
ban_form = None
|
ban_form = None
|
||||||
bans = None
|
bans = None
|
||||||
ipbanned = None
|
ipbanned = None
|
||||||
|
nuke_form = None
|
||||||
if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level:
|
if flask.g.user and flask.g.user.is_moderator and flask.g.user.level > user.level:
|
||||||
admin_form = forms.UserForm()
|
admin_form = forms.UserForm()
|
||||||
default, admin_form.user_class.choices = _create_user_class_choices(user)
|
default, admin_form.user_class.choices = _create_user_class_choices(user)
|
||||||
|
@ -37,6 +38,7 @@ def view_user(user_name):
|
||||||
admin_form.user_class.data = default
|
admin_form.user_class.data = default
|
||||||
|
|
||||||
ban_form = forms.BanForm()
|
ban_form = forms.BanForm()
|
||||||
|
nuke_form = forms.NukeForm()
|
||||||
if flask.request.method == 'POST':
|
if flask.request.method == 'POST':
|
||||||
doban = (ban_form.ban_user.data or ban_form.unban.data or ban_form.ban_userip.data)
|
doban = (ban_form.ban_user.data or ban_form.unban.data or ban_form.ban_userip.data)
|
||||||
bans = models.Ban.banned(user.id, user.last_login_ip).all()
|
bans = models.Ban.banned(user.id, user.last_login_ip).all()
|
||||||
|
@ -103,38 +105,6 @@ def view_user(user_name):
|
||||||
flask.flash(flask.Markup('User has been successfully {0}.'.format(action)), 'success')
|
flask.flash(flask.Markup('User has been successfully {0}.'.format(action)), 'success')
|
||||||
return flask.redirect(url)
|
return flask.redirect(url)
|
||||||
|
|
||||||
if flask.request.method == 'POST' and ban_form and ban_form.nuke.data:
|
|
||||||
if flask.g.user.is_superadmin:
|
|
||||||
nyaa_banned = 0
|
|
||||||
sukebei_banned = 0
|
|
||||||
for t in chain(user.nyaa_torrents, user.sukebei_torrents):
|
|
||||||
t.deleted = True
|
|
||||||
t.banned = True
|
|
||||||
db.session.add(t)
|
|
||||||
if isinstance(t, models.NyaaTorrent):
|
|
||||||
db.session.add(models.NyaaTrackerApi(t.info_hash, 'remove'))
|
|
||||||
nyaa_banned += 1
|
|
||||||
else:
|
|
||||||
db.session.add(models.SukebeiTrackerApi(t.info_hash, 'remove'))
|
|
||||||
sukebei_banned += 1
|
|
||||||
|
|
||||||
for log_flavour, num in ((models.NyaaAdminLog, nyaa_banned),
|
|
||||||
(models.SukebeiAdminLog, sukebei_banned)):
|
|
||||||
if num > 0:
|
|
||||||
log = "Nuked {0} torrents of [{1}]({2})".format(num,
|
|
||||||
user.username,
|
|
||||||
url)
|
|
||||||
adminlog = log_flavour(log=log, admin_id=flask.g.user.id)
|
|
||||||
db.session.add(adminlog)
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
flask.flash('Torrents of {0} have been nuked.'.format(user.username),
|
|
||||||
'success')
|
|
||||||
return flask.redirect(url)
|
|
||||||
else:
|
|
||||||
flask.flash('Insufficient permissions to nuke.', 'danger')
|
|
||||||
return flask.redirect(url)
|
|
||||||
|
|
||||||
req_args = flask.request.args
|
req_args = flask.request.args
|
||||||
|
|
||||||
search_term = chain_get(req_args, 'q', 'term')
|
search_term = chain_get(req_args, 'q', 'term')
|
||||||
|
@ -219,6 +189,7 @@ def view_user(user_name):
|
||||||
rss_filter=rss_query_string,
|
rss_filter=rss_query_string,
|
||||||
admin_form=admin_form,
|
admin_form=admin_form,
|
||||||
ban_form=ban_form,
|
ban_form=ban_form,
|
||||||
|
nuke_form=nuke_form,
|
||||||
bans=bans,
|
bans=bans,
|
||||||
ipbanned=ipbanned)
|
ipbanned=ipbanned)
|
||||||
|
|
||||||
|
@ -283,6 +254,45 @@ def activate_user(payload):
|
||||||
return flask.redirect(flask.url_for('main.home'))
|
return flask.redirect(flask.url_for('main.home'))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/user/<user_name>/nuke/torrents', methods=['POST'])
|
||||||
|
@admin_only
|
||||||
|
def nuke_user_torrents(user_name):
|
||||||
|
user = models.User.by_username(user_name)
|
||||||
|
if not user:
|
||||||
|
flask.abort(404)
|
||||||
|
|
||||||
|
nuke_form = forms.NukeForm(flask.request.form)
|
||||||
|
if not nuke_form.validate():
|
||||||
|
flask.abort(401)
|
||||||
|
url = flask.url_for('users.view_user', user_name=user.username)
|
||||||
|
nyaa_banned = 0
|
||||||
|
sukebei_banned = 0
|
||||||
|
for t in chain(user.nyaa_torrents, user.sukebei_torrents):
|
||||||
|
t.deleted = True
|
||||||
|
t.banned = True
|
||||||
|
db.session.add(t)
|
||||||
|
if isinstance(t, models.NyaaTorrent):
|
||||||
|
db.session.add(models.NyaaTrackerApi(t.info_hash, 'remove'))
|
||||||
|
nyaa_banned += 1
|
||||||
|
else:
|
||||||
|
db.session.add(models.SukebeiTrackerApi(t.info_hash, 'remove'))
|
||||||
|
sukebei_banned += 1
|
||||||
|
|
||||||
|
for log_flavour, num in ((models.NyaaAdminLog, nyaa_banned),
|
||||||
|
(models.SukebeiAdminLog, sukebei_banned)):
|
||||||
|
if num > 0:
|
||||||
|
log = "Nuked {0} torrents of [{1}]({2})".format(num,
|
||||||
|
user.username,
|
||||||
|
url)
|
||||||
|
adminlog = log_flavour(log=log, admin_id=flask.g.user.id)
|
||||||
|
db.session.add(adminlog)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
flask.flash('Torrents of {0} have been nuked.'.format(user.username),
|
||||||
|
'success')
|
||||||
|
return flask.redirect(url)
|
||||||
|
|
||||||
|
|
||||||
def _create_user_class_choices(user):
|
def _create_user_class_choices(user):
|
||||||
choices = [('regular', 'Regular')]
|
choices = [('regular', 'Regular')]
|
||||||
default = 'regular'
|
default = 'regular'
|
||||||
|
|
Loading…
Reference in a new issue