updating routes for the new models

migrate-new-models
lza_menace 2 years ago
parent b635e39e04
commit da874c3994

@ -23,73 +23,73 @@ def init():
db.create_tables([User, Post, AuditEvent, TipSent, TipReceived, Vote])
@bp.cli.command("post_reddit")
@click.argument('last_hours')
def post_reddit(last_hours):
posts = Post.select().where(
Post.approved==True,
Post.to_reddit==False
).order_by(Post.timestamp.asc())
for p in posts:
if p.hours_elapsed() < int(last_hours):
if not p.to_reddit:
_p = make_post(p)
if _p:
p.to_reddit = True
p.save()
return
@bp.cli.command("create_accounts")
def create_accounts():
wallet = wownero.Wallet()
for post in Post.select():
if post.account_index not in wallet.accounts():
account = wallet.new_account()
print(f"Created account {account}")
@bp.cli.command("payout_users")
def payout_users():
wallet = wownero.Wallet()
_fa = wownero.from_atomic
_aw = wownero.as_wownero
for post in Post.select():
try:
submitter = Profile.get(username=post.submitter)
balances = wallet.balances(post.account_index)
url = url_for('post.read', id=post.id, _external=True)
if balances[1] > 0.05:
print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
print(sweep)
if "tx_hash_list" in sweep:
amount = 0
for amt in sweep["amount_list"]:
amount += int(amt)
except Exception as e:
print(f"Failed because: {e}")
@bp.cli.command("show")
@click.argument("post_id")
def post_id(post_id):
p = Post.filter(id=post_id).first()
if p:
print(p.show())
else:
print("That post doesn't exist")
@bp.cli.command("load_cache")
def load_cache():
current_app.logger.info('loading top posters into cache')
get_top_posters()
current_app.logger.info('done')
current_app.logger.info('loading latest tipped into cache')
get_latest_tipped_posts()
current_app.logger.info('done')
for i in [1, 3, 7, 30, 9999]:
current_app.logger.info(f'loading top posts last {i} days into cache')
get_top_posts(i)
current_app.logger.info('done')
# @bp.cli.command("post_reddit")
# @click.argument('last_hours')
# def post_reddit(last_hours):
# posts = Post.select().where(
# Post.approved==True,
# Post.to_reddit==False
# ).order_by(Post.timestamp.asc())
# for p in posts:
# if p.hours_elapsed() < int(last_hours):
# if not p.to_reddit:
# _p = make_post(p)
# if _p:
# p.to_reddit = True
# p.save()
# return
# @bp.cli.command("create_accounts")
# def create_accounts():
# wallet = wownero.Wallet()
# for post in Post.select():
# if post.account_index not in wallet.accounts():
# account = wallet.new_account()
# print(f"Created account {account}")
# @bp.cli.command("payout_users")
# def payout_users():
# wallet = wownero.Wallet()
# _fa = wownero.from_atomic
# _aw = wownero.as_wownero
# for post in Post.select():
# try:
# submitter = Profile.get(username=post.submitter)
# balances = wallet.balances(post.account_index)
# url = url_for('post.read', id=post.id, _external=True)
# if balances[1] > 0.05:
# print(f"Post #{post.id} has {balances[1]} funds unlocked and ready to send. Sweeping all funds to user's address ({submitter.address}).")
# sweep = wallet.sweep_all(account=post.account_index, dest_address=submitter.address)
# print(sweep)
# if "tx_hash_list" in sweep:
# amount = 0
# for amt in sweep["amount_list"]:
# amount += int(amt)
# except Exception as e:
# print(f"Failed because: {e}")
# @bp.cli.command("show")
# @click.argument("post_id")
# def post_id(post_id):
# p = Post.filter(id=post_id).first()
# if p:
# print(p.show())
# else:
# print("That post doesn't exist")
# @bp.cli.command("load_cache")
# def load_cache():
# current_app.logger.info('loading top posters into cache')
# get_top_posters()
# current_app.logger.info('done')
# current_app.logger.info('loading latest tipped into cache')
# get_latest_tipped_posts()
# current_app.logger.info('done')
# for i in [1, 3, 7, 30, 9999]:
# current_app.logger.info(f'loading top posts last {i} days into cache')
# get_top_posts(i)
# current_app.logger.info('done')

