aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/aio.py
blob: d5386f159749164eb5adb0fcd3149d0911ceaf20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#!/usr/bin/env python3

try:
    import rich
    import jsonschema
    import cryptography
except ImportError:
    print("Some necessary crupest can't be imported. Please run `pip install -r requirements.txt` to install them.")
    exit(1)

from os.path import *
import argparse
import subprocess
from rich.prompt import Confirm
from crupest.install_docker import *
from crupest.path import *
from crupest.nginx import *
from crupest.config import *
from crupest.check import *
from crupest.backup import *
from crupest.download_tools import *
from crupest.test import *
from crupest.dns import *
from crupest.setup import *

from crupest.tui import console


parser = argparse.ArgumentParser(
    description="Crupest server all-in-one setup script. Have fun play with it!")
parser.add_argument("--no-hello", action="store_true",
                    default=False, help="Do not print hello message.")
parser.add_argument("--no-bye-bye", action="store_true",
                    default=False, help="Do not print bye-bye message.")

parser.add_argument("--no-check-python-version", action="store_true",
                    default=False, help="Do not check python version.")
parser.add_argument("--no-check-system", action="store_true",
                    default=False, help="Do not check system type.")
parser.add_argument("-y", "--yes", action="store_true",
                    default=False, help="Yes to all confirmation.")

subparsers = parser.add_subparsers(dest="action")

setup_parser = subparsers.add_parser(
    "setup", help="Do everything necessary to setup the server.")

print_path_parser = subparsers.add_parser(
    "print-path", help="Print the paths of all related files and dirs.")

download_tools_parser = subparsers.add_parser(
    "download-tools", help="Download some extra tools to manage the server.")

list_domain_parser = subparsers.add_parser(
    "list-domain", help="Misc things about domains.")

nginx_parser = subparsers.add_parser(
    "nginx", help="Generate nginx config.")

certbot_parser = subparsers.add_parser(
    "certbot", help="Get some common certbot commands.")

certbot_command_group = certbot_parser.add_mutually_exclusive_group()

certbot_command_group.add_argument(
    "-C", "--create", action="store_true", default=False, help="Only print the command for 'create' action.")
certbot_command_group.add_argument(
    "-E", "--expand", action="store_true", default=False, help="Only print the command for 'expand' action.")
certbot_command_group.add_argument(
    "-R", "--renew", action="store_true", default=False, help="Only print the command for 'renew' action.")

certbot_parser.add_argument(
    "-t", "--test", action="store_true", default=False, help="Make the commands for test use.")

clear_parser = subparsers.add_parser(
    "clear", help="Delete existing data so you can make a fresh start.")
clear_parser.add_argument("-D", "--include-data-dir", action="store_true",
                          default=False, help="Also delete the data directory.")

install_docker_parser = subparsers.add_parser(
    "install-docker", help="Install docker and docker-compose.")

backup_parser = subparsers.add_parser(
    "backup", help="Backup related things."
)

backup_subparsers = backup_parser.add_subparsers(dest="backup_action")
backup_restore_parser = backup_subparsers.add_parser(
    "restore", help="Restore data from url.")
backup_restore_parser.add_argument(
    "restore_url", help="Restore archive url. Can be local path or http/https.")
backup_backup_parser = backup_subparsers.add_parser(
    "backup", help="Backup data to specified path.")
backup_backup_parser.add_argument(
    "backup_path", nargs="?", help="Backup path. Can be empty for a timestamp as name. Must be local path.")

docker_parser = subparsers.add_parser("docker", help="Docker related things.")
docker_subparsers = docker_parser.add_subparsers(dest="docker_action")
docker_subparsers.add_parser("up", help="Run docker compose up -d.")
docker_subparsers.add_parser("down", help="Run docker compose down.")
docker_subparsers.add_parser(
    "prune", help="Run docker system prune -a -f.")

test_parser = subparsers.add_parser("test", help="Test things.")
test_parser.add_argument(
    "test_action", help="Test action.", choices=["crupest-api"])

dns_parser = subparsers.add_parser("dns", help="Generate dns zone.")

dns_parser.add_argument("-i", "--ip", help="IP address of the server.")

git_update_parser = subparsers.add_parser(
    "git-update", help="Update git submodules.")

update_blog_parser = subparsers.add_parser(
    "update-blog", help="Update and regenerate blog.")

up_parser = subparsers.add_parser(
    "up", help="Do something necessary and then docker compose up.")

down_parser = subparsers.add_parser(
    "down", help="Do something necessary and then docker compose down.")

args = parser.parse_args()

if args.yes:
    old_ask = Confirm.ask

    def new_ask(prompt, *args, console=console, default=None, **kwargs):
        default_text = ""
        if default is not None:
            default_text = "(y)" if default else "(n)"
        text = f"[prompt]{prompt}[/] [prompt.choices]\\[y/n][/] [prompt.default]{default_text}[/]"
        console.print(text)
        return True

    Confirm.ask = new_ask

