from io import BytesIO import arrow import stripe from flask import render_template, request, redirect, url_for, flash from flask_login import login_required, current_user from flask_wtf import FlaskForm from flask_wtf.file import FileField from wtforms import StringField, validators from app import s3, email_utils from app.config import URL, PROMO_CODE from app.dashboard.base import dashboard_bp from app.email_utils import notify_admin from app.extensions import db from app.log import LOG from app.models import PlanEnum, File, ResetPasswordCode from app.utils import random_string class SettingForm(FlaskForm): name = StringField("Name", validators=[validators.DataRequired()]) profile_picture = FileField("Profile Picture") class PromoCodeForm(FlaskForm): code = StringField("Name", validators=[validators.DataRequired()]) @dashboard_bp.route("/setting", methods=["GET", "POST"]) @login_required def setting(): form = SettingForm() promo_form = PromoCodeForm() if request.method == "POST": if request.form.get("form-name") == "update-profile": if form.validate(): # update user info current_user.name = form.name.data if form.profile_picture.data: file_path = random_string(30) file = File.create(path=file_path) s3.upload_from_bytesio( file_path, BytesIO(form.profile_picture.data.read()) ) db.session.flush() LOG.d("upload file %s to s3", file) current_user.profile_picture_id = file.id db.session.flush() db.session.commit() flash(f"Your profile has been updated", "success") elif request.form.get("form-name") == "cancel-subscription": # sanity check if not (current_user.is_premium() and current_user.plan_expiration is None): raise Exception("user cannot cancel subscription") notify_admin(f"user {current_user} cancels subscription") # the plan will finish at the end of the current period current_user.plan_expiration = current_user.plan_current_period_end() stripe.Subscription.modify( current_user.stripe_subscription_id, cancel_at_period_end=True ) db.session.commit() flash( f"Your plan will be downgraded {current_user.plan_expiration.humanize()}", "success", ) elif request.form.get("form-name") == "reactivate-subscription": if not (current_user.is_premium() and current_user.plan_expiration): raise Exception("user cannot reactivate subscription") notify_admin(f"user {current_user} reactivates subscription") # the plan will finish at the end of the current period current_user.plan_expiration = None stripe.Subscription.modify( current_user.stripe_subscription_id, cancel_at_period_end=False ) db.session.commit() flash(f"Your plan is reactivated now, thank you!", "success") elif request.form.get("form-name") == "change-password": send_reset_password_email(current_user) elif request.form.get("form-name") == "promo-code": if promo_form.validate(): promo_code = promo_form.code.data.upper() if promo_code != PROMO_CODE: flash( "Unknown promo code. Are you sure this is the right code?", "warning", ) return render_template( "dashboard/setting.html", form=form, PlanEnum=PlanEnum, promo_form=promo_form, ) elif promo_code in current_user.get_promo_codes(): flash( "You have already used this promo code. A code can be used only once :(", "warning", ) return render_template( "dashboard/setting.html", form=form, PlanEnum=PlanEnum, promo_form=promo_form, ) else: LOG.d("apply promo code %s for user %s", promo_code, current_user) current_user.plan = PlanEnum.trial if current_user.plan_expiration: LOG.d("extend the current plan 1 year") current_user.plan_expiration = current_user.plan_expiration.shift( years=1 ) else: LOG.d("set plan_expiration to 1 year from now") current_user.plan_expiration = arrow.now().shift(years=1) current_user.save_new_promo_code(promo_code) db.session.commit() flash( "The promo code has been applied successfully to your account!", "success", ) return redirect(url_for("dashboard.setting")) return render_template( "dashboard/setting.html", form=form, PlanEnum=PlanEnum, promo_form=promo_form ) def send_reset_password_email(user): """ generate a new ResetPasswordCode and send it over email to user """ # the activation code is valid for 1h reset_password_code = ResetPasswordCode.create( user_id=user.id, code=random_string(60), expired=arrow.now().shift(hours=1) ) db.session.commit() reset_password_link = f"{URL}/auth/reset_password?code={reset_password_code.code}" email_utils.send( user.email, f"Reset your password on SimpleLogin", html_content=f""" Hi {user.name}!

To reset or change your password, please follow this link reset password. Or you can paste this link into your browser:

{reset_password_link}

Cheers, SimpleLogin team. """, ) flash( "You are going to receive an email containing instruction to change your password", "success", )