setting up subscrption management for creators

revamp
lza_menace 2 years ago
parent 856490cf50
commit 3da8c9951d

@ -15,10 +15,9 @@ SERVER_NAME = getenv('SERVER_NAME', 'localhost:5000')
PLATFORM_WALLET = getenv('PLATFORM_WALLET') PLATFORM_WALLET = getenv('PLATFORM_WALLET')
# Constants - here for easy template references # Constants - here for easy template references
CREATOR_SUBSCRIPTION_TERM = 60 # term of how long creator subscriptions are valid for CREATOR_SUBSCRIPTION_TERM = 60 # days term of how long creator subscriptions are valid for until content is hidden
CREATOR_SUBSCRIPTION_GRACE = 10 # grace period after expiration of creator subscriptions until content is hidden CREATOR_SUBSCRIPTION_GRACE = 21 # days grace period after expiration of creator subscriptions until content is archived
CREATOR_SUBSCRIPTION_DELETE = 20 # time after grace period until content is deleted CREATOR_SUBSCRIPTION_FEE_XMR = .3 # XMR flat rate fee for creator subscriptions
CREATOR_SUBSCRIPTION_FEE_XMR = .15 # default flat rate fee in XMR for creator subscriptions
# Crypto RPC # Crypto RPC
XMR_WALLET_PASS = getenv('XMR_WALLET_PASS') XMR_WALLET_PASS = getenv('XMR_WALLET_PASS')

@ -1,7 +1,7 @@
from datetime import datetime from datetime import datetime
import arrow import arrow
import monero from monero import numbers
from quart import Blueprint, current_app from quart import Blueprint, current_app
@ -22,4 +22,4 @@ def xmr_block_explorer(v):
@bp.app_template_filter('from_atomic') @bp.app_template_filter('from_atomic')
def from_atomic(amt): def from_atomic(amt):
return monero.numbers.from_atomic(amt) return numbers.as_monero(numbers.from_atomic(amt))

@ -1,5 +1,5 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField from wtforms import StringField, FloatField
from wtforms.validators import DataRequired, ValidationError from wtforms.validators import DataRequired, ValidationError
from monero.address import address from monero.address import address
@ -35,3 +35,8 @@ class UserChallenge(FlaskForm):
class ConfirmSubscription(FlaskForm): class ConfirmSubscription(FlaskForm):
tx_id = StringField('TX ID:', validators=[DataRequired()], render_kw={"placeholder": "TX ID", "class": "form-control", "type": "text"}) tx_id = StringField('TX ID:', validators=[DataRequired()], render_kw={"placeholder": "TX ID", "class": "form-control", "type": "text"})
tx_key = StringField('TX Key:', validators=[DataRequired()], render_kw={"placeholder": "TX Key", "class": "form-control", "type": "text"}) tx_key = StringField('TX Key:', validators=[DataRequired()], render_kw={"placeholder": "TX Key", "class": "form-control", "type": "text"})
class CreateSubscription(FlaskForm):
price_xmr = FloatField('Price (XMR):', validators=[DataRequired()], render_kw={'placeholder': '.5', 'class': 'form-control', 'type': 'text'})
number_days = FloatField('Length (Days)', validators=[DataRequired()], render_kw={'placeholder': '30', 'class': 'form-control', 'type': 'text'})

@ -1,4 +1,4 @@
from datetime import datetime from datetime import datetime, timedelta
from enum import IntEnum, unique from enum import IntEnum, unique
from typing import List from typing import List
from secrets import token_urlsafe from secrets import token_urlsafe
@ -116,7 +116,29 @@ class CreatorSubscription(pw.Model):
atomic_xmr = pw.BigIntegerField() atomic_xmr = pw.BigIntegerField()
term_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_TERM * 24) term_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_TERM * 24)
grace_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_GRACE * 24) grace_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_GRACE * 24)
delete_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_DELETE * 24) delete_hours = pw.IntegerField(default=0) # delete me
@property
def grace_start_date(self):
# Hide content after this date if no new valid platform subscription
return self.create_date + timedelta(hours=self.term_hours)
@property
def delete_start_date(self):
# Archive content after this date if no new valid platform subscription
return self.grace_start_date + timedelta(hours=self.grace_hours)
@property
def hours_until_content_hidden(self):
return self.grace_start_date - datetime.utcnow()
@property
def hours_until_content_archived(self):
return self.delete_start_date - datetime.utcnow()
@property
def is_active(self):
return self.grace_start_date > datetime.utcnow()
class Meta: class Meta:
database = db database = db
@ -136,9 +158,10 @@ class SubscriptionMeta(pw.Model):
number_hours = pw.IntegerField() number_hours = pw.IntegerField()
wallet_address = pw.CharField() wallet_address = pw.CharField()
def get_end_date(self) -> datetime: def get_active_subscriptions(self):
# some timedelta shiz return Subscription.select().where(
pass Subscription.meta == self
)
class Meta: class Meta:
database = db database = db

