Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions config.example.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

# What the site identifies itself as. This affects templates, not database stuff.
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
SITE_FLAVOR = 'nyaa' # 'nyaa' or 'sukebei'
Expand Down Expand Up @@ -49,13 +51,25 @@
SQLALCHEMY_DATABASE_URI = (
'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_PORT = 587
MAIL_FROM_ADDRESS = '***'
SMTP_USERNAME = '***'
SMTP_PASSWORD = '***'


# The maximum number of files a torrent can contain
# until the site says "Too many files to display."
MAX_FILES_VIEW = 1000
Expand Down
83 changes: 83 additions & 0 deletions nyaa/email.py
Original file line number Diff line number Diff line change
@@ -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 changes: 24 additions & 0 deletions nyaa/templates/email/verify.html
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions nyaa/templates/email/verify.txt
Original file line number Diff line number Diff line change
@@ -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.
46 changes: 18 additions & 28 deletions nyaa/views/account.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import smtplib
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from ipaddress import ip_address

import flask

from nyaa import forms, models
from nyaa import email, forms, models
from nyaa.extensions import db
from nyaa.views.users import get_activation_link

Expand Down Expand Up @@ -83,8 +80,7 @@ def register():
db.session.commit()

if app.config['USE_EMAIL_VERIFICATION']: # force verification, enable email
activ_link = get_activation_link(user)
send_verification_email(user.email, activ_link)
send_verification_email(user)
return flask.render_template('waiting.html')
else: # disable verification, set user as active and auto log in
user.status = models.UserStatusType.ACTIVE
Expand Down Expand Up @@ -150,25 +146,19 @@ def redirect_url():
return url


def send_verification_email(to_address, activ_link):
''' this is until we have our own mail server, obviously.
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:'

msg = MIMEMultipart()
msg['Subject'] = 'Verification Link'
msg['From'] = app.config['MAIL_FROM_ADDRESS']
msg['To'] = to_address
msg.attach(MIMEText(msg_body, 'plain'))

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'], to_address, msg.as_string())
server.quit()
def send_verification_email(user):
activation_link = get_activation_link(user)

tmpl_context = {
'activation_link': activation_link,
'user': user
}

email_msg = email.EmailHolder(
subject='Verify your {} account'.format(app.config['GLOBAL_SITE_NAME']),
recipient=user,
text=flask.render_template('email/verify.txt', **tmpl_context),
html=flask.render_template('email/verify.html', **tmpl_context),
)

email.send_email(email_msg)
14 changes: 11 additions & 3 deletions nyaa/views/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,23 @@ def activate_user(payload):

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)

# Set user active
user.status = models.UserStatusType.ACTIVE

db.session.add(user)
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):
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pytest==3.1.1
python-dateutil==2.6.0
python-editor==1.0.3
python-utils==2.1.0
requests==2.18.4
six==1.10.0
SQLAlchemy==1.1.10
SQLAlchemy-FullText-Search==0.2.3
Expand Down