From f589307abeb3295d224be711a138c6a7d946f619 Mon Sep 17 00:00:00 2001 From: VIRTUALGUARD Date: Thu, 10 Jul 2025 16:41:46 +0800 Subject: [PATCH] Init project --- .python-version | 1 + README.md | 0 pyproject.toml | 7 +++ servicesManager.py | 140 +++++++++++++++++++++++++++++++++++++++++ shell/install.sh | 24 +++++++ shell/sslrenew.sh | 3 + src/ServicesManager.py | 64 +++++++++++++++++++ src/__init__.py | 1 + 8 files changed, 240 insertions(+) create mode 100644 .python-version create mode 100644 README.md create mode 100644 pyproject.toml create mode 100755 servicesManager.py create mode 100755 shell/install.sh create mode 100755 shell/sslrenew.sh create mode 100644 src/ServicesManager.py create mode 100644 src/__init__.py diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6c06e2f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "managescripts" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [] diff --git a/servicesManager.py b/servicesManager.py new file mode 100755 index 0000000..d599aaa --- /dev/null +++ b/servicesManager.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +import os +import logging +import colorlog + +# init color log +handler = colorlog.StreamHandler() +handler.setFormatter(colorlog.ColoredFormatter( + '%(log_color)s%(asctime)s - %(levelname)s - %(message)s', + log_colors={ + 'DEBUG': 'cyan', + 'INFO': 'green', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'bold_red', + } +)) + +logger = colorlog.getLogger(__name__) +logger.setLevel(logging.INFO) +logger.addHandler(handler) + +def get_operation(): + while True: + choice = input("请选择操作 (0 停止, 1 重启, q 退出): ").strip().lower() + if choice == 'q': + return 'q' + try: + op = int(choice) + if op in (0, 1): + return op + except ValueError: + pass + logger.warning("无效输入,请输入 0、1 或 q。") + +def manage_service(name, command_template): + logger.info(f"===== {name} 管理 =====") + logger.info("0. 停止") + logger.info("1. 启动") + op = get_operation() + if op == 'q': + return False + action = 'down' if op == 0 else 'up -d' + cmd = command_template.format(action=action) + try: + subprocess.run(cmd, shell=True, check=True) + logger.info(f"{name} {('停止' if op == 0 else '重启')} 完成") + except subprocess.CalledProcessError as e: + logger.error(f"{name} 管理失败: {e}") + return True + +def nginx_manager(): + logger.info("===== Nginx 管理 =====") + logger.info("0. 停止") + logger.info("1. 重启") + op = get_operation() + if op == 'q': + return False + action = 'stop' if op == 0 else 'restart' + cmd = f"sudo systemctl {action} nginx" + try: + subprocess.run(cmd, shell=True, check=True) + logger.info(f"Nginx {('停止' if op == 0 else '重启')} 完成") + except subprocess.CalledProcessError as e: + logger.error(f"Nginx 管理失败: {e}") + return True + +def homepage_manager(): + path = os.path.expanduser("~/web/homepageService") + return manage_service("Homepage", f"cd {path} && docker compose {{action}}") + +def status_manager(): + path = os.path.expanduser("~/web/statusService") + return manage_service("Status", f"cd {path} && docker compose {{action}}") + +def gitea_manager(): + path = os.path.expanduser("~/web/giteaService") + return manage_service("Gitea", f"cd {path} && docker compose {{action}}") + +def main(): + services = { + 0: ("Nginx", nginx_manager), + 1: ("Homepage", homepage_manager), + 2: ("Status", status_manager), + 3: ("Gitea", gitea_manager), + } + + while True: + logger.info("=== 服务管理 ===") + for key, (name, _) in services.items(): + logger.info(f"{key}. {name}") + logger.info("a. 全部") + logger.info("q. 退出") + + choice = input("请选择要管理的服务或 a 全部: ").strip().lower() + if choice == 'q': + logger.info("退出程序") + break + if choice == 'a': + logger.info("=== 批量操作: 全部服务 ===") + logger.info("0. 停止") + logger.info("1. 重启") + op = get_operation() + if op == 'q': + continue + action_nginx = 'stop' if op == 0 else 'restart' + action_others = 'down' if op == 0 else 'restart' + commands = [ + ("Nginx", f"sudo systemctl {action_nginx} nginx"), + ("Homepage", f"cd {os.path.expanduser('~/web/homepageService')} && docker compose {action_others}"), + ("Status", f"cd {os.path.expanduser('~/web/statusService')} && docker compose {action_others}"), + ("Gitea", f"cd {os.path.expanduser('~/web/giteaService')} && docker compose {action_others}"), + ] + for name, cmd in commands: + try: + logger.info(f"{name} 开始 {('停止' if op==0 else '重启')}") + subprocess.run(cmd, shell=True, check=True) + logger.info(f"{name} 完成") + except subprocess.CalledProcessError as e: + logger.error(f"{name} 操作失败: {e}") + continue + + try: + idx = int(choice) + if idx in services: + cont = services[idx][1]() + if cont is False: + continue + else: + logger.warning("无效选项") + except ValueError: + logger.warning("无效输入,请输入数字或 q") + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/shell/install.sh b/shell/install.sh new file mode 100755 index 0000000..ed9eea9 --- /dev/null +++ b/shell/install.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -ue +sudo apt update + +## Docker +# curl -fsSL https://get.docker.com | bash -s docker +# sudo apt install docker-compose +# # sudo groupadd docker # 若尚不存在 docker 组,则需先创建 +# sudo usermod -aG docker $USER + +## Nginx +sudo apt install nginx +sudo systemctl enable nginx + +## Certbot +sudo apt install certbot + +## Cron +sudo apt install cron + +## Python +sudo apt install python3 +sudo apt install pip +curl -LsSf https://astral.sh/uv/install.sh | sh # astral uv diff --git a/shell/sslrenew.sh b/shell/sslrenew.sh new file mode 100755 index 0000000..83534b8 --- /dev/null +++ b/shell/sslrenew.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +sudo certbot renew diff --git a/src/ServicesManager.py b/src/ServicesManager.py new file mode 100644 index 0000000..bd09121 --- /dev/null +++ b/src/ServicesManager.py @@ -0,0 +1,64 @@ +import subprocess +import sys +import os +import logging +import colorlog + +# init color log +handler = colorlog.StreamHandler() +handler.setFormatter(colorlog.ColoredFormatter( + '%(log_color)s%(asctime)s - %(levelname)s - %(message)s', + log_colors={ + 'DEBUG': 'cyan', + 'INFO': 'green', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'bold_red', + } +)) + +logger = colorlog.getLogger(__name__) +logger.setLevel(logging.INFO) +logger.addHandler(handler) + + +def get_operation(): + while True: + choice = input("请选择操作 (0 停止, 1 重启, q 退出): ").strip().lower() + if choice == 'q': + return 'q' + try: + op = int(choice) + if op in (0, 1): + return op + except ValueError: + pass + logger.warning("无效输入,请输入 0、1 或 q。") + + +class Service: + """A template of webservices + + Attributes: + tag (str): The tag that mark the service instance use which way to deploy. + name (str): The name of the service instance. + dir (str): The configuration & data directory of the service instance. + """ + + def __init__(self, tag: str, name: str, dir: str): + self.tag = tag + self.name = name + self.dir = dir + + def command_gen(self): + """Generate services's management commands according to services's tag + + """ + system_services_command = "sudo systemctl" + docker_services_command = f"cd {os.path.expanduser(f'{self.dir}')} && docker compose" + if self.tag == "sys": + return system_services_command + elif self.tag == "docker": + return docker_services_command + else: + raise ValueError("The service tag {self.tag} was not included") diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +