mirror of
https://github.com/chatmail/relay.git
synced 2026-05-14 18:04:38 +00:00
Compare commits
1 Commits
fix_metric
...
metrics
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4385b84139 |
@@ -17,8 +17,8 @@ max_user_send_per_minute = 60
|
|||||||
# maximum mailbox size of a chatmail account
|
# maximum mailbox size of a chatmail account
|
||||||
max_mailbox_size = 100M
|
max_mailbox_size = 100M
|
||||||
|
|
||||||
# days after which mails are unconditionally deleted
|
# time after which seen mails are deleted
|
||||||
delete_mails_after = 40
|
delete_mails_after = 40d
|
||||||
|
|
||||||
# minimum length a username must have
|
# minimum length a username must have
|
||||||
username_min_length = 9
|
username_min_length = 9
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def main(vmail_dir=None):
|
def main():
|
||||||
if vmail_dir is None:
|
vmail_dir = sys.argv[1]
|
||||||
vmail_dir = sys.argv[1]
|
|
||||||
|
|
||||||
accounts = 0
|
accounts = 0
|
||||||
ci_accounts = 0
|
ci_accounts = 0
|
||||||
|
|
||||||
for path in Path(vmail_dir).iterdir():
|
for path in Path(vmail_dir).iterdir():
|
||||||
accounts += 1
|
accounts += 1
|
||||||
if path.name[:3] in ("ci-", "ac_"):
|
if path.name.startswith("ci-"):
|
||||||
ci_accounts += 1
|
ci_accounts += 1
|
||||||
|
|
||||||
timestamp = int(time.time() * 1000)
|
timestamp = int(time.time() * 1000)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def test_read_config_testrun(make_config):
|
|||||||
assert config.postfix_reinject_port == 10025
|
assert config.postfix_reinject_port == 10025
|
||||||
assert config.max_user_send_per_minute == 60
|
assert config.max_user_send_per_minute == 60
|
||||||
assert config.max_mailbox_size == "100M"
|
assert config.max_mailbox_size == "100M"
|
||||||
assert config.delete_mails_after == "40"
|
assert config.delete_mails_after == "40d"
|
||||||
assert config.username_min_length == 9
|
assert config.username_min_length == 9
|
||||||
assert config.username_max_length == 9
|
assert config.username_max_length == 9
|
||||||
assert config.password_min_length == 9
|
assert config.password_min_length == 9
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
from chatmaild.metrics import main
|
|
||||||
|
|
||||||
|
|
||||||
def test_main(tmp_path, capsys):
|
|
||||||
for x in ("ci-asllkj", "ac_12l3kj", "qweqwe", "ci-l1k2j31l2k3"):
|
|
||||||
tmp_path.joinpath(x).mkdir()
|
|
||||||
main(tmp_path)
|
|
||||||
out, _ = capsys.readouterr()
|
|
||||||
d = {}
|
|
||||||
for line in out.split("\n"):
|
|
||||||
if line.strip():
|
|
||||||
name, num, _ = line.split()
|
|
||||||
d[name] = int(num)
|
|
||||||
|
|
||||||
assert d["accounts"] == 4
|
|
||||||
assert d["ci_accounts"] == 3
|
|
||||||
@@ -126,7 +126,7 @@ def _install_remote_venv_with_chatmaild(config) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
def _configure_opendkim(domain: str, dkim_selector: str) -> bool:
|
||||||
"""Configures OpenDKIM"""
|
"""Configures OpenDKIM"""
|
||||||
need_restart = False
|
need_restart = False
|
||||||
|
|
||||||
@@ -364,22 +364,20 @@ def check_config(config):
|
|||||||
if mail_domain != "testrun.org" and not mail_domain.endswith(".testrun.org"):
|
if mail_domain != "testrun.org" and not mail_domain.endswith(".testrun.org"):
|
||||||
blocked_words = "merlinux schmieder testrun.org".split()
|
blocked_words = "merlinux schmieder testrun.org".split()
|
||||||
for value in config.__dict__.values():
|
for value in config.__dict__.values():
|
||||||
if any(x in str(value) for x in blocked_words):
|
if any(x in value for x in blocked_words):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"please set your own privacy contacts/addresses in {config._inipath}"
|
f"please set your own privacy contacts/addresses in {config._inipath}"
|
||||||
)
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def deploy_chatmail(config_path: Path) -> None:
|
def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> None:
|
||||||
"""Deploy a chat-mail instance.
|
"""Deploy a chat-mail instance.
|
||||||
|
|
||||||
:param config_path: path to chatmail.ini
|
:param mail_domain: domain part of your future email addresses
|
||||||
|
:param mail_server: the DNS name under which your mail server is reachable
|
||||||
|
:param dkim_selector:
|
||||||
"""
|
"""
|
||||||
config = read_config(config_path)
|
|
||||||
check_config(config)
|
|
||||||
mail_domain = config.mail_domain
|
|
||||||
|
|
||||||
from .www import build_webpages
|
from .www import build_webpages
|
||||||
|
|
||||||
apt.update(name="apt update", cache_time=24 * 3600)
|
apt.update(name="apt update", cache_time=24 * 3600)
|
||||||
@@ -409,7 +407,7 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Deploy acmetool to have TLS certificates.
|
# Deploy acmetool to have TLS certificates.
|
||||||
deploy_acmetool(nginx_hook=True, domains=[mail_domain, f"mta-sts.{mail_domain}"])
|
deploy_acmetool(nginx_hook=True, domains=[mail_server, f"mta-sts.{mail_server}"])
|
||||||
|
|
||||||
apt.packages(
|
apt.packages(
|
||||||
name="Install Postfix",
|
name="Install Postfix",
|
||||||
@@ -439,7 +437,11 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
packages=["fcgiwrap"],
|
packages=["fcgiwrap"],
|
||||||
)
|
)
|
||||||
|
|
||||||
www_path = importlib.resources.files(__package__).joinpath("../../../www").resolve()
|
pkg_root = importlib.resources.files(__package__)
|
||||||
|
chatmail_ini = pkg_root.joinpath("../../../chatmail.ini").resolve()
|
||||||
|
config = read_config(chatmail_ini)
|
||||||
|
check_config(config)
|
||||||
|
www_path = pkg_root.joinpath("../../../www").resolve()
|
||||||
|
|
||||||
build_dir = www_path.joinpath("build")
|
build_dir = www_path.joinpath("build")
|
||||||
src_dir = www_path.joinpath("src")
|
src_dir = www_path.joinpath("src")
|
||||||
@@ -450,7 +452,7 @@ def deploy_chatmail(config_path: Path) -> None:
|
|||||||
debug = False
|
debug = False
|
||||||
dovecot_need_restart = _configure_dovecot(config, debug=debug)
|
dovecot_need_restart = _configure_dovecot(config, debug=debug)
|
||||||
postfix_need_restart = _configure_postfix(config, debug=debug)
|
postfix_need_restart = _configure_postfix(config, debug=debug)
|
||||||
opendkim_need_restart = _configure_opendkim(mail_domain)
|
opendkim_need_restart = _configure_opendkim(mail_domain, dkim_selector)
|
||||||
mta_sts_need_restart = _install_mta_sts_daemon()
|
mta_sts_need_restart = _install_mta_sts_daemon()
|
||||||
nginx_need_restart = _configure_nginx(mail_domain)
|
nginx_need_restart = _configure_nginx(mail_domain)
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ def run_cmd(args, out):
|
|||||||
"""Deploy chatmail services on the remote server."""
|
"""Deploy chatmail services on the remote server."""
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
env["CHATMAIL_INI"] = args.inipath
|
env["CHATMAIL_DOMAIN"] = args.config.mail_domain
|
||||||
deploy_path = importlib.resources.files(__package__).joinpath("deploy.py").resolve()
|
deploy_path = "cmdeploy/src/cmdeploy/deploy.py"
|
||||||
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
|
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
|
||||||
cmd = f"{pyinf} --ssh-user root {args.config.mail_domain} {deploy_path}"
|
cmd = f"{pyinf} --ssh-user root {args.config.mail_domain} {deploy_path}"
|
||||||
out.check_call(cmd, env=env)
|
out.check_call(cmd, env=env)
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import os
|
import os
|
||||||
import importlib.resources
|
|
||||||
import pyinfra
|
import pyinfra
|
||||||
from cmdeploy import deploy_chatmail
|
from cmdeploy import deploy_chatmail
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
config_path = os.getenv(
|
mail_domain = os.getenv("CHATMAIL_DOMAIN")
|
||||||
"CHATMAIL_INI",
|
mail_server = os.getenv("CHATMAIL_SERVER", mail_domain)
|
||||||
importlib.resources.files("cmdeploy").joinpath("../../../chatmail.ini"),
|
dkim_selector = os.getenv("CHATMAIL_DKIM_SELECTOR", "dkim")
|
||||||
)
|
|
||||||
|
|
||||||
deploy_chatmail(config_path)
|
assert mail_domain
|
||||||
|
assert mail_server
|
||||||
|
assert dkim_selector
|
||||||
|
|
||||||
|
deploy_chatmail(mail_domain, mail_server, dkim_selector)
|
||||||
|
|
||||||
|
|
||||||
if pyinfra.is_cli:
|
if pyinfra.is_cli:
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
# delete all mails after {{ config.delete_mails_after }} days, in the Inbox
|
2 0 * * * dovecot doveadm expunge -A SEEN BEFORE {{ config.delete_mails_after }} INBOX
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/cur -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 0 * * * dovecot doveadm expunge -A SEEN BEFORE {{ config.delete_mails_after }} Deltachat
|
||||||
# or in any IMAP subfolder
|
2 0 * * * dovecot doveadm expunge -A SEEN BEFORE {{ config.delete_mails_after }} Trash
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/cur -mtime +{{ config.delete_mails_after }} -type f -delete
|
2 30 * * * dovecot doveadm purge -A
|
||||||
# even if they are unseen
|
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/new -mtime +{{ config.delete_mails_after }} -type f -delete
|
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/new -mtime +{{ config.delete_mails_after }} -type f -delete
|
|
||||||
# or only temporary (but then they shouldn't be around after {{ config.delete_mails_after }} days anyway).
|
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/tmp -mtime +{{ config.delete_mails_after }} -type f -delete
|
|
||||||
2 0 * * * dovecot find /home/vmail/mail/{{ config.mail_domain }}/*/.*/tmp -mtime +{{ config.delete_mails_after }} -type f -delete
|
|
||||||
|
|||||||
@@ -36,6 +36,29 @@ def build_webpages(src_dir, build_dir, config):
|
|||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
def timespan_to_english(timespan):
|
||||||
|
val = int(timespan[:-1])
|
||||||
|
c = timespan[-1].lower()
|
||||||
|
match c:
|
||||||
|
case "y":
|
||||||
|
return f"{val} years"
|
||||||
|
case "m":
|
||||||
|
return f"{val} months"
|
||||||
|
case "w":
|
||||||
|
return f"{val} weeks"
|
||||||
|
case "d":
|
||||||
|
return f"{val} days"
|
||||||
|
case "h":
|
||||||
|
return f"{val} hours"
|
||||||
|
case "c":
|
||||||
|
return f"{val} seconds"
|
||||||
|
case _:
|
||||||
|
raise ValueError(
|
||||||
|
c
|
||||||
|
+ " is not a valid time unit. Try [y]ears, [w]eeks, [d]ays, or [h]ours"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def int_to_english(number):
|
def int_to_english(number):
|
||||||
if number >= 0 and number <= 12:
|
if number >= 0 and number <= 12:
|
||||||
a = [
|
a = [
|
||||||
@@ -81,6 +104,9 @@ def _build_webpages(src_dir, build_dir, config):
|
|||||||
render_vars["password_min_length"] = int_to_english(
|
render_vars["password_min_length"] = int_to_english(
|
||||||
config.password_min_length
|
config.password_min_length
|
||||||
)
|
)
|
||||||
|
render_vars["delete_mails_after"] = timespan_to_english(
|
||||||
|
config.delete_mails_after
|
||||||
|
)
|
||||||
target = build_dir.joinpath(path.stem + ".html")
|
target = build_dir.joinpath(path.stem + ".html")
|
||||||
|
|
||||||
# recursive jinja2 rendering
|
# recursive jinja2 rendering
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ The first login sets your password.
|
|||||||
|
|
||||||
- You may send up to {{ config.max_user_send_per_minute }} messages per minute.
|
- You may send up to {{ config.max_user_send_per_minute }} messages per minute.
|
||||||
|
|
||||||
- Messages are unconditionally removed {{ config.delete_mails_after }} days after arriving on the server.
|
- Seen messages are removed {{ delete_mails_after }} after arriving on the server.
|
||||||
|
|
||||||
- You can store up to [{{ config.max_mailbox_size }} messages on the server](https://delta.chat/en/help#what-happens-if-i-turn-on-delete-old-messages-from-server).
|
- You can store up to [{{ config.max_mailbox_size }} messages on the server](https://delta.chat/en/help#what-happens-if-i-turn-on-delete-old-messages-from-server).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user