diff --git a/nerochan/cli.py b/nerochan/cli.py index 537241d..8c8ac61 100644 --- a/nerochan/cli.py +++ b/nerochan/cli.py @@ -18,6 +18,28 @@ def cli(app): model = peewee.Model.__subclasses__() db.create_tables(model) + @click.argument('handle') + @app.cli.command('add_admin') + def add_admin(handle): + user = User.select().where(User.handle == handle).first() + if not user: + click.echo('user does not exist') + return False + user.is_admin = True + user.save() + click.echo(f'{handle} is now an admin') + + @click.argument('handle') + @app.cli.command('remove_admin') + def remove_admin(handle): + user = User.select().where(User.handle == handle).first() + if not user: + click.echo('user does not exist') + return False + user.is_admin = False + user.save() + click.echo(f'{handle} is no longer an admin') + @app.cli.command('verify_tips') def verify_tips(): txes = Transaction.select().where(Transaction.verified == False) diff --git a/nerochan/decorators.py b/nerochan/decorators.py index 409128e..281d097 100644 --- a/nerochan/decorators.py +++ b/nerochan/decorators.py @@ -1,25 +1,18 @@ -from flask import session, redirect, url_for, flash +from flask import redirect, flash, request, url_for from flask_login import current_user from functools import wraps -from nerochan.models import User, CreatorProfile, BackerProfile, Subscription +from nerochan.models import User - -# def login_required(f): -# @wraps(f) -# def decorated_function(*args, **kwargs): -# if "auth" not in session or not session["auth"]: -# return redirect(url_for("auth.login")) -# return f(*args, **kwargs) -# return decorated_function - -def subscription_required(f): +def admin_required(f): @wraps(f) def decorated_function(*args, **kwargs): - print(current_user) - # m = Moderator.filter(username=session["auth"]["preferred_username"]) - # if m: - # return f(*args, **kwargs) - # else: - # flash("You are not a moderator") - # return redirect(url_for("index")) + if current_user.is_admin: + return f(*args, **kwargs) + else: + flash('Must be an admin to access that page.', 'warning') + if request.referrer: + u = request.referrer + else: + u = url_for('main.index') + return redirect(u) return decorated_function diff --git a/nerochan/factory.py b/nerochan/factory.py index e10ca08..0056c46 100644 --- a/nerochan/factory.py +++ b/nerochan/factory.py @@ -28,13 +28,14 @@ def create_app(): return User.get_or_none(user_id) with app.app_context(): - from nerochan.routes import api, auth, main, artwork, user + from nerochan.routes import api, auth, main, artwork, user, admin from nerochan import filters app.register_blueprint(main.bp) app.register_blueprint(api.bp) app.register_blueprint(auth.bp) app.register_blueprint(artwork.bp) app.register_blueprint(user.bp) + app.register_blueprint(admin.bp) app.register_blueprint(filters.bp) return app diff --git a/nerochan/forms.py b/nerochan/forms.py index eef07bd..01cd418 100644 --- a/nerochan/forms.py +++ b/nerochan/forms.py @@ -3,6 +3,7 @@ from wtforms import StringField, FloatField from wtforms.validators import DataRequired, ValidationError from monero.address import address +from nerochan.models import User from nerochan import config @@ -10,7 +11,6 @@ def is_valid_xmr_address(form, field): try: # Ensure the provided address is valid address/subaddress/integrated address a = address(field.data) - print(config.XMR_WALLET_NETWORK) # Ensure the provided address matches the network that the application's wallet is using if not config.XMR_WALLET_NETWORK.startswith(a.net): raise ValidationError('Provided Monero address does not match the configured network. Application: {}. Provided: {}'.format( @@ -19,14 +19,23 @@ def is_valid_xmr_address(form, field): except ValueError: raise ValidationError('Invalid Monero address provided') +def is_valid_user(form, field): + try: + u = User.select().where(User.handle == field.data).first() + if not u: + raise ValidationError('User does not exist') + return True + except ValueError: + raise ValidationError('Error looking up user') + class UserRegistration(FlaskForm): handle = StringField('Handle:', validators=[DataRequired()], render_kw={'placeholder': 'online handle', 'class': 'u-full-width', 'type': 'text'}) wallet_address = StringField('Wallet Address:', validators=[DataRequired(), is_valid_xmr_address], render_kw={'placeholder': 'monero wallet address', 'class': 'u-full-width', 'type': 'text'}) -class UserLogin(FlaskForm): - handle = StringField('Handle:', validators=[DataRequired()], render_kw={'placeholder': 'online handle', 'class': 'u-full-width', 'type': 'text'}) +class UserForm(FlaskForm): + handle = StringField('Handle:', validators=[DataRequired(), is_valid_user], render_kw={'placeholder': 'handle', 'class': 'u-full-width', 'type': 'text'}) class UserChallenge(FlaskForm): diff --git a/nerochan/routes/admin.py b/nerochan/routes/admin.py index 29da16a..e8b46cf 100644 --- a/nerochan/routes/admin.py +++ b/nerochan/routes/admin.py @@ -1,20 +1,37 @@ -from flask import Blueprint, render_template, redirect, url_for, flash -from flask_login import login_required +from flask import Blueprint, render_template, redirect, request, flash, url_for +from flask_login import login_required, current_user +from nerochan.forms import UserForm +from nerochan.decorators import admin_required from nerochan.models import User bp = Blueprint('admin', 'admin', url_prefix='/admin') -@bp.route('/') -def main(handle: str): - user = User.select().where(User.handle == handle).first() - if not user: - flash('That user does not exist.', 'warning') - return redirect(url_for('main.index')) +@bp.route('', methods=['GET', 'POST']) +@login_required +@admin_required +def main(): + user_form = UserForm() + if user_form.validate_on_submit(): + u = User.select().where(User.handle == user_form.handle.data).first() + u.is_admin = True + u.save() + return redirect(request.referrer) + if request.args.get('remove'): + a = User.select().where(User.handle == request.args.get('remove')).first() + if a == current_user: + flash('cannot delete yourself') + return redirect(url_for('admin.main')) + if a: + a.is_admin = False + a.save() + return redirect(url_for('admin.main')) + admins = User.select().where(User.is_admin == True).order_by(User.register_date.desc()) return render_template( - 'user/show.html', - user=user + 'admin/main.html', + admins=admins, + user_form=user_form ) # approve artwork @@ -22,6 +39,4 @@ def main(handle: str): # allow user # hide artwork # promote mod -# demote mod -# promote admin -# demote admin \ No newline at end of file +# demote mod \ No newline at end of file diff --git a/nerochan/routes/auth.py b/nerochan/routes/auth.py index 51e9e2c..d1ec624 100644 --- a/nerochan/routes/auth.py +++ b/nerochan/routes/auth.py @@ -2,7 +2,7 @@ from flask import Blueprint, render_template from flask import flash, redirect, url_for from flask_login import login_user, logout_user, current_user -from nerochan.forms import UserLogin, UserRegistration, UserChallenge +from nerochan.forms import UserForm, UserRegistration, UserChallenge from nerochan.helpers import make_wallet_rpc from nerochan.models import User @@ -38,7 +38,7 @@ def register(): @bp.route("/login", methods=["GET", "POST"]) def login(): - form = UserLogin() + form = UserForm() if current_user.is_authenticated: flash('Already logged in.') return redirect(url_for('main.index')) diff --git a/nerochan/static/css/main.css b/nerochan/static/css/main.css index 785b3b6..38c4b8a 100644 --- a/nerochan/static/css/main.css +++ b/nerochan/static/css/main.css @@ -8,6 +8,14 @@ a, a:visited { color: white; } +.ml-2 { + margin-left: 2em; +} + +ul { + list-style: circle; +} + .container-wide { max-width: 90%; margin-left: 3em; diff --git a/nerochan/templates/admin/main.html b/nerochan/templates/admin/main.html new file mode 100644 index 0000000..0b87df7 --- /dev/null +++ b/nerochan/templates/admin/main.html @@ -0,0 +1,33 @@ +{% extends 'includes/base.html' %} + +{% block content %} + +
Add a homie
+ +