@ -21,6 +21,7 @@ WALLET_PROTO = getenv('WALLET_PROTO', 'http') #
WALLET_RPC_USER = getenv('WALLET_RPC_USER', 'suchwow') #
WALLET_RPC_PASS = getenv('WALLET_RPC_PASS', 'suchwow') #
WALLET_PASS = getenv('WALLET_PASS', 'zzzzzzz') # You specify all these wallet details in .env
WALLET_ACCOUNT = getenv('WALLET_ACCOUNT', 0)
# Optional for posting to Reddit
PRAW_CLIENT_SECRET = getenv('PRAW_CLIENT_SECRET', None)

@ -2,6 +2,8 @@ from flask import Blueprint
from arrow import get as arrow_get
from suchwow.models import Moderator
from suchwow.wownero import from_atomic as _from_atomic
from suchwow.wownero import as_wownero
bp = Blueprint('filters', 'filters')
@ -16,6 +18,10 @@ def shorten_address(a):
def humanize(d):
return arrow_get(d).humanize()
@bp.app_template_filter('from_atomic')
def from_atomic(a):
return as_wownero(_from_atomic(a))
@bp.app_template_filter('is_moderator')
def is_moderator(s):
m = Moderator.select().where(Moderator.username == s)

@ -2,7 +2,7 @@ from math import ceil
from flask import Blueprint, request, render_template, flash
from suchwow.models import Post, Profile, Moderator
from suchwow._models import Post, User
from suchwow.utils.helpers import get_latest_tipped_posts
bp = Blueprint('main', 'main')
@ -31,7 +31,11 @@ def index():
posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc())
if submitter:
posts = posts.where(Post.submitter==submitter)
user = Post.select().where(Post.username == submitter).first()
if not user:
flash('That user does not exist!', 'is-warning')
else:
posts = posts.where(Post.user == user)
paginated_posts = posts.paginate(page, itp)
total_pages = ceil(posts.count() / itp)
@ -46,5 +50,5 @@ def index():
@bp.route("/about")
def about():
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
mods = User.select().where(User.moderator == True)
return render_template("about.html", mods=mods)

