diff --git a/config.example.py b/config.example.py
index 53b2a40..9054e44 100644
--- a/config.example.py
+++ b/config.example.py
@@ -1,7 +1,13 @@
import os
-
DEBUG = True
+# A read-only maintenance mode, in which the database is not modified
+MAINTENANCE_MODE = True
+# A maintenance message (used in layout.html template)
+MAINTENANCE_MODE_MESSAGE = 'Site is currently in read-only maintenance mode.'
+# Allow logging in during maintenance (without updating last login date)
+MAINTENANCE_MODE_LOGINS = True
+
USE_RECAPTCHA = False
USE_EMAIL_VERIFICATION = False
USE_MYSQL = True
diff --git a/nyaa/templates/layout.html b/nyaa/templates/layout.html
index 9d7feea..737847d 100644
--- a/nyaa/templates/layout.html
+++ b/nyaa/templates/layout.html
@@ -312,6 +312,12 @@
{% include "flashes.html" %}
+ {% if config.MAINTENANCE_MODE and config.MAINTENANCE_MODE_MESSAGE %}
+
+
+ {{ config.MAINTENANCE_MODE_MESSAGE | safe }}
+
+ {% endif %}
{% block body %}{% endblock %}
diff --git a/nyaa/views/__init__.py b/nyaa/views/__init__.py
index ae58c99..32f745b 100644
--- a/nyaa/views/__init__.py
+++ b/nyaa/views/__init__.py
@@ -1,3 +1,5 @@
+import flask
+
from nyaa.views import ( # isort:skip
account,
admin,
@@ -8,8 +10,37 @@ from nyaa.views import ( # isort:skip
)
+def _maintenance_mode_hook():
+ ''' Blocks POSTs, unless MAINTENANCE_MODE_LOGINS is True and the POST is for a login. '''
+ if flask.request.method == 'POST':
+ allow_logins = flask.current_app.config['MAINTENANCE_MODE_LOGINS']
+ endpoint = flask.request.endpoint
+
+ if not (allow_logins and endpoint == 'account.login'):
+ message = 'Site is currently in maintenance mode.'
+
+ # In case of an API request, return a plaintext error message
+ if endpoint.startswith('api.'):
+ resp = flask.make_response(message, 405)
+ resp.headers['Content-Type'] = 'text/plain'
+ return resp
+ else:
+ # Otherwise redirect to the target page and flash a message
+ flask.flash(flask.Markup(message), 'danger')
+ try:
+ target_url = flask.url_for(endpoint)
+ except:
+ # Non-GET-able endpoint, try referrer or default to home page
+ target_url = flask.request.referrer or flask.url_for('main.home')
+ return flask.redirect(target_url)
+
+
def register_views(flask_app):
""" Register the blueprints using the flask_app object """
+ # Add our POST blocker first
+ if flask_app.config['MAINTENANCE_MODE']:
+ flask_app.before_request(_maintenance_mode_hook)
+
flask_app.register_blueprint(account.bp)
flask_app.register_blueprint(admin.bp)
flask_app.register_blueprint(main.bp)
diff --git a/nyaa/views/account.py b/nyaa/views/account.py
index 5e9615a..003f061 100644
--- a/nyaa/views/account.py
+++ b/nyaa/views/account.py
@@ -21,6 +21,10 @@ def login():
form = forms.LoginForm(flask.request.form)
if flask.request.method == 'POST' and form.validate():
+ if app.config['MAINTENANCE_MODE'] and not app.config['MAINTENANCE_MODE_LOGINS']:
+ flask.flash(flask.Markup('Logins are currently disabled.'), 'danger')
+ return flask.redirect(flask.url_for('account.login'))
+
username = form.username.data.strip()
password = form.password.data
user = models.User.by_username(username)
@@ -40,8 +44,9 @@ def login():
user.last_login_date = datetime.utcnow()
user.last_login_ip = ip_address(flask.request.remote_addr).packed
- db.session.add(user)
- db.session.commit()
+ if not app.config['MAINTENANCE_MODE']:
+ db.session.add(user)
+ db.session.commit()
flask.g.user = user
flask.session['user_id'] = user.id
diff --git a/nyaa/views/main.py b/nyaa/views/main.py
index d9f9789..21ed75e 100644
--- a/nyaa/views/main.py
+++ b/nyaa/views/main.py
@@ -41,11 +41,12 @@ def before_request():
flask.session.permanent = True
flask.session.modified = True
- ip = ip_address(flask.request.remote_addr)
- if user.last_login_ip != ip:
- user.last_login_ip = ip.packed
- db.session.add(user)
- db.session.commit()
+ if not app.config['MAINTENANCE_MODE']:
+ ip = ip_address(flask.request.remote_addr)
+ if user.last_login_ip != ip:
+ user.last_login_ip = ip.packed
+ db.session.add(user)
+ db.session.commit()
# Check if user is banned on POST
if flask.request.method == 'POST':
diff --git a/nyaa/views/users.py b/nyaa/views/users.py
index 31b9861..48cf861 100644
--- a/nyaa/views/users.py
+++ b/nyaa/views/users.py
@@ -190,6 +190,10 @@ def view_user(user_name):
@bp.route('/user/activate/')
def activate_user(payload):
+ if app.config['MAINTENANCE_MODE']:
+ flask.flash(flask.Markup('Activations are currently disabled.'), 'danger')
+ return flask.redirect(flask.url_for('main.home'))
+
s = get_serializer()
try:
user_id = s.loads(payload)