@ -122,6 +122,43 @@ async def show(handle):
) )
@bp.route('/creator/subscriptions/manage', methods=['GET', 'POST'])
@login_required
async def manage_subscriptions():
form = forms.CreateSubscription()
if UserRole.creator not in current_user.roles:
await flash('You are not a creator!', 'warning')
return redirect(url_for('main.index'))
platform_subs = CreatorSubscription.select().where(
CreatorSubscription.user == current_user
).order_by(CreatorSubscription.create_date.desc())
content_subs = SubscriptionMeta.select().where(
SubscriptionMeta.user == current_user
).order_by(SubscriptionMeta.create_date.desc())
subscribers = Subscription.select().where(
Subscription.meta.in_(content_subs)
).order_by(Subscription.subscribe_date.desc())
if form.validate_on_submit():
s = SubscriptionMeta(
user=current_user,
atomic_xmr=to_atomic(form.price_xmr.data),
number_hours=form.number_days.data * 24.0,
wallet_address=current_user.wallet_address
)
s.save()
await flash('posting form data', 'success')
return await render_template(
'creator/manage_subscriptions.html',
platform_subs=platform_subs,
content_subs=content_subs,
subscribers=subscribers,
form=form
)
# @bp.route('/creator/<username>/subscription') # @bp.route('/creator/<username>/subscription')
# async def subscription(username): # async def subscription(username):
# user = User.select().where(User.username == username) # user = User.select().where(User.username == username)

@ -8,26 +8,23 @@ bp = Blueprint('main', 'main')
@bp.route('/') @bp.route('/')
async def index(): async def index():
feed = None feed = dict()
if current_user.is_authenticated:
new_creators = User.select().where( new_creators = User.select().where(
User.roles.contains_any(UserRole.creator) User.roles.contains_any(UserRole.creator)
).order_by(User.register_date.desc()).execute() ).order_by(User.register_date.desc()).execute()
feed['new_creators'] = new_creators
if current_user.is_authenticated:
active_subscriptions = Subscription.select().where( active_subscriptions = Subscription.select().where(
Subscription.active == True, Subscription.active == True,
Subscription.backer == current_user Subscription.backer == current_user
).order_by(Subscription.subscribe_date.desc()).execute() ).order_by(Subscription.subscribe_date.desc()).execute()
feed['active_subscriptions'] = active_subscriptions
new_posts = Content.select().where( new_posts = Content.select().where(
Content.hidden == False, Content.hidden == False,
Content.creator in [c.creator for c in active_subscriptions] Content.creator in [c.creator for c in active_subscriptions]
).order_by(Content.post_date.desc()).execute() ).order_by(Content.post_date.desc()).execute()
feed = { feed['new_posts'] = new_posts
'new_creators': new_creators,
'new_posts': new_posts,
'active_subscriptions': active_subscriptions
}
return await render_template( return await render_template(
'index.html', 'index.html',
feed=feed feed=feed
) )

@ -8,23 +8,6 @@
</p> </p>
<p>Platform Wallet: {{ config.PLATFORM_WALLET }}</p> <p>Platform Wallet: {{ config.PLATFORM_WALLET }}</p>
<form method="POST" action=""> {% include 'includes/form.html' %}
{% for f in form %}
{% if f.name == 'csrf_token' %}
{{ f }}
{% else %}
<div class="form-group">
{{ f.label }}
{{ f }}
</div>
{% endif %}
{% endfor %}
<ul>
{% for field, errors in form.errors.items() %}
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
{% endfor %}
</ul>
<input type="submit" value="Confirm" class="btn btn-link btn-outline btn-xl">
</form>
{% endblock %} {% endblock %}

