Compare commits

..

1 Commits

Author SHA1 Message Date
link2xt
2b806971a3 Avoid datetime overflow, cache deb for a year instead of 300k years 2025-07-08 17:53:29 +00:00
19 changed files with 54 additions and 290 deletions

View File

@@ -70,6 +70,9 @@ jobs:
rsync -avz dkimkeys-restore/dkimkeys root@staging-ipv4.testrun.org:/etc/ || true rsync -avz dkimkeys-restore/dkimkeys root@staging-ipv4.testrun.org:/etc/ || true
ssh -o StrictHostKeyChecking=accept-new -v root@staging-ipv4.testrun.org chown root:root -R /var/lib/acme || true ssh -o StrictHostKeyChecking=accept-new -v root@staging-ipv4.testrun.org chown root:root -R /var/lib/acme || true
- name: run formatting checks
run: cmdeploy fmt -v
- name: run deploy-chatmail offline tests - name: run deploy-chatmail offline tests
run: pytest --pyargs cmdeploy run: pytest --pyargs cmdeploy

View File

@@ -70,6 +70,9 @@ jobs:
rsync -avz dkimkeys-restore/dkimkeys root@staging2.testrun.org:/etc/ || true rsync -avz dkimkeys-restore/dkimkeys root@staging2.testrun.org:/etc/ || true
ssh -o StrictHostKeyChecking=accept-new -v root@staging2.testrun.org chown root:root -R /var/lib/acme || true ssh -o StrictHostKeyChecking=accept-new -v root@staging2.testrun.org chown root:root -R /var/lib/acme || true
- name: run formatting checks
run: cmdeploy fmt -v
- name: run deploy-chatmail offline tests - name: run deploy-chatmail offline tests
run: pytest --pyargs cmdeploy run: pytest --pyargs cmdeploy

View File

@@ -1,50 +0,0 @@
This diagram shows components of the chatmail server; this is a draft
overview as of mid-August 2025:
```mermaid
graph LR;
cmdeploy --- sshd;
letsencrypt --- |80|acmetool-redirector;
acmetool-redirector --- |443|nginx-right(["`nginx
(external)`"]);
nginx-external --- |465|postfix;
nginx-external(["`nginx
(external)`"]) --- |8443|nginx-internal["`nginx
(internal)`"];
nginx-internal --- website["`Website
/var/www/html`"];
nginx-internal --- newemail.py;
nginx-internal --- autoconfig.xml;
certs-nginx[("`TLS certs
/var/lib/acme`")] --> nginx-internal;
cron --- chatmail-metrics;
cron --- acmetool;
cron --- expunge;
chatmail-metrics --- website;
acmetool --> certs[("`TLS certs
/var/lib/acme`")];
nginx-external --- |993|dovecot;
autoconfig.xml --- postfix;
autoconfig.xml --- dovecot;
postfix --- echobot;
postfix --- |10080,10081|filtermail;
postfix --- users["`User data
home/vmail/mail`"];
postfix --- |doveauth.socket|doveauth;
dovecot --- |doveauth.socket|doveauth;
dovecot --- users;
dovecot --- |metadata.socket|chatmail-metadata;
doveauth --- users;
expunge --- users;
chatmail-metadata --- iroh-relay;
certs-nginx --> postfix;
certs-nginx --> dovecot;
style certs fill:#ff6;
style certs-nginx fill:#ff6;
style nginx-external fill:#fc9;
style nginx-right fill:#fc9;
```
The edges in this graph should not be taken too literally; they
reflect some sort of communication path or dependency relationship
between components of the chatmail server.

View File

