mirror of
https://github.com/Mailu/Mailu.git
synced 2024-12-14 10:53:30 +02:00
Merge branch 'master' of https://github.com/Mailu/Mailu into fetchmail-improvements
This commit is contained in:
commit
3fc0a0e7fa
@ -5,7 +5,6 @@ FROM node:16-alpine3.16
|
|||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
|
|
||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
COPY webpack.config.js ./
|
|
||||||
|
|
||||||
RUN set -euxo pipefail \
|
RUN set -euxo pipefail \
|
||||||
; npm config set update-notifier false \
|
; npm config set update-notifier false \
|
||||||
@ -17,6 +16,7 @@ RUN set -euxo pipefail \
|
|||||||
done
|
done
|
||||||
|
|
||||||
COPY assets/ ./assets/
|
COPY assets/ ./assets/
|
||||||
|
COPY webpack.config.js ./
|
||||||
|
|
||||||
RUN set -euxo pipefail \
|
RUN set -euxo pipefail \
|
||||||
; node_modules/.bin/webpack-cli --color
|
; node_modules/.bin/webpack-cli --color
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
require('./app.css');
|
|
||||||
|
|
||||||
import logo from './mailu.png';
|
|
||||||
import modules from "./*.json";
|
|
||||||
|
|
||||||
// Inspired from https://github.com/mehdibo/hibp-js/blob/master/hibp.js
|
// Inspired from https://github.com/mehdibo/hibp-js/blob/master/hibp.js
|
||||||
function sha1(string) {
|
function sha1(string) {
|
||||||
var buffer = new TextEncoder("utf-8").encode(string);
|
var buffer = new TextEncoder("utf-8").encode(string);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// AdminLTE
|
// AdminLTE
|
||||||
import 'admin-lte/plugins/jquery/jquery.min.js';
|
window.$ = window.jQuery = require('admin-lte/plugins/jquery/jquery.min.js');
|
||||||
import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle.min.js';
|
import 'admin-lte/plugins/bootstrap/js/bootstrap.bundle.min.js';
|
||||||
import 'admin-lte/build/scss/adminlte.scss';
|
import 'admin-lte/build/scss/adminlte.scss';
|
||||||
import 'admin-lte/build/js/AdminLTE.js';
|
import 'admin-lte/build/js/AdminLTE.js';
|
||||||
@ -18,7 +18,7 @@ import 'admin-lte/plugins/datatables/jquery.dataTables.min.js';
|
|||||||
import 'admin-lte/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js';
|
import 'admin-lte/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js';
|
||||||
import 'admin-lte/plugins/datatables-responsive/js/dataTables.responsive.min.js';
|
import 'admin-lte/plugins/datatables-responsive/js/dataTables.responsive.min.js';
|
||||||
import 'admin-lte/plugins/datatables-responsive/js/responsive.bootstrap4.min.js';
|
import 'admin-lte/plugins/datatables-responsive/js/responsive.bootstrap4.min.js';
|
||||||
|
import modules from "./*.json";
|
||||||
|
|
||||||
// clipboard.js
|
// clipboard.js
|
||||||
import 'clipboard/dist/clipboard.min.js';
|
window.ClipboardJS = require('clipboard/dist/clipboard.min.js');
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ module.exports = {
|
|||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: {
|
entry: {
|
||||||
app: {
|
app: {
|
||||||
import: './assets/app.js',
|
import: ['./assets/app.css', './assets/mailu.png', './assets/app.js'],
|
||||||
dependOn: 'vendor',
|
dependOn: 'vendor',
|
||||||
},
|
},
|
||||||
vendor: './assets/vendor.js',
|
vendor: './assets/vendor.js',
|
||||||
|
@ -13,17 +13,19 @@ DEFAULT_CONFIG = {
|
|||||||
'RATELIMIT_STORAGE_URL': '',
|
'RATELIMIT_STORAGE_URL': '',
|
||||||
'DEBUG': False,
|
'DEBUG': False,
|
||||||
'DEBUG_PROFILER': False,
|
'DEBUG_PROFILER': False,
|
||||||
|
'DEBUG_TB_INTERCEPT_REDIRECTS': False,
|
||||||
'DEBUG_ASSETS': '',
|
'DEBUG_ASSETS': '',
|
||||||
'DOMAIN_REGISTRATION': False,
|
'DOMAIN_REGISTRATION': False,
|
||||||
'TEMPLATES_AUTO_RELOAD': True,
|
'TEMPLATES_AUTO_RELOAD': True,
|
||||||
'MEMORY_SESSIONS': False,
|
'MEMORY_SESSIONS': False,
|
||||||
|
'FETCHMAIL_ENABLED': False,
|
||||||
# Database settings
|
# Database settings
|
||||||
'DB_FLAVOR': None,
|
'DB_FLAVOR': None,
|
||||||
'DB_USER': 'mailu',
|
'DB_USER': 'mailu',
|
||||||
'DB_PW': None,
|
'DB_PW': None,
|
||||||
'DB_HOST': 'database',
|
'DB_HOST': 'database',
|
||||||
'DB_NAME': 'mailu',
|
'DB_NAME': 'mailu',
|
||||||
'SQLITE_DATABASE_FILE':'data/main.db',
|
'SQLITE_DATABASE_FILE': 'data/main.db',
|
||||||
'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db',
|
'SQLALCHEMY_DATABASE_URI': 'sqlite:////data/main.db',
|
||||||
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
|
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
|
||||||
# Statistics management
|
# Statistics management
|
||||||
@ -60,7 +62,7 @@ DEFAULT_CONFIG = {
|
|||||||
# Web settings
|
# Web settings
|
||||||
'SITENAME': 'Mailu',
|
'SITENAME': 'Mailu',
|
||||||
'WEBSITE': 'https://mailu.io',
|
'WEBSITE': 'https://mailu.io',
|
||||||
'ADMIN' : 'none',
|
'ADMIN': 'none',
|
||||||
'WEB_ADMIN': '/admin',
|
'WEB_ADMIN': '/admin',
|
||||||
'WEB_WEBMAIL': '/webmail',
|
'WEB_WEBMAIL': '/webmail',
|
||||||
'WEBMAIL': 'none',
|
'WEBMAIL': 'none',
|
||||||
@ -73,7 +75,7 @@ DEFAULT_CONFIG = {
|
|||||||
'SESSION_KEY_BITS': 128,
|
'SESSION_KEY_BITS': 128,
|
||||||
'SESSION_TIMEOUT': 3600,
|
'SESSION_TIMEOUT': 3600,
|
||||||
'PERMANENT_SESSION_LIFETIME': 30*24*3600,
|
'PERMANENT_SESSION_LIFETIME': 30*24*3600,
|
||||||
'SESSION_COOKIE_SECURE': True,
|
'SESSION_COOKIE_SECURE': None,
|
||||||
'CREDENTIAL_ROUNDS': 12,
|
'CREDENTIAL_ROUNDS': 12,
|
||||||
'TLS_PERMISSIVE': True,
|
'TLS_PERMISSIVE': True,
|
||||||
'TZ': 'Etc/UTC',
|
'TZ': 'Etc/UTC',
|
||||||
@ -156,6 +158,8 @@ class ConfigManager:
|
|||||||
self.config['SESSION_STORAGE_URL'] = f'redis://{self.config["REDIS_ADDRESS"]}/3'
|
self.config['SESSION_STORAGE_URL'] = f'redis://{self.config["REDIS_ADDRESS"]}/3'
|
||||||
self.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
|
self.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
|
||||||
self.config['SESSION_COOKIE_HTTPONLY'] = True
|
self.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||||
|
if self.config['SESSION_COOKIE_SECURE'] is None:
|
||||||
|
self.config['SESSION_COOKIE_SECURE'] = self.config['TLS_FLAVOR'] != 'notls'
|
||||||
self.config['SESSION_PERMANENT'] = True
|
self.config['SESSION_PERMANENT'] = True
|
||||||
self.config['SESSION_TIMEOUT'] = int(self.config['SESSION_TIMEOUT'])
|
self.config['SESSION_TIMEOUT'] = int(self.config['SESSION_TIMEOUT'])
|
||||||
self.config['PERMANENT_SESSION_LIFETIME'] = int(self.config['PERMANENT_SESSION_LIFETIME'])
|
self.config['PERMANENT_SESSION_LIFETIME'] = int(self.config['PERMANENT_SESSION_LIFETIME'])
|
||||||
|
@ -31,12 +31,14 @@
|
|||||||
<p>{% trans %}Auto-reply{% endtrans %}</p>
|
<p>{% trans %}Auto-reply{% endtrans %}</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{%- if config["FETCHMAIL_ENABLED"] %}
|
||||||
<li class="nav-item" role="none">
|
<li class="nav-item" role="none">
|
||||||
<a href="{{ url_for('.fetch_list') }}" class="nav-link" role="menuitem">
|
<a href="{{ url_for('.fetch_list') }}" class="nav-link" role="menuitem">
|
||||||
<i class="nav-icon fas fa-download"></i>
|
<i class="nav-icon fas fa-download"></i>
|
||||||
<p>{% trans %}Fetched accounts{% endtrans %}</p>
|
<p>{% trans %}Fetched accounts{% endtrans %}</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{%- endif %}
|
||||||
<li class="nav-item" role="none">
|
<li class="nav-item" role="none">
|
||||||
<a href="{{ url_for('.token_list') }}" class="nav-link" role="menuitem">
|
<a href="{{ url_for('.token_list') }}" class="nav-link" role="menuitem">
|
||||||
<i class="nav-icon fas fa-ticket-alt"></i>
|
<i class="nav-icon fas fa-ticket-alt"></i>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mailu import models, utils
|
from mailu import models, utils
|
||||||
from mailu.ui import ui, forms, access
|
from mailu.ui import ui, forms, access
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
import flask_login
|
import flask_login
|
||||||
@ -10,6 +11,8 @@ import wtforms
|
|||||||
@ui.route('/fetch/list/<path:user_email>', methods=['GET'])
|
@ui.route('/fetch/list/<path:user_email>', methods=['GET'])
|
||||||
@access.owner(models.User, 'user_email')
|
@access.owner(models.User, 'user_email')
|
||||||
def fetch_list(user_email):
|
def fetch_list(user_email):
|
||||||
|
if not app.config['FETCHMAIL_ENABLED']:
|
||||||
|
flask.abort(404)
|
||||||
user_email = user_email or flask_login.current_user.email
|
user_email = user_email or flask_login.current_user.email
|
||||||
user = models.User.query.get(user_email) or flask.abort(404)
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
return flask.render_template('fetch/list.html', user=user)
|
return flask.render_template('fetch/list.html', user=user)
|
||||||
@ -19,6 +22,8 @@ def fetch_list(user_email):
|
|||||||
@ui.route('/fetch/create/<path:user_email>', methods=['GET', 'POST'])
|
@ui.route('/fetch/create/<path:user_email>', methods=['GET', 'POST'])
|
||||||
@access.owner(models.User, 'user_email')
|
@access.owner(models.User, 'user_email')
|
||||||
def fetch_create(user_email):
|
def fetch_create(user_email):
|
||||||
|
if not app.config['FETCHMAIL_ENABLED']:
|
||||||
|
flask.abort(404)
|
||||||
user_email = user_email or flask_login.current_user.email
|
user_email = user_email or flask_login.current_user.email
|
||||||
user = models.User.query.get(user_email) or flask.abort(404)
|
user = models.User.query.get(user_email) or flask.abort(404)
|
||||||
form = forms.FetchForm()
|
form = forms.FetchForm()
|
||||||
@ -40,6 +45,8 @@ def fetch_create(user_email):
|
|||||||
@ui.route('/fetch/edit/<fetch_id>', methods=['GET', 'POST'])
|
@ui.route('/fetch/edit/<fetch_id>', methods=['GET', 'POST'])
|
||||||
@access.owner(models.Fetch, 'fetch_id')
|
@access.owner(models.Fetch, 'fetch_id')
|
||||||
def fetch_edit(fetch_id):
|
def fetch_edit(fetch_id):
|
||||||
|
if not app.config['FETCHMAIL_ENABLED']:
|
||||||
|
flask.abort(404)
|
||||||
fetch = models.Fetch.query.get(fetch_id) or flask.abort(404)
|
fetch = models.Fetch.query.get(fetch_id) or flask.abort(404)
|
||||||
form = forms.FetchForm(obj=fetch)
|
form = forms.FetchForm(obj=fetch)
|
||||||
utils.formatCSVField(form.folders)
|
utils.formatCSVField(form.folders)
|
||||||
@ -61,6 +68,8 @@ def fetch_edit(fetch_id):
|
|||||||
@access.confirmation_required("delete a fetched account")
|
@access.confirmation_required("delete a fetched account")
|
||||||
@access.owner(models.Fetch, 'fetch_id')
|
@access.owner(models.Fetch, 'fetch_id')
|
||||||
def fetch_delete(fetch_id):
|
def fetch_delete(fetch_id):
|
||||||
|
if not app.config['FETCHMAIL_ENABLED']:
|
||||||
|
flask.abort(404)
|
||||||
fetch = models.Fetch.query.get(fetch_id) or flask.abort(404)
|
fetch = models.Fetch.query.get(fetch_id) or flask.abort(404)
|
||||||
user = fetch.user
|
user = fetch.user
|
||||||
models.db.session.delete(fetch)
|
models.db.session.delete(fetch)
|
||||||
|
@ -64,6 +64,7 @@ def user_edit(user_email):
|
|||||||
form.quota_bytes.validators = [
|
form.quota_bytes.validators = [
|
||||||
wtforms.validators.NumberRange(max=max_quota_bytes)]
|
wtforms.validators.NumberRange(max=max_quota_bytes)]
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
if form.pw.data:
|
||||||
if msg := utils.isBadOrPwned(form):
|
if msg := utils.isBadOrPwned(form):
|
||||||
flask.flash(msg, "error")
|
flask.flash(msg, "error")
|
||||||
return flask.render_template('user/edit.html', form=form, user=user,
|
return flask.render_template('user/edit.html', form=form, user=user,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""empty message
|
""" Add fetch.scan and fetch.folders
|
||||||
|
|
||||||
Revision ID: f4f0f89e0047
|
Revision ID: f4f0f89e0047
|
||||||
Revises: 8f9ea78776f4
|
Revises: 8f9ea78776f4
|
||||||
|
@ -73,7 +73,7 @@ ENV \
|
|||||||
DEBUG="true" \
|
DEBUG="true" \
|
||||||
DEBUG_PROFILER="${DEV_PROFILER}" \
|
DEBUG_PROFILER="${DEV_PROFILER}" \
|
||||||
DEBUG_ASSETS="/app/static" \
|
DEBUG_ASSETS="/app/static" \
|
||||||
DEBUG_TB_ENABLED="true" \
|
DEBUG_TB_INTERCEPT_REDIRECTS=False \
|
||||||
\
|
\
|
||||||
IMAP_ADDRESS="127.0.0.1" \
|
IMAP_ADDRESS="127.0.0.1" \
|
||||||
POP3_ADDRESS="127.0.0.1" \
|
POP3_ADDRESS="127.0.0.1" \
|
||||||
|
@ -12,7 +12,16 @@ ARG MAILU_GID=1000
|
|||||||
RUN set -euxo pipefail \
|
RUN set -euxo pipefail \
|
||||||
; addgroup -Sg ${MAILU_GID} mailu \
|
; addgroup -Sg ${MAILU_GID} mailu \
|
||||||
; adduser -Sg ${MAILU_UID} -G mailu -h /app -g "mailu app" -s /bin/bash mailu \
|
; adduser -Sg ${MAILU_UID} -G mailu -h /app -g "mailu app" -s /bin/bash mailu \
|
||||||
; apk add --no-cache bash ca-certificates curl python3 tzdata
|
; apk add --no-cache bash ca-certificates curl python3 tzdata \
|
||||||
|
; machine="$(uname -m)" \
|
||||||
|
; ! [[ "${machine}" == x86_64 ]] \
|
||||||
|
|| apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing hardened-malloc
|
||||||
|
|
||||||
|
ENV LD_PRELOAD=/usr/lib/libhardened_malloc.so
|
||||||
|
ENV CXXFLAGS="-g -O2 -fdebug-prefix-map=/app=. -fstack-protector-strong -Wformat -Werror=format-security -fstack-clash-protection -fexceptions"
|
||||||
|
ENV CFLAGS="-g -O2 -fdebug-prefix-map=/app=. -fstack-protector-strong -Wformat -Werror=format-security -fstack-clash-protection -fexceptions"
|
||||||
|
ENV CPPFLAGS="-Wdate-time -D_FORTIFY_SOURCE=2"
|
||||||
|
ENV LDFLAGS="-Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
@ -104,6 +104,9 @@ support or e.g. mismatching TLS versions to deliver emails to Mailu.
|
|||||||
|
|
||||||
.. _fetchmail:
|
.. _fetchmail:
|
||||||
|
|
||||||
|
When ``FETCHMAIL_ENABLED`` is set to ``True``, the fetchmail functionality is enabled in the admin interface.
|
||||||
|
The container itself still needs to be deployed manually. ``FETCHMAIL_ENABLED`` defaults to ``True``.
|
||||||
|
|
||||||
The ``FETCHMAIL_DELAY`` is a delay (in seconds) for the fetchmail service to
|
The ``FETCHMAIL_DELAY`` is a delay (in seconds) for the fetchmail service to
|
||||||
go and fetch new email if available. Do not use too short delays if you do not
|
go and fetch new email if available. Do not use too short delays if you do not
|
||||||
want to be blacklisted by external services, but not too long delays if you
|
want to be blacklisted by external services, but not too long delays if you
|
||||||
@ -287,6 +290,10 @@ The admin service stores configurations in a database.
|
|||||||
- ``DB_USER``: the database user for mailu admin service. (when not ``sqlite``)
|
- ``DB_USER``: the database user for mailu admin service. (when not ``sqlite``)
|
||||||
- ``DB_NAME``: the database name for mailu admin service. (when not ``sqlite``)
|
- ``DB_NAME``: the database name for mailu admin service. (when not ``sqlite``)
|
||||||
|
|
||||||
|
Alternatively, if you need more control, you can use a `DB URL`_ : do not set any of the ``DB_`` settings and set ``SQLALCHEMY_DATABASE_URI`` instead.
|
||||||
|
|
||||||
|
.. _`DB URL`: https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
|
||||||
|
|
||||||
The roundcube service stores configurations in a database.
|
The roundcube service stores configurations in a database.
|
||||||
|
|
||||||
- ``ROUNDCUBE_DB_FLAVOR``: the database type for roundcube service. (``sqlite``, ``postgresql``, ``mysql``)
|
- ``ROUNDCUBE_DB_FLAVOR``: the database type for roundcube service. (``sqlite``, ``postgresql``, ``mysql``)
|
||||||
|
@ -119,6 +119,13 @@ if __name__ == "__main__":
|
|||||||
os.setgid(id_fetchmail.pw_gid)
|
os.setgid(id_fetchmail.pw_gid)
|
||||||
os.setuid(id_fetchmail.pw_uid)
|
os.setuid(id_fetchmail.pw_uid)
|
||||||
while True:
|
while True:
|
||||||
time.sleep(int(os.environ.get("FETCHMAIL_DELAY", 60)))
|
delay = int(os.environ.get("FETCHMAIL_DELAY", 60))
|
||||||
|
print("Sleeping for {} seconds".format(delay))
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
|
if not os.environ.get("FETCHMAIL_ENABLED", 'True') in ('True', 'true'):
|
||||||
|
print("Fetchmail disabled, skipping...")
|
||||||
|
continue
|
||||||
|
|
||||||
run(os.environ.get("DEBUG", None) == "True")
|
run(os.environ.get("DEBUG", None) == "True")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
@ -79,6 +79,9 @@ RELAYNETS=
|
|||||||
# Will relay all outgoing mails if configured
|
# Will relay all outgoing mails if configured
|
||||||
RELAYHOST={{ relayhost }}
|
RELAYHOST={{ relayhost }}
|
||||||
|
|
||||||
|
# Show fetchmail functionality in admin interface
|
||||||
|
FETCHMAIL_ENABLED={{ fetchmail_enabled or 'False' }}
|
||||||
|
|
||||||
# Fetchmail delay
|
# Fetchmail delay
|
||||||
FETCHMAIL_DELAY={{ fetchmail_delay or '600' }}
|
FETCHMAIL_DELAY={{ fetchmail_delay or '600' }}
|
||||||
|
|
||||||
|
1
towncrier/newsfragments/2127.feature
Normal file
1
towncrier/newsfragments/2127.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add FETCHMAIL_ENABLED to toggle the fetchmail functionality in the admin interface
|
1
towncrier/newsfragments/2525.feature
Normal file
1
towncrier/newsfragments/2525.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Switch to GrapheneOS's hardened_malloc
|
Loading…
Reference in New Issue
Block a user