diff options
| author | crupest <crupest@outlook.com> | 2022-11-19 13:00:13 +0800 | 
|---|---|---|
| committer | crupest <crupest@outlook.com> | 2022-11-19 13:00:13 +0800 | 
| commit | f775f4c76691fc61b2993d81d9f1b4690e76b141 (patch) | |
| tree | 9fe248e5379c66cbaa740243bebc04bb7df7cd29 /tool/modules | |
| parent | 1562a317e147954c5618ecdccbfd10c944bf81ce (diff) | |
| download | crupest-f775f4c76691fc61b2993d81d9f1b4690e76b141.tar.gz crupest-f775f4c76691fc61b2993d81d9f1b4690e76b141.tar.bz2 crupest-f775f4c76691fc61b2993d81d9f1b4690e76b141.zip  | |
No caddy, only nginx and certbot.
Diffstat (limited to 'tool/modules')
| -rw-r--r-- | tool/modules/configfile.py | 45 | ||||
| -rwxr-xr-x | tool/modules/nginx.py | 72 | ||||
| -rw-r--r-- | tool/modules/path.py | 13 | ||||
| -rw-r--r-- | tool/modules/template.py | 32 | 
4 files changed, 162 insertions, 0 deletions
diff --git a/tool/modules/configfile.py b/tool/modules/configfile.py new file mode 100644 index 0000000..49f2200 --- /dev/null +++ b/tool/modules/configfile.py @@ -0,0 +1,45 @@ +import os.path +from .path import config_file_path + +config_file_exist = os.path.isfile(config_file_path) + + +def parse_config(str: str) -> dict: +    config = {} +    for line_number, line in enumerate(str.splitlines()): +        # check if it's a comment +        if line.startswith("#"): +            continue +        # check if there is a '=' +        if line.find("=") == -1: +            raise ValueError( +                f"Invalid config string. Please check line {line_number + 1}. There is even no '='!") +        # split at first '=' +        key, value = line.split("=", 1) +        key = key.strip() +        value = value.strip() +        config[key] = value +    return config + + +def get_domain() -> str: +    if not config_file_exist: +        raise ValueError("Config file not found!") +    with open(config_file_path) as f: +        config = parse_config(f.read()) +    if "CRUPEST_DOMAIN" not in config: +        raise ValueError("Domain not found in config file!") +    return config["CRUPEST_DOMAIN"] + + +def config_to_str(config: dict) -> str: +    return "\n".join([f"{key}={value}" for key, value in config.items()]) + + +def print_config(console, config: dict) -> None: +    for key, value in config.items(): +        console.print(f"[magenta]{key}[/] = [cyan]{value}") + + +__all__ = ["config_file_exist", "parse_config", +           "get_domain", "config_to_str", "print_config"] diff --git a/tool/modules/nginx.py b/tool/modules/nginx.py new file mode 100755 index 0000000..6cb918c --- /dev/null +++ b/tool/modules/nginx.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +from .template import Template +from .path import nginx_template_dir +import json +import jsonschema +import os +import os.path +import shutil + +with open(os.path.join(nginx_template_dir, 'server.json')) as f: +    server = json.load(f) + +with open(os.path.join(nginx_template_dir, 'server.schema.json')) as f: +    schema = json.load(f) + +jsonschema.validate(server, schema) + +root_template = Template(os.path.join( +    nginx_template_dir, 'root.conf.template')) +static_file_template = Template(os.path.join( +    nginx_template_dir, 'static-file.conf.template')) +reverse_proxy_template = Template(os.path.join( +    nginx_template_dir, 'reverse-proxy.conf.template')) + + +def nginx_config_gen(domain: str, dest: str) -> None: +    if not os.path.isdir(dest): +        raise ValueError('dest must be a directory') +    # copy ssl.conf and https-redirect.conf which need no variable substitution +    for filename in ['ssl.conf', 'https-redirect.conf']: +        src = os.path.join(nginx_template_dir, filename) +        dst = os.path.join(dest, filename) +        shutil.copyfile(src, dst) +    config = {"CRUPEST_DOMAIN": domain} +    # generate root.conf +    with open(os.path.join(dest, f'{domain}.conf'), 'w') as f: +        f.write(root_template.generate(config)) +    # generate nginx config for each site +    sites: list = server["sites"] +    for site in sites: +        if site["type"] not in ['static-file', 'reverse-proxy']: +            continue +        subdomain = site["subdomain"] +        local_config = config.copy() +        local_config['CRUPEST_NGINX_SUBDOMAIN'] = subdomain +        match site["type"]: +            case 'static-file': +                template = static_file_template +                local_config['CRUPEST_NGINX_ROOT'] = site["root"] +            case 'reverse-proxy': +                template = reverse_proxy_template +                local_config['CRUPEST_NGINX_UPSTREAM_NAME'] = site["upstream"]["name"] +                local_config['CRUPEST_NGINX_UPSTREAM_SERVER'] = site["upstream"]["server"] +        with open(os.path.join(dest, f'{subdomain}.{domain}.conf'), 'w') as f: +            f.write(template.generate(local_config)) + + +def list_domains(domain: str) -> list[str]: +    return [domain, *server.sites.map(lambda s: f"{s.subdomain}.{domain}")] + + +def certbot_command_gen(domain: str, action, test=False) -> str: +    domains = list_domains(domain) +    match action: +        case 'create': +            # create with standalone mode +            return f'docker run -it --name certbot -v "./data/certbot/certs:/etc/letsencrypt" -v "./data/certbot/data:/var/lib/letsencrypt" certbot/certbot certonly --standalone -d {" -d ".join(domains)}{ " --test-cert" if test else "" }' +        case 'renew': +            # renew with webroot mode +            return f'docker run -it --name certbot -v "./data/certbot/certs:/etc/letsencrypt" -v "./data/certbot/data:/var/lib/letsencrypt" -v "./data/certbot/webroot:/var/www/certbot" certbot/certbot renew --webroot -w /var/www/certbot' +    raise ValueError('Invalid action') diff --git a/tool/modules/path.py b/tool/modules/path.py new file mode 100644 index 0000000..94adc27 --- /dev/null +++ b/tool/modules/path.py @@ -0,0 +1,13 @@ +import os.path + +script_dir = os.path.relpath(os.path.dirname(__file__)) +project_dir = os.path.normpath(os.path.join(script_dir, "../../")) +template_dir = os.path.join(project_dir, "template") +nginx_template_dir = os.path.join(template_dir, "nginx") +data_dir = os.path.join(project_dir, "data") +tool_dir = os.path.join(project_dir, "tool") +config_file_path = os.path.join(data_dir, "config") +nginx_config_dir = os.path.join(project_dir, "nginx-config") + +__all__ = ["script_dir", "project_dir", "template_dir", +           "nginx_template_dir", "data_dir", "config_file_path", "tool_dir", "nginx_config_dir"] diff --git a/tool/modules/template.py b/tool/modules/template.py new file mode 100644 index 0000000..15238ea --- /dev/null +++ b/tool/modules/template.py @@ -0,0 +1,32 @@ +import os.path +import re + + +class Template: +    def __init__(self, template_path: str, var_prefix: str = "CRUPEST"): +        if len(var_prefix) != 0 and re.fullmatch(r"^[a-zA-Z_][a-zA-Z0-9_]*$", var_prefix) is None: +            raise ValueError("Invalid var prefix.") +        self.template_path = template_path +        self.template_name = os.path.basename( +            template_path)[:-len(".template")] +        with open(template_path, "r") as f: +            self.template = f.read() +        self.var_prefix = var_prefix +        self.__var_regex = re.compile(r"\$(" + var_prefix + r"_[a-zA-Z0-9_]+)") +        self.__var_brace_regex = re.compile( +            r"\$\{\s*(" + var_prefix + r"_[a-zA-Z0-9_]+)\s*\}") +        var_set = set() +        for match in self.__var_regex.finditer(self.template): +            var_set.add(match.group(1)) +        for match in self.__var_brace_regex.finditer(self.template): +            var_set.add(match.group(1)) +        self.var_set = var_set + +    def generate(self, config: dict[str, str]) -> str: +        result = self.template +        for var in self.var_set: +            if var not in config: +                raise ValueError(f"Missing config var {var}.") +            result = result.replace("$" + var, config[var]) +            re.sub(r"\$\{\s*" + var + r"\s*\}", config[var], result) +        return result  | 