@ -1,6 +1,6 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request
from suchwow.models import AuditEvent, Post, Profile, Moderator, Ban, get_ban_reason
from suchwow._models import AuditEvent, Post, User, get_ban_reason
from suchwow.utils.decorators import moderator_required
from suchwow.utils.helpers import get_session_user, audit_event
from suchwow import config
@ -13,8 +13,8 @@ bp = Blueprint("mod", "mod")
def main():
live_posts = Post.select().where(Post.approved == True).count()
pending_posts = Post.select().where(Post.approved == False).count()
active_posters = Profile.select().join(Post, on=Post.submitter == Profile.username).distinct().count()
mods = Moderator.select().count()
active_posters = User.select().join(Post, on=Post.user).distinct().count()
mods = User.select().where(User.moderator == True).count()
return render_template(
'mod/main.html',
live_posts=live_posts,
@ -37,73 +37,76 @@ def pending_posts():
@bp.route('/mods/manage', methods=['GET', 'POST'])
@moderator_required
def manage_mods():
to_delete = request.args.get('delete')
if to_delete:
m = Moderator.select().where(Moderator.username == to_delete).first()
if not m:
flash('No moderator exists with that name', 'is-danger')
elif m.username == get_session_user():
to_remove = request.args.get('delete')
if to_remove:
u = User.select().where(User.username == to_remove).first()
if not u.moderator:
flash('That user is not a moderator', 'is-danger')
elif u.username == get_session_user():
flash('Cannot remove yourself.', 'is-danger')
elif m.username == config.SUPER_ADMIN:
elif u.username == config.SUPER_ADMIN:
flash('Cannot delete super admin you son-of-a-bitch.', 'is-danger')
else:
m.delete_instance()
audit_event(f'Deleted {to_delete} from mods')
flash(f'Removed {to_delete} from mods!', 'is-success')
u.moderator = False
u.save()
audit_event(f'Removed {to_remove} from mods')
flash(f'Removed {to_remove} from mods!', 'is-success')
return redirect(url_for('mod.manage_mods'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
u = User.select().where(User.username == to_add).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Moderator.select().where(Moderator.username == to_add).first():
flash('That user does not appear to exist', 'is-danger')
elif u.moderator:
flash(f'{to_add} is already a mod, ya dingus.', 'is-warning')
else:
m = Moderator(username=to_add)
m.save()
u.moderator = True
u.save()
audit_event(f'Added {to_add} to mods')
flash(f'Added {to_add} to mods!', 'is-success')
mods = Profile.select().join(Moderator, on=(Profile.username == Moderator.username))
mods = User.select().where(User.moderator == True)
return render_template('mod/manage.html', mods=mods)
@bp.route('/mods/bans', methods=['GET', 'POST'])
@moderator_required
def manage_bans():
to_delete = request.args.get('delete')
if to_delete:
ban = Ban.select().join(Profile).where(Profile.username == to_delete).first()
if not ban:
to_unban = request.args.get('delete')
if to_unban:
u = User.select().where(User.username == to_unban).first()
if not u.banned:
flash('No ban exists for that user', 'is-danger')
elif ban.user == get_session_user():
elif u.username == get_session_user():
flash('Cannot ban yourself.', 'is-danger')
elif ban.user == config.SUPER_ADMIN:
elif u.username == config.SUPER_ADMIN:
flash('Cannot ban super admin you son-of-a-bitch.', 'is-danger')
else:
ban.delete_instance()
audit_event(f'Removed ban on {to_delete}')
flash(f'Unbanned {to_delete}!', 'is-success')
u.banned = False
u.save()
audit_event(f'Removed ban on {to_unban}')
flash(f'Unbanned {to_unban}!', 'is-success')
return redirect(url_for('mod.manage_bans'))
if request.method == 'POST':
to_add = request.form.get('username', None)
if to_add:
u = Profile.select().where(Profile.username == to_add).first()
to_ban = request.form.get('username', None)
if to_ban:
u = User.select().where(User.username == to_ban).first()
if not u:
flash('That user does not appear to exist (no profile setup yet)', 'is-danger')
elif Ban.select().join(Profile).where(Profile.username == to_add).first():
flash(f'{to_add} is already banned, ya dingus.', 'is-warning')
elif to_add == config.SUPER_ADMIN:
flash('That user does not appear to exist', 'is-danger')
elif u.banned:
flash(f'{to_ban} is already banned, ya dingus.', 'is-warning')
elif u.username == config.SUPER_ADMIN:
flash('Cannot ban the super admin you son-of-a-bitch.', 'is-danger')
else:
reason = request.form.get('reason')
if not reason:
reason = get_ban_reason()
ban = Ban(user=u, reason=reason)
ban.save()
audit_event(f'Banned {to_add} ({reason})')
flash(f'Banned {to_add}!', 'is-success')
bans = Ban.select()
u.banned = True
u.ban_reason = reason
u.save()
audit_event(f'Banned {to_ban} ({reason})')
flash(f'Banned {to_ban}!', 'is-success')
bans = User.select().where(User.banned == True)
return render_template('mod/bans.html', bans=bans)

@ -8,9 +8,9 @@ from flask import render_template, Blueprint, request, flash
from flask import send_from_directory, redirect, url_for, current_app
from werkzeug.utils import secure_filename
from suchwow import wownero
from suchwow.models import Post, Profile, Comment, Ban
from suchwow.utils.decorators import login_required, profile_required, moderator_required
from suchwow import wownero, config
from suchwow._models import User, Post, TipReceived
from suchwow.utils.decorators import login_required, address_required, moderator_required
from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user
from suchwow.utils.helpers import audit_event
from suchwow.discord import post_discord_webhook
@ -22,28 +22,21 @@ bp = Blueprint("post", "post")
def read(id):
_address_qr = BytesIO()
qr_code = None
if Post.filter(id=id):
wallet = wownero.Wallet()
post = Post.get(id=id)
post = Post.select().where(Post.id == id).first()
if post:
if not post.approved:
if not is_moderator(get_session_user()):
flash("That post has not been approved.", "is-warning")
return redirect("/")
if wallet.connected:
address = wallet.get_address(account=post.account_index)
transfers = wallet.transfers(account=post.account_index)
qr_uri = f'wownero:{address}?tx_description=suchwow%20post%20{post.id}'
address_qr = qrcode_make(qr_uri).save(_address_qr)
qr_code = b64encode(_address_qr.getvalue()).decode()
else:
address = "?"
transfers = "?"
qr_uri = f'wownero:{post.address}?tx_description=suchwow%20post%20{post.id}'
qrcode_make(qr_uri).save(_address_qr)
qr_code = b64encode(_address_qr.getvalue()).decode()
tips = TipReceived.select().where(TipReceived.post == post).order_by(TipReceived.timestamp.desc())
return render_template(
"post/read.html",
post=post,
address=address,
transfers=transfers,
qr_code=qr_code
qr_code=qr_code,
tips=tips
)
else:
flash("No meme there, brah", "is-warning")
@ -51,13 +44,11 @@ def read(id):
@bp.route("/post/create", methods=["GET", "POST"])
@login_required
@profile_required
@address_required
def create():
submitter = get_session_user()
u = Profile.filter(username=submitter)
banned = Ban.filter(user=u).first()
if banned:
flash(f"You can't post: {banned.reason}", "is-danger")
u = User.select().where(User.username == get_session_user()).first()
if u.banned:
flash(f"You can't post: {u.ban_reason}", "is-danger")
return redirect("/")
if request.method == "POST":
post_title = request.form.get("title")
@ -82,37 +73,34 @@ def create():
save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads")
save_path = path.join(save_path_base, filename)
file.save(save_path)
try:
wallet_found = False
while wallet_found is False:
wallet = wownero.Wallet()
account_index = wallet.new_account()
in_use = Post.select().where(Post.account_index == account_index).first()
address_idx, address = wallet.new_address(config.WALLET_ACCOUNT)
in_use = Post.select().where(Post.address == address).first()
if in_use:
flash("Suchwow wallet is fucked up! Try again later.", "is-danger")
return redirect(request.url)
except:
flash("Suchwow wallet is fucked up! Try again later.", "is-danger")
return redirect(request.url)
post = Post(
title=post_title,
text=request.form.get("text", ""),
submitter=submitter,
image_name=filename,
account_index=account_index,
address_index=0
)
post.save()
post.save_thumbnail()
url = url_for('post.read', id=post.id, _external=True)
audit_event(f'Created new post {post.id}')
flash("New post created and pending approval!", "is-success")
return redirect(url_for("main.index"))
continue
wallet_found = True
post = Post(
title=post_title,
text=request.form.get("text", ""),
user=u,
image_name=filename,
account_index=config.WALLET_ACCOUNT,
address_index=address_idx,
address=address
)
post.save()
post.save_thumbnail()
audit_event(f'Created new post {post.id}')
flash("New post created and pending approval!", "is-success")
return redirect(url_for("main.index"))
return render_template("post/create.html")
@bp.route("/post/<id>/approve")
@moderator_required
def approve(id):
post = Post.get(id=id)
url = url_for('post.read', id=post.id, _external=True)
post = Post.select().where(Post.id == id).first()
if post:
if not post.approved:
post.approved = True
@ -129,12 +117,10 @@ def approve(id):
@bp.route("/post/<id>/delete")
@login_required
def delete(id):
filtered = Post.filter(id=id)
user = get_session_user()
is_mod = is_moderator(user)
if filtered:
post = filtered.first()
if user == post.submitter or is_mod:
post = Post.select().where(Post.id == id).first()
user = User.select().where(User.username == get_session_user()).first()
if post:
if user == post.user or user.moderator:
save_path_base = path.join(current_app.config["DATA_FOLDER"], "uploads")
save_path = path.join(save_path_base, post.image_name)
try:
@ -144,7 +130,7 @@ def delete(id):
audit_event(f'Deleted post {post.id}')
post.delete_instance()
flash("Deleted that shit, brah!", "is-success")
if is_mod:
if user.moderator:
return redirect(url_for("mod.pending_posts"))
else:
return redirect(url_for("main.index"))

@ -28,7 +28,7 @@
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" />
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.user.username }}" src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" />
{% endif %}
</a>
</div>

@ -8,11 +8,11 @@
{% for ban in bans %}
<article class="message" style="width: 30%; margin: 2em auto;">
<div class="message-header">
<p>{{ ban.user.username }}</p>
<a href="?delete={{ ban.user.username }}" class="delete"></a>
<p>{{ ban.username }}</p>
<a href="?delete={{ ban.username }}" class="delete"></a>
</div>
<div class="message-body">
{{ ban.reason }}
{{ ban.ban_reason }}
</div>
</article>
{% endfor %}

@ -12,9 +12,6 @@
<li class="is-active"><a href="#" aria-current="page">Post {{ post.id }}</a></li>
</ul>
</nav>
{% if post.hidden %}
<h2>You cannot see this post</h2>
{% else %}
<!-- Post Info -->
<section class="section">
<div class="content">
@ -24,15 +21,14 @@
<a href="{{ url_for('post.approve', id=post.id) }}" class="button is-success">Approve</a>
<a href="{{ url_for('post.delete', id=post.id) }}" class="button is-danger">Reject</a>
{% endif %}
<p class="mt-2">Submitted by <i><u><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></u></i> at <i>{{ post.timestamp }}</i></p>
<!-- <img src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" width=600/ style="border-radius:4px;"> -->
<p class="mt-2">Submitted by <i><u><a href="/?submitter={{ post.user.username }}">{{ post.user.username }}</a></u></i> at <i>{{ post.timestamp }}</i></p>
{% if post.get_image_path().endswith('mp4') %}
<video style="max-height: 100vh!important;" controls>
<source src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" type="video/mp4">
Your browser does not support the video tag.
</video>
{% else %}
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.submitter }}" src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" />
<img alt="SuchWow #{{ post.id }} - {{ post.title }} by {{ post.user.username }}" src="{{ url_for('post.uploaded_file', filename=post.image_name) }}" />
{% endif %}
</div>
</section>
@ -40,52 +36,30 @@
<!-- Payments -->
<section class="section content">
<h3>Payments</h3>
<p style="word-break:break-all;">Vote for this post by sending WOW to the following address:<br><i>{{ address }}</i></p>
<p style="word-break:break-all;">Vote for this post by sending WOW to the following address:<br><i>{{ post.address }}</i></p>
{% if qr_code %}
<img src="data:image/png;base64,{{ qr_code }}" width=180 class="center"><br /><br />
{% endif %}
<div class="columns">
<div class="column content">
<h4>WOW Received</h4>
{% if transfers.in %}
<h4>{{ tips | sum(attribute='amount') | from_atomic }} WOW Received</h4>
{% if tips %}
<ul>
{% for transfer in transfers.in %}
{% if transfer.amount > 0 %}
<li>
{{ transfer.amount / 100000000000 }} WOW
(<a href="https://wownero.club/transaction/{{ transfer.txid }}" target="_blank">{{ transfer.txid | shorten_address }}</a>)
- {{ transfer.timestamp | humanize }}
{% for tip in tips %}
<li class="is-small" style="font-size:.8em;">
{{ tip.amount | from_atomic }} WOW
(<a href="https://explore.wownero.com/tx/{{ tip.txid }}" target="_blank">{{ tip.txid | shorten_address }}</a>)
- {{ tip.timestamp | humanize }}
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<p>No WOW received yet. Show this post some love!</p>
{% endif %}
</div>
<div class="column content">
<h4>WOW Sent</h4>
{% if transfers.out %}
<ul>
{% for transfer in transfers.out %}
<li>
{{ transfer.amount / 100000000000 }} WOW
(<a href="https://wownero.club/transaction/{{ transfer.txid }}" target="_blank">{{ transfer.txid | shorten_address }}</a>)
- {{ transfer.timestamp | humanize }}
</li>
{% endfor %}
</ul>
{% else %}
<p>No payouts yet.</p>
{% endif %}
</div>
</div>
</section>
{% endif %}
{% if config.DEBUG %}
{{ post.show() }}
{% endif %}