@@ -2,21 +2,6 @@
## untagged ## untagged
- Enable invite-only chatmail relays with invite tokens
that can override disabled account creation
([#600](https://github.com/chatmail/relay/pull/600))
- dovecot: keep mailbox index only in memory to avoid unnecessary disc usage
([#632](https://github.com/chatmail/relay/pull/632))
## 1.7.0 2025-09-11
- Make www upload path configurable
([#618](https://github.com/chatmail/relay/pull/618))
- Check whether GCC is installed in initenv.sh
([#608](https://github.com/chatmail/relay/pull/608))
- Expire push notification tokens after 90 days - Expire push notification tokens after 90 days
([#583](https://github.com/chatmail/relay/pull/583)) ([#583](https://github.com/chatmail/relay/pull/583))
@@ -29,9 +14,6 @@
- Reconfigure Dovecot imap-login service to high-performance mode - Reconfigure Dovecot imap-login service to high-performance mode
([#578](https://github.com/chatmail/relay/pull/578)) ([#578](https://github.com/chatmail/relay/pull/578))
- Set timezone to improve dovecot performance
([#584](https://github.com/chatmail/relay/pull/584))
- Increase nginx connection limits - Increase nginx connection limits
([#576](https://github.com/chatmail/relay/pull/576)) ([#576](https://github.com/chatmail/relay/pull/576))
@@ -41,9 +23,6 @@
- filtermail: respect config message size limit - filtermail: respect config message size limit
([#572](https://github.com/chatmail/relay/pull/572)) ([#572](https://github.com/chatmail/relay/pull/572))
- Don't deploy if one of the ports used for chatmail relay services is occupied by an unexpected process
([#568](https://github.com/chatmail/relay/pull/568))
- Add config value after how many days large files are deleted - Add config value after how many days large files are deleted
([#555](https://github.com/chatmail/relay/pull/555)) ([#555](https://github.com/chatmail/relay/pull/555))

View File

@@ -255,18 +255,6 @@ This starts a local live development cycle for chatmail web pages:
- Starts a browser window automatically where you can "refresh" as needed. - Starts a browser window automatically where you can "refresh" as needed.
#### Custom web pages
You can skip uploading a web page
by setting `www_folder=disabled` in `chatmail.ini`.
If you want to manage your web pages outside this git repository,
you can set `www_folder` in `chatmail.ini` to a custom directory on your computer.
`cmdeploy run` will upload it as the server's home page,
and if it contains a `src/index.md` file,
will build it with hugo.
## Mailbox directory layout ## Mailbox directory layout
Fresh chatmail addresses have a mailbox directory that contains: Fresh chatmail addresses have a mailbox directory that contains:
@@ -284,23 +272,8 @@ Fresh chatmail addresses have a mailbox directory that contains:
will typically be empty unless the user of that address hasn't been online will typically be empty unless the user of that address hasn't been online
for a while. for a while.
## Restrict address creation
### Only allow new addresses with an invite token ## Emergency Commands to disable automatic address creation
To restrict address creation for anyone who doesn't have the invite link/QR code:
1. Use the `invite_token` option to add
one or more tokens of your choice to `chatmail.ini`:
`invite_token = s3cr3t privil3g3`
- (recommendation: choose 9 or more letters, or it will be easily bruteforced)
2. Run `scripts/cmdeploy run`
3. Distribute a `dcaccount` invite link/QR code
(like the one on your web page)
with one of your invite tokens added at the end,
for example: `dcaccount:https://example.org/new?s3cr3t`
### Emergency Command to disable automatic address creation
If you need to stop address creation, If you need to stop address creation,
e.g. because some script is wildly creating addresses, e.g. because some script is wildly creating addresses,
@@ -568,6 +541,3 @@ Here are some related projects that you may be interested in:
progress](https://github.com/mjl-/mox/issues/251) to modify it to support all progress](https://github.com/mjl-/mox/issues/251) to modify it to support all
of the features and configuration settings required to operate as a chatmail of the features and configuration settings required to operate as a chatmail
relay. relay.
- [Maddy-Chatmail](https://github.com/sadraiiali/maddy_chatmail): a plugin for the
[Maddy email server](https://maddy.email/) which aims to implement the
chatmail relay features and configuration options.

View File

@@ -31,10 +31,8 @@ class Config:
self.username_min_length = int(params["username_min_length"]) self.username_min_length = int(params["username_min_length"])
self.username_max_length = int(params["username_max_length"]) self.username_max_length = int(params["username_max_length"])
self.password_min_length = int(params["password_min_length"]) self.password_min_length = int(params["password_min_length"])
self.invite_token = params.get("invite_token", "")
self.passthrough_senders = params["passthrough_senders"].split() self.passthrough_senders = params["passthrough_senders"].split()
self.passthrough_recipients = params["passthrough_recipients"].split() self.passthrough_recipients = params["passthrough_recipients"].split()
self.www_folder = params.get("www_folder", "")
self.filtermail_smtp_port = int(params["filtermail_smtp_port"]) self.filtermail_smtp_port = int(params["filtermail_smtp_port"])
self.filtermail_smtp_port_incoming = int( self.filtermail_smtp_port_incoming = int(
params["filtermail_smtp_port_incoming"] params["filtermail_smtp_port_incoming"]

View File

@@ -26,19 +26,8 @@ def is_allowed_to_create(config: Config, user, cleartext_password) -> bool:
if os.path.exists(NOCREATE_FILE): if os.path.exists(NOCREATE_FILE):
logging.warning(f"blocked account creation because {NOCREATE_FILE!r} exists.") logging.warning(f"blocked account creation because {NOCREATE_FILE!r} exists.")
return False return False
password_length = len(cleartext_password)
if config.invite_token:
for inv_token in config.invite_token.split():
if cleartext_password.startswith(inv_token):
password_length = len(cleartext_password) - len(inv_token)
break
else:
logging.warning(
"blocked account creation because password didn't contain invite token(s)."
)
return False
if password_length < config.password_min_length: if len(cleartext_password) < config.password_min_length:
logging.warning( logging.warning(
"Password needs to be at least %s characters long", "Password needs to be at least %s characters long",
config.password_min_length, config.password_min_length,

View File

@@ -3,7 +3,6 @@
"""CGI script for creating new accounts.""" """CGI script for creating new accounts."""
import json import json
import os
import random import random
import secrets import secrets
import string import string
@@ -21,11 +20,7 @@ def create_newemail_dict(config: Config):
secrets.choice(ALPHANUMERIC_PUNCT) secrets.choice(ALPHANUMERIC_PUNCT)
for _ in range(config.password_min_length + 3) for _ in range(config.password_min_length + 3)
) )
redirect_uri = os.getenv("REQUEST_URI", "/new") return dict(email=f"{user}@{config.mail_domain}", password=f"{password}")
invite_token = "" if redirect_uri == "/new" else redirect_uri[5:]
return dict(
email=f"{user}@{config.mail_domain}", password=f"{invite_token}{password}"
)
def print_new_account(): def print_new_account():

View File

@@ -64,38 +64,12 @@ def test_dont_overwrite_password_on_wrong_login(dictproxy):
assert res["password"] == res2["password"] assert res["password"] == res2["password"]
@pytest.mark.parametrize( def test_nocreate_file(monkeypatch, tmpdir, dictproxy):
["nocreate_file", "account", "invite_token", "password"], p = tmpdir.join("nocreate")
[ p.write("")
(False, True, "asdf", "asdfasdmaimfelsgwerw"), monkeypatch.setattr(chatmaild.doveauth, "NOCREATE_FILE", str(p))
(False, False, "asdf", "z9873240187420913798"), dictproxy.lookup_passdb("newuser12@chat.example.org", "zequ0Aimuchoodaechik")
(False, True, "", "dsaiujfw9fjiwf9w"), assert not dictproxy.lookup_userdb("newuser12@chat.example.org")
(False, False, "asdf", "z987324018742asdf0913798"),
(False, True, "as df", "asj0wiefkj0ofkeefok"),
(False, True, "as df", "dfj0wiefkj0ofkeefok"),
(False, False, "as df", "j0wiefkj0ofas dfkeefok"),
(True, False, "asdf", "asdfmosadkdkfwdofkw"),
(True, False, "asdf", "z9873240187420913798"),
(True, False, "", "dsaiujfw9fjiwf9w"),
],
)
def test_nocreate_file(
monkeypatch,
tmpdir,
dictproxy,
example_config,
nocreate_file: bool,
account: bool,
invite_token: str,
password: str,
):
if nocreate_file:
p = tmpdir.join("nocreate")
p.write("")
monkeypatch.setattr(chatmaild.doveauth, "NOCREATE_FILE", str(p))
example_config.invite_token = invite_token
dictproxy.lookup_passdb("newuser12@chat.example.org", password)
assert bool(dictproxy.lookup_userdb("newuser12@chat.example.org")) == account
def test_handle_dovecot_request(dictproxy): def test_handle_dovecot_request(dictproxy):

View File

@@ -11,7 +11,7 @@ from io import StringIO
from pathlib import Path from pathlib import Path
from chatmaild.config import Config, read_config from chatmaild.config import Config, read_config
from pyinfra import facts, host, logger from pyinfra import facts, host
from pyinfra.api import FactBase from pyinfra.api import FactBase
from pyinfra.facts.files import File from pyinfra.facts.files import File
from pyinfra.facts.server import Sysctl from pyinfra.facts.server import Sysctl
@@ -346,7 +346,7 @@ def _install_dovecot_package(package: str, arch: str):
src=url, src=url,
dest=deb_filename, dest=deb_filename,
sha256sum=sha256, sha256sum=sha256,
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package cache_time=60 * 60 * 24 * 365, # cache the .deb for a year,
) )
apt.deb(name=f"Install dovecot-{package}", src=deb_filename) apt.deb(name=f"Install dovecot-{package}", src=deb_filename)
@@ -410,13 +410,6 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
persist=True, persist=True,
) )
timezone_env = files.line(
name="Set TZ environment variable",
path="/etc/environment",
line="TZ=:/etc/localtime",
)
need_restart |= timezone_env.changed
return need_restart return need_restart
@@ -618,7 +611,7 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
check_config(config) check_config(config)
mail_domain = config.mail_domain mail_domain = config.mail_domain
from .www import build_webpages, get_paths from .www import build_webpages
server.group(name="Create vmail group", group="vmail", system=True) server.group(name="Create vmail group", group="vmail", system=True)
server.user(name="Create vmail user", user="vmail", group="vmail", system=True) server.user(name="Create vmail user", user="vmail", group="vmail", system=True)
@@ -675,30 +668,10 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
# to use 127.0.0.1 as the resolver. # to use 127.0.0.1 as the resolver.
from cmdeploy.cmdeploy import Out from cmdeploy.cmdeploy import Out
port_services = [ process_on_53 = host.get_fact(Port, port=53)
(["master", "smtpd"], 25), if process_on_53 not in (None, "unbound"):
("unbound", 53), Out().red(f"Can't install unbound: port 53 is occupied by: {process_on_53}")
("acmetool", 80), exit(1)
("imap-login", 143),
("nginx", 443),
(["master", "smtpd"], 465),
(["master", "smtpd"], 587),
("imap-login", 993),
("iroh-relay", 3340),
("nginx", 8443),
(["master", "smtpd"], config.postfix_reinject_port),
(["master", "smtpd"], config.postfix_reinject_port_incoming),
("filtermail", config.filtermail_smtp_port),
("filtermail", config.filtermail_smtp_port_incoming),
]
for service, port in port_services:
print(f"Checking if port {port} is available for {service}...")
running_service = host.get_fact(Port, port=port)
if running_service:
if running_service not in service:
Out().red(f"Deploy failed: port {port} is occupied by: {running_service}")
exit(1)
apt.packages( apt.packages(
name="Install unbound", name="Install unbound",
packages=["unbound", "unbound-anchor", "dnsutils"], packages=["unbound", "unbound-anchor", "dnsutils"],
@@ -736,10 +709,9 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
packages="postfix", packages="postfix",
) )
if not "dovecot.service" in host.get_fact(SystemdEnabled): _install_dovecot_package("core", host.get_fact(facts.server.Arch))
_install_dovecot_package("core", host.get_fact(facts.server.Arch)) _install_dovecot_package("imapd", host.get_fact(facts.server.Arch))
_install_dovecot_package("imapd", host.get_fact(facts.server.Arch)) _install_dovecot_package("lmtpd", host.get_fact(facts.server.Arch))
_install_dovecot_package("lmtpd", host.get_fact(facts.server.Arch))
apt.packages( apt.packages(
name="Install nginx", name="Install nginx",
@@ -751,16 +723,12 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
packages=["fcgiwrap"], packages=["fcgiwrap"],
) )
www_path, src_dir, build_dir = get_paths(config) www_path = importlib.resources.files(__package__).joinpath("../../../www").resolve()
# if www_folder was set to a non-existing folder, skip upload
if not www_path.is_dir(): build_dir = www_path.joinpath("build")
logger.warning("Building web pages is disabled in chatmail.ini, skipping") src_dir = www_path.joinpath("src")
else: build_webpages(src_dir, build_dir, config)
# if www_folder is a hugo page, build it files.rsync(f"{build_dir}/", "/var/www/html", flags=["-avz"])
if build_dir:
www_path = build_webpages(src_dir, build_dir, config)
# if it is not a hugo page, upload it as is
files.rsync(f"{www_path}/", "/var/www/html", flags=["-avz"])
_install_remote_venv_with_chatmaild(config) _install_remote_venv_with_chatmaild(config)
debug = False debug = False
@@ -840,14 +808,8 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
name="Ensure cron is installed", name="Ensure cron is installed",
packages=["cron"], packages=["cron"],
) )
try: git_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode()
git_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode() git_diff = subprocess.check_output(["git", "diff"]).decode()
except Exception:
git_hash = "unknown\n"
try:
git_diff = subprocess.check_output(["git", "diff"]).decode()
except Exception:
git_diff = ""
files.put( files.put(
name="Upload chatmail relay git commiit hash", name="Upload chatmail relay git commiit hash",
src=StringIO(git_hash + git_diff), src=StringIO(git_hash + git_diff),

View File

@@ -1,5 +1,7 @@
import importlib.resources import importlib.resources
from pyinfra import host
from pyinfra.facts.systemd import SystemdStatus
from pyinfra.operations import apt, files, server, systemd from pyinfra.operations import apt, files, server, systemd
@@ -52,6 +54,12 @@ def deploy_acmetool(email="", domains=[]):
group="root", group="root",
mode="644", mode="644",
) )
if host.get_fact(SystemdStatus).get("nginx.service"):
systemd.service(
name="Stop nginx service to free port 80",
service="nginx",
running=False,
)
systemd.service( systemd.service(
name="Setup acmetool-redirector service", name="Setup acmetool-redirector service",

View File

@@ -89,14 +89,6 @@ def run_cmd(args, out):
try: try:
retcode = out.check_call(cmd, env=env) retcode = out.check_call(cmd, env=env)
if retcode == 0: if retcode == 0:
print("\nYou can try out the relay by talking to this echo bot: ")
sshexec = SSHExec(args.config.mail_domain, verbose=args.verbose)
print(
sshexec(
call=remote.rshell.shell,
kwargs=dict(command="cat /var/lib/echobot/invite-link.txt"),
)
)
out.green("Deploy completed, call `cmdeploy dns` next.") out.green("Deploy completed, call `cmdeploy dns` next.")
elif not remote_data["acme_account_url"]: elif not remote_data["acme_account_url"]:
out.red("Deploy completed but letsencrypt not configured") out.red("Deploy completed but letsencrypt not configured")

View File

@@ -68,7 +68,7 @@ userdb {
## ##
# Mailboxes are stored in the "mail" directory of the vmail user home. # Mailboxes are stored in the "mail" directory of the vmail user home.
mail_location = maildir:{{ config.mailboxes_dir }}/%u:INDEX=MEMORY mail_location = maildir:{{ config.mailboxes_dir }}/%u
namespace inbox { namespace inbox {
inbox = yes inbox = yes

View File

@@ -84,13 +84,12 @@ http {
if ($request_method = GET) { if ($request_method = GET) {
# Redirect to Delta Chat, # Redirect to Delta Chat,
# which will in turn do a POST request. # which will in turn do a POST request.
return 301 dcaccount:https://{{ config.domain_name }}$request_uri; return 301 dcaccount:https://{{ config.domain_name }}/new;
} }
fastcgi_pass unix:/run/fcgiwrap.socket; fastcgi_pass unix:/run/fcgiwrap.socket;
include /etc/nginx/fastcgi_params; include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/cgi-bin/newemail.py; fastcgi_param SCRIPT_FILENAME /usr/lib/cgi-bin/newemail.py;
fastcgi_param QUERY_STRING $query_string;
} }
# Old URL for compatibility with e.g. printed QR codes. # Old URL for compatibility with e.g. printed QR codes.
@@ -101,7 +100,7 @@ http {
# Redirects are only for browsers. # Redirects are only for browsers.
location /cgi-bin/newemail.py { location /cgi-bin/newemail.py {
if ($request_method = GET) { if ($request_method = GET) {
return 301 dcaccount:https://{{ config.domain_name }}$request_uri; return 301 dcaccount:https://{{ config.domain_name }}/new;
} }
fastcgi_pass unix:/run/fcgiwrap.socket; fastcgi_pass unix:/run/fcgiwrap.socket;

View File

@@ -65,14 +65,6 @@ class TestSSHExecutor:
assert (now - since_date).total_seconds() < 60 * 60 * 51 assert (now - since_date).total_seconds() < 60 * 60 * 51
def test_timezone_env(remote):
for line in remote.iter_output("env"):
print(line)
if line == "tz=:/etc/localtime":
return True
pytest.fail("TZ is not set")
def test_remote(remote, imap_or_smtp): def test_remote(remote, imap_or_smtp):
lineproducer = remote.iter_output(imap_or_smtp.logcmd) lineproducer = remote.iter_output(imap_or_smtp.logcmd)
imap_or_smtp.connect() imap_or_smtp.connect()

View File

@@ -1,10 +1,8 @@
import importlib
import os import os
import pytest import pytest
from cmdeploy.cmdeploy import get_parser, main from cmdeploy.cmdeploy import get_parser, main
from cmdeploy.www import get_paths
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -29,28 +27,3 @@ class TestCmdline:
assert main(["init", "chat.example.org"]) == 1 assert main(["init", "chat.example.org"]) == 1
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert "path exists" in out.lower() assert "path exists" in out.lower()
def test_www_folder(example_config, tmp_path):
reporoot = importlib.resources.files(__package__).joinpath("../../../../").resolve()
assert not example_config.www_folder
www_path, src_dir, build_dir = get_paths(example_config)
assert www_path.absolute() == reporoot.joinpath("www").absolute()
assert src_dir == reporoot.joinpath("www").joinpath("src")
assert build_dir == reporoot.joinpath("www").joinpath("build")
example_config.www_folder = "disabled"
www_path, _, _ = get_paths(example_config)
assert not www_path.is_dir()
example_config.www_folder = str(tmp_path)
www_path, src_dir, build_dir = get_paths(example_config)
assert www_path == tmp_path
assert not src_dir.exists()
assert not build_dir
src_path = tmp_path.joinpath("src")
os.mkdir(src_path)
with open(src_path / "index.md", "w") as f:
f.write("# Test")
www_path, src_dir, build_dir = get_paths(example_config)
assert www_path == tmp_path
assert src_dir == src_path
assert build_dir == tmp_path.joinpath("build")

View File

@@ -3,7 +3,6 @@ import importlib.resources
import time import time
import traceback import traceback
import webbrowser import webbrowser
from pathlib import Path
import markdown import markdown
from chatmaild.config import read_config from chatmaild.config import read_config
@@ -31,25 +30,9 @@ def prepare_template(source):
return render_vars, page_layout return render_vars, page_layout
def get_paths(config) -> (Path, Path, Path): def build_webpages(src_dir, build_dir, config):
reporoot = importlib.resources.files(__package__).joinpath("../../../").resolve()
www_path = Path(config.www_folder)
# if www_folder was not set, use default directory
if config.www_folder == "":
www_path = reporoot.joinpath("www")
src_dir = www_path.joinpath("src")
# if www_folder is a hugo page, build it
if src_dir.joinpath("index.md").is_file():
build_dir = www_path.joinpath("build")
# if it is not a hugo page, upload it as is
else:
build_dir = None
return www_path, src_dir, build_dir
def build_webpages(src_dir, build_dir, config) -> Path:
try: try:
return _build_webpages(src_dir, build_dir, config) _build_webpages(src_dir, build_dir, config)
except Exception: except Exception:
print(traceback.format_exc()) print(traceback.format_exc())
@@ -123,11 +106,15 @@ def main():
config = read_config(inipath) config = read_config(inipath)
config.webdev = True config.webdev = True
assert config.mail_domain assert config.mail_domain
www_path = reporoot.joinpath("www")
src_path = www_path.joinpath("src")
stats = None
build_dir = www_path.joinpath("build")
src_dir = www_path.joinpath("src")
index_path = build_dir.joinpath("index.html")
# start web page generation, open a browser and wait for changes # start web page generation, open a browser and wait for changes
www_path, src_path, build_dir = get_paths(config) build_webpages(src_dir, build_dir, config)
build_dir = build_webpages(src_path, build_dir, config)
index_path = build_dir.joinpath("index.html")
webbrowser.open(str(index_path)) webbrowser.open(str(index_path))
stats = snapshot_dir_stats(src_path) stats = snapshot_dir_stats(src_path)
print(f"\nOpened URL: file://{index_path.resolve()}\n") print(f"\nOpened URL: file://{index_path.resolve()}\n")
@@ -148,7 +135,7 @@ def main():
changenum += 1 changenum += 1
stats = newstats stats = newstats
build_webpages(src_path, build_dir, config) build_webpages(src_dir, build_dir, config)
print(f"[{changenum}] regenerated web pages at: {index_path}") print(f"[{changenum}] regenerated web pages at: {index_path}")
print(f"URL: file://{index_path.resolve()}\n\n") print(f"URL: file://{index_path.resolve()}\n\n")
count = 0 count = 0

View File

@@ -9,11 +9,6 @@ if command -v lsb_release 2>&1 >/dev/null; then
echo "You need to install python3-dev for installing the other dependencies." echo "You need to install python3-dev for installing the other dependencies."
exit 1 exit 1
fi fi
if ! gcc --version 2>&1 >/dev/null
then
echo "You need to install gcc for building Python dependencies."
exit 1
fi
;; ;;
esac esac
fi fi

View File

@@ -11,7 +11,6 @@ for Delta Chat users. For details how it avoids storing personal information
please see our [privacy policy](privacy.html). please see our [privacy policy](privacy.html).
{% endif %} {% endif %}
{% if not config.invite_token %}
<a class="cta-button" href="DCACCOUNT:https://{{ config.mail_domain }}/new">Get a {{config.mail_domain}} chat profile</a> <a class="cta-button" href="DCACCOUNT:https://{{ config.mail_domain }}/new">Get a {{config.mail_domain}} chat profile</a>
If you are viewing this page on a different device If you are viewing this page on a different device
@@ -24,10 +23,6 @@ you can also **scan this QR code** with Delta Chat:
🐣 **Choose** your Avatar and Name 🐣 **Choose** your Avatar and Name
💬 **Start** chatting with any Delta Chat contacts using [QR invite codes](https://delta.chat/en/help#howtoe2ee) 💬 **Start** chatting with any Delta Chat contacts using [QR invite codes](https://delta.chat/en/help#howtoe2ee)
{% else %}
**To join this instance, you need an invite link or QR code -
ask the admin for an invite.**
{% endif %}
{% if config.mail_domain != "nine.testrun.org" %} {% if config.mail_domain != "nine.testrun.org" %}
<div class="experimental">Note: this is only a temporary development chatmail service</div> <div class="experimental">Note: this is only a temporary development chatmail service</div>