This patch adds an optional config parameter, PREFIX, that can be used
in conjunction with a reverse proxy that places the application in a
virtual path, i.e., http://server.example.com/newspipe/
B. Stack
---
contrib/apache.conf | 8 ++++++++
instance/config.py | 3 +++
instance/sqlite.py | 3 +++
newspipe/bootstrap.py | 20 ++++++++++++++++++++
newspipe/controllers/user.py | 2 +-
newspipe/templates/article.html | 2 +-
newspipe/templates/duplicates.html | 6 +++---
newspipe/templates/history.html | 2 +-
newspipe/templates/home.html | 10 +++++-----
newspipe/templates/layout.html | 2 +-
newspipe/templates/login.html | 2 +-
newspipe/templates/management.html | 2 +-
12 files changed, 48 insertions(+), 14 deletions(-)
create mode 100644 contrib/apache.conf
diff --git a/contrib/apache.conf b/contrib/apache.conf
new file mode 100644
index 0000000..e5d001e
--- /dev/null
+++ b/contrib/apache.conf
@@ -0,0 +1,8 @@
+<Location /newspipe>
+ ProxyPass http://newspipe.vm.example.com:5004/ retry=0
+ ProxyPassReverse http://newspipe.vm.example.com:5004/
+ # You might be tempted to try the following, but it does not work
in flask or newspipe.
+ # a2enmod headers. These are extra ones that are not provided by
Apache natively.
+ #RequestHeader append X-Forwarded-Prefix "/newspipe"
+ #RequestHeader append X-Script-Name "/newspipe"
+</Location>
diff --git a/instance/config.py b/instance/config.py
index 42e624c..af7617a 100644
--- a/instance/config.py
+++ b/instance/config.py
@@ -10,6 +10,9 @@ PORT = 5000
DEBUG = True
API_ROOT = "/api/v2.0"
+# Optional, and useful if you are using a reverse proxy with this
virtual path prefix
+#PREFIX = "/newspipe"
+
CSRF_ENABLED = True
SECRET_KEY = "LCx3BchmHRxFzkEv4BqQJyeXRLXenf"
SECURITY_PASSWORD_SALT = "L8gTsyrpRQEF8jNWQPyvRfv7U5kJkD"
diff --git a/instance/sqlite.py b/instance/sqlite.py
index 1f8d620..c6eaa46 100644
--- a/instance/sqlite.py
+++ b/instance/sqlite.py
@@ -10,6 +10,9 @@ PORT = 5000
DEBUG = True
API_ROOT = "/api/v2.0"
+# Optional, and useful if you are using a reverse proxy with this
virtual path prefix
+#PREFIX = "/newspipe"
+
CSRF_ENABLED = True
SECRET_KEY = "LCx3BchmHRxFzkEv4BqQJyeXRLXenf"
SECURITY_PASSWORD_SALT = "L8gTsyrpRQEF8jNWQPyvRfv7U5kJkD"
diff --git a/newspipe/bootstrap.py b/newspipe/bootstrap.py
index 1204491..a809bdc 100644
--- a/newspipe/bootstrap.py
+++ b/newspipe/bootstrap.py
@@ -43,6 +43,16 @@ def set_logging(
for handler in logger.handlers:
handler.setLevel(log_level)
logger.setLevel(log_level)
+
+
+class ReverseProxied(object):
+ def __init__(self, app, script_name):
+ self.app = app
+ self.script_name = script_name
+
+ def __call__(self, environ, start_response):
+ environ['SCRIPT_NAME'] = self.script_name
+ return self.app(environ, start_response)
# Create Flask application
@@ -63,6 +73,11 @@ else:
set_logging(application.config["LOG_PATH"])
+_prefix = ""
+if "PREFIX" in application.config:
+ application.wsgi_app = ReverseProxied(application.wsgi_app,
script_name=application.config["PREFIX"])
+ _prefix = application.config["PREFIX"]
+
db = SQLAlchemy(application)
migrate = Migrate(application, db)
@@ -100,3 +115,8 @@ application.jinja_env.filters["datetime"] = format_datetime
application.jinja_env.filters["datetimeformat"] = datetimeformat
# inject application in Jinja env
application.jinja_env.globals["application"] = application
+@application.context_processor
+def utility_processor():
+ def prefix():
+ return _prefix.rstrip("/")
+ return dict(prefix=prefix)
diff --git a/newspipe/controllers/user.py b/newspipe/controllers/user.py
index c250164..5413779 100644
--- a/newspipe/controllers/user.py
+++ b/newspipe/controllers/user.py
@@ -144,7 +144,7 @@ class LdapuserController:
namelist = []
try:
query = dns.resolver.query(f"_ldap._tcp.{domain}", "SRV")
- except dns.resolver.NXDOMAIN:
+ except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# no records exist that match the request, so we were probably
# given a specific hostname, and an empty query will trigger
# the logic below that will add the original domain to the list.
diff --git a/newspipe/templates/article.html b/newspipe/templates/article.html
index c62e3f0..d9cf9a6 100644
--- a/newspipe/templates/article.html
+++ b/newspipe/templates/article.html
@@ -4,7 +4,7 @@
<div class="row" data-article="{{ article.id }}" id="filters"
data-filter="{{ filter_ }}">
<div class="col">
<h2><a href="{{ article.link }}" target="_blank">{{
article.title|safe }}</a></h2>
- <h3>{{ _('from') }} <a href="/feed/{{ article.source.id
}}">{{ article.source.title }}</a></h3>
+ <h3>{{ _('from') }} <a href="{{ prefix() }}/feed/{{
article.source.id }}">{{ article.source.title }}</a></h3>
<a href="{{ url_for("article.delete",
article_id=article.id) }}"><i class="fa fa-times delete"
aria-hidden="true" title="{{ _('Delete this article') }}"></i></a>
{% if article.like %}
<a href="#"><i class="fa fa-star like"
aria-hidden="true" title="{{ _('One of your favorites') }}"></i></a>
diff --git a/newspipe/templates/duplicates.html
b/newspipe/templates/duplicates.html
index 38dc52b..d138226 100644
--- a/newspipe/templates/duplicates.html
+++ b/newspipe/templates/duplicates.html
@@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
- <p><h1>{{ _('Duplicates in the feed') }} <a href="/feed/{{
feed.id }}">{{ feed.title }}</a>.</h1><p>
+ <p><h1>{{ _('Duplicates in the feed') }} <a href="{{ prefix()
}}/feed/{{ feed.id }}">{{ feed.title }}</a>.</h1><p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@@ -19,8 +19,8 @@
{% for pair in duplicates %}
<tr>
<td>{{ loop.index }}</td>
- <td id="{{ pair[0].id }}"><a href="{{
url_for("article.delete", article_id=pair[0].id) }}"><i class="fa
fa-times" aria-hidden="true" title="{{ _('Delete this article')
}}"></i></a> <a href="/article/{{ pair[0].id }}">{{ pair[0].title
}}</a> ({{ pair[0].retrieved_date }})</td>
- <td id="{{ pair[1].id }}"><a href="{{
url_for("article.delete", article_id=pair[1].id) }}"><i class="fa
fa-times" aria-hidden="true" title="{{ _('Delete this article')
}}"></i></a> <a href="/article/{{ pair[1].id }}">{{ pair[1].title
}}</a> ({{ pair[1].retrieved_date }})</td>
+ <td id="{{ pair[0].id }}"><a href="{{
url_for("article.delete", article_id=pair[0].id) }}"><i class="fa
fa-times" aria-hidden="true" title="{{ _('Delete this article')
}}"></i></a> <a href="{{ prefix() }}/article/{{ pair[0].id }}">{{
pair[0].title }}</a> ({{ pair[0].retrieved_date }})</td>
+ <td id="{{ pair[1].id }}"><a href="{{
url_for("article.delete", article_id=pair[1].id) }}"><i class="fa
fa-times" aria-hidden="true" title="{{ _('Delete this article')
}}"></i></a> <a href="{{ prefix() }}/article/{{ pair[1].id }}">{{
pair[1].title }}</a> ({{ pair[1].retrieved_date }})</td>
</tr>
{% endfor %}
</tbody>
diff --git a/newspipe/templates/history.html b/newspipe/templates/history.html
index 153c2f1..00e22ef 100644
--- a/newspipe/templates/history.html
+++ b/newspipe/templates/history.html
@@ -43,7 +43,7 @@
<ul class="list-group">
{% for date in articles_counter | sort(reverse = True) %}
{% for article in articles %}
- <li class="list-group-item">{{ article.date | datetime }} -
<a href="/article/{{ article.id }}">{{ article.title | safe
}}</a></li>
+ <li class="list-group-item">{{ article.date | datetime }} -
<a href="{{ prefix() }}/article/{{ article.id }}">{{ article.title |
safe }}</a></li>
{% endfor %}
{% endfor %}
</ul>
diff --git a/newspipe/templates/home.html b/newspipe/templates/home.html
index 631b769..5feb18d 100644
--- a/newspipe/templates/home.html
+++ b/newspipe/templates/home.html
@@ -42,7 +42,7 @@
{% if feed_id == feed.id %}</b>{% endif %}
</a></li>
<li class="nav-item feed-commands {% if
in_error.get(fid, 0) > 0 %}d-none{% endif %}" data-bs-feed="{{ feed.id
}}"><span class="nav-link">
- <a href="/feed/{{ feed.id }}"><i
class="fa fa-info" aria-hidden="true" title="{{ _('Details')
}}"></i></a>
+ <a href="{{ prefix() }}/feed/{{ feed.id
}}"><i class="fa fa-info" aria-hidden="true" title="{{ _('Details')
}}"></i></a>
<a href="{{ url_for('feed.form',
feed_id=feed.id) }}"><i class="fa fa-pencil-square-o"
aria-hidden="true" title="{{ _('Edit this feed') }}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='unread', feed_id=feed.id) }}"><i class="fa fa-square-o"
aria-hidden="true" title="{{ _('Mark this feed as unread')
}}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='read', feed_id=feed.id) }}"><i class="fa fa-check-square-o"
aria-hidden="true" title="{{ _('Mark this feed as read') }}"></i></a>
@@ -73,7 +73,7 @@
{% if feed_id == fid %}</b>{% endif %}
</a></li>
<li class="nav-item feed-commands {% if in_error.get(fid,
0) > 0 %}d-none{% endif %}" data-bs-feed="{{ fid }}"><span
class="nav-link">
- <a href="/feed/{{ fid }}"><i class="fa fa-info"
aria-hidden="true" title="{{ _('Details') }}"></i></a>
+ <a href="{{ prefix() }}/feed/{{ fid }}"><i class="fa
fa-info" aria-hidden="true" title="{{ _('Details') }}"></i></a>
<a href="{{ url_for('feed.form', feed_id=fid) }}"><i
class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit
this feed') }}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='unread', feed_id=fid) }}"><i class="fa fa-square-o"
aria-hidden="true" title="{{ _('Mark this feed as unread')
}}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='read', feed_id=fid) }}"><i class="fa fa-check-square-o"
aria-hidden="true" title="{{ _('Mark this feed as read') }}"></i></a>
@@ -102,7 +102,7 @@
{% if feed_id == fid %}</b>{% endif %}
</a></li>
<li class="nav-item feed-commands {% if in_error.get(fid,
0) > 0 %}d-none{% endif %}" data-bs-feed="{{ fid }}"><span
class="nav-link">
- <a href="/feed/{{ fid }}"><i class="fa fa-info"
aria-hidden="true" title="{{ _('Details') }}"></i></a>
+ <a href="{{ prefix() }}/feed/{{ fid }}"><i class="fa
fa-info" aria-hidden="true" title="{{ _('Details') }}"></i></a>
<a href="{{ url_for('feed.form', feed_id=fid) }}"><i
class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit
this feed') }}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='unread', feed_id=fid) }}"><i class="fa fa-square-o"
aria-hidden="true" title="{{ _('Mark this feed as unread')
}}"></i></a>
<a href="{{ url_for('article.mark_as',
new_value='read', feed_id=fid) }}"><i class="fa fa-check-square-o"
aria-hidden="true" title="{{ _('Mark this feed as read') }}"></i></a>
@@ -174,11 +174,11 @@
{% if not feed_id %}
<td class="d-none d-md-block">
<img src="{{ url_for('icon.icon',
url=feeds[article.source.id].icon_url) }}" width="16px">
- <a href="/article/redirect/{{
article.id}}" target="_blank">{{ article.source.title | safe }}</a>
+ <a href="{{ prefix()
}}/article/redirect/{{ article.id}}" target="_blank">{{
article.source.title | safe }}</a>
</td>
{% endif %}
<td {%if filter_ == 'all' and
article.readed == False %}style='font-weight:bold'{% endif %}>
- <a href="/article/{{ article.id }}"
title="{{ article.title }}">{{ article.title | truncate(100, False,
'...') }}</a>
+ <a href="{{ prefix() }}/article/{{
article.id }}" title="{{ article.title }}">{{ article.title |
truncate(100, False, '...') }}</a>
</td>
<td class="date d-none d-lg-block">{{
article.date | datetime(format='short') }}</td>
</tr>
diff --git a/newspipe/templates/layout.html b/newspipe/templates/layout.html
index 2464040..90919a7 100644
--- a/newspipe/templates/layout.html
+++ b/newspipe/templates/layout.html
@@ -21,7 +21,7 @@
{% block menu %}
<nav class="navbar navbar-expand-lg navbar-dark bg-newspipe-blue">
<div class="container-fluid">
- <a class="navbar-brand" href="/">Newspipe</a>
+ <a class="navbar-brand" href="{{ prefix() }}/">Newspipe</a>
<button class="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
diff --git a/newspipe/templates/login.html b/newspipe/templates/login.html
index b995230..78af3c4 100644
--- a/newspipe/templates/login.html
+++ b/newspipe/templates/login.html
@@ -19,7 +19,7 @@
<div class="alert alert-warning" role="alert">{{
message }}</div>
{% endfor %}
{{ form.submit(class_="btn btn-primary") }}
- {% if self_registration %}<a href="/signup"
class="btn btn-info">{{ _('Sign up') }}</a>{% endif %}
+ {% if self_registration %}<a href="{{ prefix()
}}/signup" class="btn btn-info">{{ _('Sign up') }}</a>{% endif %}
</form>
</div>
</div>
diff --git a/newspipe/templates/management.html
b/newspipe/templates/management.html
index 4e977f8..19dbded 100644
--- a/newspipe/templates/management.html
+++ b/newspipe/templates/management.html
@@ -6,7 +6,7 @@
<div class="row">
<div class="col">
<h2>{{ _('Your subscriptions') }}</h2>
- <p>{{ _('You are subscribed to') }} {{ nb_feeds
}} <a href="/feeds">{{ _('feeds') }}</a>. <a href="{{
url_for("feed.form") }}">{{ _('Add') }}</a> {{ _('a feed') }}.</p>
+ <p>{{ _('You are subscribed to') }} {{ nb_feeds
}} <a href="{{ prefix() }}/feeds">{{ _('feeds') }}</a>. <a href="{{
url_for("feed.form") }}">{{ _('Add') }}</a> {{ _('a feed') }}.</p>
<p>{{ nb_articles }} {{ _('articles are stored in
the database with') }} {{ nb_unread_articles }} {{ _('unread
articles') }}.</p>
<p>{{ _('You have') }} {{ nb_categories }} <a
href="{{ url_for("categories.list_")}}">{{ _('categories') }}</a>.</p>
<a href="{{ url_for("articles.expire", weeks=10)
}}" class="btn btn-primary" onclick="return confirm('{{ _('You are
going to delete old articles.') }}');">{{ _('Delete articles older
than 10 weeks') }}</a>
--
1.8.3.1