@ -0,0 +1,49 @@
{% extends 'includes/base.html' %}
{% block content %}
<h2>Platform Subscriptions</h2>
<ul>
{% for s in platform_subs %}
<li>
Platform Subscription ID: {{ s.id }} <br>
Created: {{ s.create_date }} <br>
TX ID: {{ s.tx.tx_id }} <br>
Paid: {{ s.tx.atomic_xmr | from_atomic }} XMR <br>
Term Length: {{ s.term_hours }} hours ({{ s.hours_until_content_hidden }} hours) <br>
Hide Content: {{ s.hours_until_content_hidden }} hours <br>
Archive Content: {{ s.hours_until_content_archived }} hours <br>
Active Subscription: <strong>{{ s.is_active }}</strong> <br>
<br>
</li>
{% endfor %}
</ul>
<h2>Content Subscriptions</h2>
{% if not content_subs %}
<p>No subscriptions to your content yet.</p>
{% include 'includes/form.html' %}
{% else %}
<ul>
{% for s in content_subs %}
<li>
Content Subscription ID: {{ s.id }} <br>
Created: {{ s.create_date }} <br>
Price: {{ s.atomic_xmr | from_atomic }} XMR <br>
Term Length: {{ s.number_hours }} hours <br>
Active Subscriptions: {{ s.get_active_subscriptions().count() }} <br>
</li>
{% endfor %}
</ul>
{% endif %}
<h2>Subscribers</h2>
{% if not subscribers %}
<p>No subscribers yet.</p>
{% else %}
{% for subscriber in subscribers %}
{{ subscriber }}
{% endfor %}
{% endif %}
{% endblock %}

@ -4,7 +4,7 @@
<body> <body>
{% include 'includes/header.html' %} {% include 'includes/header.html' %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
{% include 'includes/footer.html' %} {% include 'includes/debug.html' %}
{% include 'includes/scripts.html' %} {% include 'includes/scripts.html' %}
</body> </body>
</html> </html>

@ -0,0 +1,11 @@
<hr>
{% if config.DEBUG and current_user.is_authenticated %}
<h2>Debug</h2>
<p>
Authenticated: {{ current_user.is_authenticated }} <br>
Username: {{ current_user.handle }} <br>
Email: {{ current_user.email }} <br>
Wallet Address: {{ current_user.wallet_address }} <br>
</p>
{% endif %}

@ -0,0 +1,18 @@
<form method="POST" action="">
{% for f in form %}
{% if f.name == 'csrf_token' %}
{{ f }}
{% else %}
<div class="form-group">
{{ f.label }}
{{ f }}
</div>
{% endif %}
{% endfor %}
<ul>
{% for field, errors in form.errors.items() %}
<li>{{ form[field].label }}: {{ ', '.join(errors) }}</li>
{% endfor %}
</ul>
<input type="submit" value="Confirm" class="btn btn-link btn-outline btn-xl">
</form>

@ -7,16 +7,8 @@
<li><a href="{{ url_for('auth.logout') }}">Logout</a></li> <li><a href="{{ url_for('auth.logout') }}">Logout</a></li>
</ul> </ul>
</nav> </nav>
{% if current_user.is_authenticated %}
<p>Authenticated: {{ current_user.is_authenticated }}</p>
<p>Username: {{ current_user.handle }}</p>
<p>Email: {{ current_user.email }}</p>
<p>Wallet Address: {{ current_user.wallet_address }}</p>
{% endif %}
</header> </header>
<hr>
<script src="/static/js/noty.js"></script> <script src="/static/js/noty.js"></script>
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}

@ -1,20 +1,17 @@
<!DOCTYPE HTML> {% extends 'includes/base.html' %}
<html>
{% include 'includes/head.html' %} {% block content %}
<body class="is-preload landing"> {% if current_user.is_authenticated %}
<div id="page-wrapper">
{% include 'includes/header.html' %}
{% if current_user.is_authenticated %}
{% if 2 not in current_user.roles %} {% if 2 not in current_user.roles %}
<a href="{{ url_for('creator.join') }}">Become a Creator</a> <a href="{{ url_for('creator.join') }}">Become a Creator</a>
{% else %}
<a href="{{ url_for('creator.manage_subscriptions') }}">Manage Subscriptions</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if feed %} {% if feed %}
<h1>Feed</h1>
{% if feed['new_creators'] %} {% if feed['new_creators'] %}
<h2>New Creators</h2> <h2>New Creators</h2>
{% for c in feed['new_creators'] %} {% for c in feed['new_creators'] %}
@ -33,13 +30,6 @@
<p>{{ s.id }}</p> <p>{{ s.id }}</p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% include 'includes/footer.html' %}
</div>
{% include 'includes/scripts.html' %}
</body> {% endblock %}
</html>

Loading…
Cancel
Save