mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 10:19:59 +00:00
Update email verification, add Mailgun backend (#380)
Changes config.example.py!
This commit is contained in:
parent
19eaa2c532
commit
6d09920abd
|
@ -19,6 +19,8 @@ MAINTENANCE_MODE_LOGINS = True
|
||||||
|
|
||||||
# What the site identifies itself as. This affects templates, not database stuff.
|
# What the site identifies itself as. This affects templates, not database stuff.
|
||||||
SITE_NAME = 'Nyaa'
|
SITE_NAME = 'Nyaa'
|
||||||
|
# What the both sites are labeled under (used for eg. email subjects)
|
||||||
|
GLOBAL_SITE_NAME = 'Nyaa.si'
|
||||||
|
|
||||||
# General prefix for running multiple sites, eg. most database tables are site-prefixed
|
# General prefix for running multiple sites, eg. most database tables are site-prefixed
|
||||||
SITE_FLAVOR = 'nyaa' # 'nyaa' or 'sukebei'
|
SITE_FLAVOR = 'nyaa' # 'nyaa' or 'sukebei'
|
||||||
|
@ -49,13 +51,25 @@ else:
|
||||||
SQLALCHEMY_DATABASE_URI = (
|
SQLALCHEMY_DATABASE_URI = (
|
||||||
'sqlite:///' + os.path.join(BASE_DIR, 'test.db') + '?check_same_thread=False')
|
'sqlite:///' + os.path.join(BASE_DIR, 'test.db') + '?check_same_thread=False')
|
||||||
|
|
||||||
# Email server settings
|
###########
|
||||||
|
## EMAIL ##
|
||||||
|
###########
|
||||||
|
|
||||||
|
# 'smtp' or 'mailgun'
|
||||||
|
MAIL_BACKEND = 'mailgun'
|
||||||
|
MAIL_FROM_ADDRESS = 'Sender Name <sender@domain.com>'
|
||||||
|
|
||||||
|
# Mailgun settings
|
||||||
|
MAILGUN_API_BASE = 'https://api.mailgun.net/v3/YOUR_DOMAIN_NAME'
|
||||||
|
MAILGUN_API_KEY = 'YOUR_API_KEY'
|
||||||
|
|
||||||
|
# SMTP settings
|
||||||
SMTP_SERVER = '***'
|
SMTP_SERVER = '***'
|
||||||
SMTP_PORT = 587
|
SMTP_PORT = 587
|
||||||
MAIL_FROM_ADDRESS = '***'
|
|
||||||
SMTP_USERNAME = '***'
|
SMTP_USERNAME = '***'
|
||||||
SMTP_PASSWORD = '***'
|
SMTP_PASSWORD = '***'
|
||||||
|
|
||||||
|
|
||||||
# The maximum number of files a torrent can contain
|
# The maximum number of files a torrent can contain
|
||||||
# until the site says "Too many files to display."
|
# until the site says "Too many files to display."
|
||||||
MAX_FILES_VIEW = 1000
|
MAX_FILES_VIEW = 1000
|
||||||
|
|
83
nyaa/email.py
Normal file
83
nyaa/email.py
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import smtplib
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from nyaa import models
|
||||||
|
|
||||||
|
|
||||||
|
class EmailHolder(object):
|
||||||
|
''' Holds email subject, recipient and content, so we have a general class for
|
||||||
|
all mail backends. '''
|
||||||
|
def __init__(self, subject=None, recipient=None, text=None, html=None):
|
||||||
|
self.subject = subject
|
||||||
|
self.recipient = recipient # models.User or string
|
||||||
|
self.text = text
|
||||||
|
self.html = html
|
||||||
|
|
||||||
|
def format_recipient(self):
|
||||||
|
if isinstance(self.recipient, models.User):
|
||||||
|
return '{} <{}>'.format(self.recipient.username, self.recipient.email)
|
||||||
|
else:
|
||||||
|
return self.recipient
|
||||||
|
|
||||||
|
def recipient_email(self):
|
||||||
|
if isinstance(self.recipient, models.User):
|
||||||
|
return self.recipient.email
|
||||||
|
else:
|
||||||
|
return self.recipient.email
|
||||||
|
|
||||||
|
def as_mimemultipart(self):
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg['Subject'] = self.subject
|
||||||
|
msg['From'] = app.config['MAIL_FROM_ADDRESS']
|
||||||
|
msg['To'] = self.format_recipient()
|
||||||
|
|
||||||
|
msg.attach(MIMEText(self.text, 'plain'))
|
||||||
|
if self.html:
|
||||||
|
msg.attach(MIMEText(self.html, 'html'))
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(email_holder):
|
||||||
|
mail_backend = app.config.get('MAIL_BACKEND')
|
||||||
|
if mail_backend == 'mailgun':
|
||||||
|
_send_mailgun(email_holder)
|
||||||
|
elif mail_backend == 'smtp':
|
||||||
|
_send_smtp(email_holder)
|
||||||
|
elif mail_backend:
|
||||||
|
# TODO: Do this in logging.error when we have that set up
|
||||||
|
print('Unknown mail backend:', mail_backend)
|
||||||
|
|
||||||
|
|
||||||
|
def _send_mailgun(email_holder):
|
||||||
|
mailgun_endpoint = app.config['MAILGUN_API_BASE'] + '/messages'
|
||||||
|
auth = ('api', app.config['MAILGUN_API_KEY'])
|
||||||
|
data = {
|
||||||
|
'from': app.config['MAIL_FROM_ADDRESS'],
|
||||||
|
'to': email_holder.format_recipient(),
|
||||||
|
'subject': email_holder.subject,
|
||||||
|
'text': email_holder.text,
|
||||||
|
'html': email_holder.html
|
||||||
|
}
|
||||||
|
r = requests.post(mailgun_endpoint, data=data, auth=auth)
|
||||||
|
# TODO real error handling?
|
||||||
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def _send_smtp(email_holder):
|
||||||
|
# NOTE: Unused, most likely untested! Should work, however.
|
||||||
|
msg = email_holder.as_mimemultipart()
|
||||||
|
|
||||||
|
server = smtplib.SMTP(app.config['SMTP_SERVER'], app.config['SMTP_PORT'])
|
||||||
|
server.set_debuglevel(1)
|
||||||
|
server.ehlo()
|
||||||
|
server.starttls()
|
||||||
|
server.ehlo()
|
||||||
|
server.login(app.config['SMTP_USERNAME'], app.config['SMTP_PASSWORD'])
|
||||||
|
server.sendmail(app.config['SMTP_USERNAME'], email_holder.recipient_email(), msg.as_string())
|
||||||
|
server.quit()
|
24
nyaa/templates/email/verify.html
Normal file
24
nyaa/templates/email/verify.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Verify your {{ config.GLOBAL_SITE_NAME }} account</title>
|
||||||
|
<style type="text/css">
|
||||||
|
.well {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: rgb(240, 240, 240)
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
{{ user.username }}, please verify your email by clicking the link below:
|
||||||
|
</div>
|
||||||
|
<div class="well">
|
||||||
|
<a href="{{ activation_link }}">{{ activation_link }}</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
If you did not sign up for {{ config.GLOBAL_SITE_NAME }}, feel free to ignore this email.
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
6
nyaa/templates/email/verify.txt
Normal file
6
nyaa/templates/email/verify.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{{ user.username }}, please verify your email by clicking the link below:
|
||||||
|
|
||||||
|
{{ activation_link }}
|
||||||
|
(if you can't click on the link, copy and paste it to your browser's address bar)
|
||||||
|
|
||||||
|
If you did not sign up for {{ config.GLOBAL_SITE_NAME }}, feel free to ignore this email.
|
|
@ -1,12 +1,9 @@
|
||||||
import smtplib
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.mime.multipart import MIMEMultipart
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
|
||||||
from nyaa import forms, models
|
from nyaa import email, forms, models
|
||||||
from nyaa.extensions import db
|
from nyaa.extensions import db
|
||||||
from nyaa.views.users import get_activation_link
|
from nyaa.views.users import get_activation_link
|
||||||
|
|
||||||
|
@ -83,8 +80,7 @@ def register():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if app.config['USE_EMAIL_VERIFICATION']: # force verification, enable email
|
if app.config['USE_EMAIL_VERIFICATION']: # force verification, enable email
|
||||||
activ_link = get_activation_link(user)
|
send_verification_email(user)
|
||||||
send_verification_email(user.email, activ_link)
|
|
||||||
return flask.render_template('waiting.html')
|
return flask.render_template('waiting.html')
|
||||||
else: # disable verification, set user as active and auto log in
|
else: # disable verification, set user as active and auto log in
|
||||||
user.status = models.UserStatusType.ACTIVE
|
user.status = models.UserStatusType.ACTIVE
|
||||||
|
@ -150,25 +146,19 @@ def redirect_url():
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
def send_verification_email(to_address, activ_link):
|
def send_verification_email(user):
|
||||||
''' this is until we have our own mail server, obviously.
|
activation_link = get_activation_link(user)
|
||||||
This can be greatly cut down if on same machine.
|
|
||||||
probably can get rid of all but msg formatting/building,
|
|
||||||
init line and sendmail line if local SMTP server '''
|
|
||||||
|
|
||||||
msg_body = 'Please click on: ' + activ_link + ' to activate your account.\n\n\nUnsubscribe:'
|
tmpl_context = {
|
||||||
|
'activation_link': activation_link,
|
||||||
|
'user': user
|
||||||
|
}
|
||||||
|
|
||||||
msg = MIMEMultipart()
|
email_msg = email.EmailHolder(
|
||||||
msg['Subject'] = 'Verification Link'
|
subject='Verify your {} account'.format(app.config['GLOBAL_SITE_NAME']),
|
||||||
msg['From'] = app.config['MAIL_FROM_ADDRESS']
|
recipient=user,
|
||||||
msg['To'] = to_address
|
text=flask.render_template('email/verify.txt', **tmpl_context),
|
||||||
msg.attach(MIMEText(msg_body, 'plain'))
|
html=flask.render_template('email/verify.html', **tmpl_context),
|
||||||
|
)
|
||||||
|
|
||||||
server = smtplib.SMTP(app.config['SMTP_SERVER'], app.config['SMTP_PORT'])
|
email.send_email(email_msg)
|
||||||
server.set_debuglevel(1)
|
|
||||||
server.ehlo()
|
|
||||||
server.starttls()
|
|
||||||
server.ehlo()
|
|
||||||
server.login(app.config['SMTP_USERNAME'], app.config['SMTP_PASSWORD'])
|
|
||||||
server.sendmail(app.config['SMTP_USERNAME'], to_address, msg.as_string())
|
|
||||||
server.quit()
|
|
||||||
|
|
|
@ -202,15 +202,23 @@ def activate_user(payload):
|
||||||
|
|
||||||
user = models.User.by_id(user_id)
|
user = models.User.by_id(user_id)
|
||||||
|
|
||||||
if not user:
|
# Only allow activating inactive users
|
||||||
|
if not user or user.status != models.UserStatusType.INACTIVE:
|
||||||
flask.abort(404)
|
flask.abort(404)
|
||||||
|
|
||||||
|
# Set user active
|
||||||
user.status = models.UserStatusType.ACTIVE
|
user.status = models.UserStatusType.ACTIVE
|
||||||
|
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return flask.redirect(flask.url_for('account.login'))
|
# Log user in
|
||||||
|
flask.g.user = user
|
||||||
|
flask.session['user_id'] = user.id
|
||||||
|
flask.session.permanent = True
|
||||||
|
flask.session.modified = True
|
||||||
|
|
||||||
|
flask.flash(flask.Markup("You've successfully verified your account!"), 'success')
|
||||||
|
return flask.redirect(flask.url_for('main.home'))
|
||||||
|
|
||||||
|
|
||||||
def _create_user_class_choices(user):
|
def _create_user_class_choices(user):
|
||||||
|
|
|
@ -41,6 +41,7 @@ pytest==3.1.1
|
||||||
python-dateutil==2.6.0
|
python-dateutil==2.6.0
|
||||||
python-editor==1.0.3
|
python-editor==1.0.3
|
||||||
python-utils==2.1.0
|
python-utils==2.1.0
|
||||||
|
requests==2.18.4
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
SQLAlchemy==1.1.10
|
SQLAlchemy==1.1.10
|
||||||
SQLAlchemy-FullText-Search==0.2.3
|
SQLAlchemy-FullText-Search==0.2.3
|
||||||
|
|
Loading…
Reference in a new issue