@ -1,6 +1,6 @@
from flask import session, redirect, url_for, flash
from functools import wraps
from suchwow.models import Profile, Moderator
from suchwow._models import User
def login_required(f):
@ -16,19 +16,19 @@ def moderator_required(f):
def decorated_function(*args, **kwargs):
if "auth" not in session or not session["auth"]:
return redirect(url_for("auth.login"))
m = Moderator.filter(username=session["auth"]["preferred_username"])
if m:
u = User.select().where(User.username == session["auth"]["preferred_username"]).first()
if u.moderator:
return f(*args, **kwargs)
else:
flash("You are not a moderator", "is-warning")
return redirect(url_for("main.index"))
return decorated_function
def profile_required(f):
def address_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
un = session["auth"]["preferred_username"]
if not Profile.filter(username=un):
u = User.select().where(User.username == session["auth"]["preferred_username"]).first()
if not u.address:
url = "{}?redirect={}".format(
url_for("profile.edit"),
url_for("post.create")

@ -4,7 +4,8 @@ from datetime import datetime, timedelta
from flask import session
from suchwow.models import Moderator, Post, AuditEvent, Profile
from suchwow._models import AuditEvent, User
from suchwow.models import Moderator, Post, Profile
from suchwow.wownero import Wallet, from_atomic
from suchwow import config
@ -20,12 +21,12 @@ def is_moderator(username):
else:
return False
def get_profile():
p = Profile.filter(username=get_session_user()).first()
return p
def get_current_user():
u = User.select().where(User.username == get_session_user()).first()
return u
def audit_event(event):
e = AuditEvent(user=get_profile(), action=event)
e = AuditEvent(user=get_current_user(), action=event)
e.save()
def get_session_user():