if (args.action == "certbot" and (args.create or args.renew or args.expand)) or (args.action == "dns" and args.ip is not None):
    args.no_hello = True
    args.no_bye_bye = True


if not args.no_check_python_version:
    if check_python_version():
        console.print("This script works well on python 3.10. Otherwise you may encounter some problems. But I would like to improve some rational compatibility.", style="yellow")

if not args.no_check_system:
    if not check_ubuntu():
        console.print("This script works well on Ubuntu 22.04. Otherwise you may encounter some problems. But I would like to improve some rational compatibility.", style="yellow")


if not args.no_hello:
    console.print("Nice to see you! :waving_hand:", style="cyan")


def check_domain_is_defined():
    try:
        return get_domain()
    except Exception as e:
        console.print(e.args[0], style="red")
        raise e


def git_update():
    def do_it():
        subprocess.run(["git", "pull"], check=True)
    run_in_project_dir(do_it)


def update_blog():
    def do_it():
        subprocess.run(["docker", "compose", "exec",
                       "crupest-blog", "/scripts/update.bash"], check=True)
    run_in_project_dir(do_it)


def docker_compose_up():
    def do_docker_compose_up():
        subprocess.run(["docker", "compose", "up", "-d"], check=True)
    run_in_dir(project_abs_path, do_docker_compose_up)


def docker_compose_down():
    def do_docker_compose_down():
        subprocess.run(
            ["docker", "compose", "down"], check=True)
    run_in_dir(project_abs_path, do_docker_compose_down)


action = args.action


def run():
    match action:
        case "install-docker":
            install_docker()
            console.print(
                "Succeeded to install docker. Please re-login to take effect.", style="green")

        case "docker":
            docker_action = args.docker_action

            match docker_action:
                case "up":
                    docker_compose_up()
                case "down":
                    docker_compose_down()
                case "prune":
                    to_do = Confirm.ask(
                        "[yellow]Are you sure to prune docker?[/]", console=console)
                    if to_do:
                        subprocess.run(
                            ["docker", "system", "prune", "-a", "-f"], check=True)
                case _:
                    raise ValueError("Unknown docker action.")

        case "backup":
            backup_action = args.backup_action
            match backup_action:
                case "backup":
                    backup_backup(args.backup_path, console)
                    console.print("Succeeded to restore data.", style="green")
                case "restore":
                    backup_restore(args.restore_path, console)
                    console.print("Succeeded to backup data.", style="green")

        case 'print-path':
            console.print("Project path =", project_dir)
            console.print("Project absolute path =", project_abs_path)
            console.print("Data path =", data_dir)

        case "download-tools":
            download_tools(console)

        case "list-domain":
            domain = check_domain_is_defined()
            domains = list_domains(domain)
            for domain in domains:
                console.print(domain)

        case "nginx":
            raise Exception("This command is deprecated.")

        case "certbot":
            domain = check_domain_is_defined()
            is_test = args.test
            if args.create:
                console.print(certbot_command_gen(domain, "create",
                                                  test=is_test), soft_wrap=True, highlight=False)
            elif args.expand:
                console.print(certbot_command_gen(domain, "expand",
                                                  test=is_test), soft_wrap=True, highlight=False)
            elif args.renew:
                console.print(certbot_command_gen(domain, "renew",
                                                  test=is_test), soft_wrap=True, highlight=False)
            else:
                console.print(
                    "Here is some commands you can use to do certbot related work.")
                if is_test:
                    console.print(
                        "Note you specified --test, so the commands are for test use.", style="yellow")
                console.print(
                    "To create certs for init (standalone):", style="cyan")
                console.print(certbot_command_gen(
                    domain, 'create', test=is_test), soft_wrap=True)
                console.print("To expand certs (nginx):", style="cyan")
                console.print(certbot_command_gen(
                    domain, 'create', test=is_test), soft_wrap=True)
                console.print(
                    "To renew certs previously created (nginx):", style="cyan")
                console.print(certbot_command_gen(
                    domain, 'renew', test=is_test), soft_wrap=True)
        case "test":
            match args.test_action:
                case "crupest-api":
                    test_crupest_api(console)
                case _:
                    console.print("Test action invalid.", style="red")

        case "dns":
            domain = check_domain_is_defined()
            if domain is not None:
                if args.ip is None:
                    ip = Prompt.ask(
                        "Please enter your server ip", console=console)
                else:
                    ip = args.ip
                console.print(generate_dns_zone_with_dkim(
                    domain, ip), soft_wrap=True, highlight=False)

        case "git-update":
            git_update()

        case "update-blog":
            update_blog()

        case "up":
            git_update()
            template_generate(console)
            docker_compose_up()

        case "down":
            docker_compose_down()

        case "clear":
            clear(console, args.include_data_dir)

        case _:
            template_generate(console)
            if Confirm.ask(
                    "By the way, would you like to download some scripts to do some extra setup like creating email user?", console=console, default=True):
                download_tools(console)


run()

if not args.no_bye_bye:
    console.print(":beers: All done! Bye bye!", style="green")