├── 1Panel ├── 1panel-appstore-1.x │ ├── README.md │ ├── d2c.py │ ├── generate_config.bat │ ├── generate_env.bat │ └── genv.py ├── 1panel-appstore-2.x │ ├── 1to2 │ │ ├── 1.app-version_json2yml.bat │ │ ├── 1.app-version_json2yml.py │ │ ├── 2.move-and-delete.bat │ │ ├── 2.move-and-delete.py │ │ ├── 3.app-json2yml.bat │ │ ├── 3.app-json2yml.py │ │ └── README.md │ └── gen-yml │ │ ├── README.md │ │ ├── app-version.xlsx │ │ ├── app-version_xlsx2yml.bat │ │ ├── app-version_xlsx2yml.py │ │ └── 需求.png ├── 1panel-auto-upgrade │ ├── .env │ ├── 1panel-auto-upgrade.service │ ├── 1panel-auto-upgrade.sh │ └── README.md ├── 1panel-execution-mode │ └── 1panel_docker_to_sys.sh ├── 1panel-restore │ └── 1panel_quick_restore.sh └── 1panel-upgrade │ ├── 1panel-upgrade.sh │ └── README.md ├── README.md └── tools ├── README.md ├── daily-news.sh ├── generate-ecc-ssl.sh ├── generate-frp-ssl.sh ├── generate-rsa-ssl.sh ├── generate-ssl.sh ├── timesync.sh └── vps-setup.sh /1Panel/1panel-appstore-1.x/README.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | 一个非常人工智能的`config.json`生成脚本。~~(指人工输入关键词,AI生成的)~~ 4 | 5 | 和`.env.sample`生成脚本。 6 | 7 | 由`New Bing`和 `Chat GPT`编写。 8 | 9 | # 使用说明 10 | 11 | - 拖动一个`docker-compose.yml`文件到`generate_config.bat`,会生成一个符合`1Panel`应用的`config.json`文件,方便作为初始模板。 12 | 13 | - 拖动一个`config.json`文件到`generate_env.bat`,会生成一个`.env.sample`文件,方便单独部署。 14 | 15 | > 注意需要预装python 16 | 17 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-1.x/d2c.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import sys 4 | 5 | # 读取输入文件和输出文件名 6 | input_file = sys.argv[1] 7 | output_file = "config.json" 8 | 9 | # 读取输入文件 10 | with open(input_file, "r") as f: 11 | content = f.read() 12 | 13 | # 从输入文件中提取所有环境变量 14 | env_vars = re.findall(r'\$\{([^}]+)\}', content) 15 | 16 | # 初始化表单字段数组 17 | formFields = [] 18 | 19 | # 遍历环境变量,并将它们添加到表单字段数组中 20 | for env_var in env_vars: 21 | formFields.append({ 22 | "type": "text", 23 | "labelZh": "", 24 | "labelEn": "", 25 | "required": True, 26 | "default": "", 27 | "rule": "paramCommon", 28 | "envKey": env_var 29 | }) 30 | 31 | # 创建最终的 JSON 对象 32 | json_data = { 33 | "formFields": formFields 34 | } 35 | 36 | # 格式化 JSON 数据 37 | json_string = json.dumps(json_data, indent=2) 38 | 39 | # 将格式化后的 JSON 字符串写入输出文件 40 | with open(output_file, "w") as f: 41 | f.write(json_string) 42 | 43 | print(f"已将格式化的 JSON 对象写入 {output_file}") 44 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-1.x/generate_config.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem 设置输入参数为脚本拖动到的文件路径 4 | set "input_file=%~1" 5 | 6 | rem 设置Python脚本的名称 7 | set "py_script=d2c.py" 8 | 9 | rem 运行Python脚本生成配置文件 10 | echo 正在生成配置文件,请稍候... 11 | python %py_script% %input_file% 12 | 13 | rem 提示用户完成 14 | echo 配置文件已生成。 15 | done 16 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-1.x/generate_env.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem 设置输入参数为脚本拖动到的文件路径 4 | set "input_file=%~1" 5 | 6 | rem 设置Python脚本的名称 7 | set "py_script=genv.py" 8 | 9 | rem 运行Python脚本生成配置文件 10 | echo 正在生成配置文件,请稍候... 11 | python %py_script% %input_file% 12 | 13 | rem 提示用户完成 14 | echo 配置文件已生成。 15 | done 16 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-1.x/genv.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | # 读取 JSON 文件 4 | with open("config.json", "r") as f: 5 | data = json.load(f) 6 | 7 | # 从 JSON 数据中提取表单字段列表 8 | form_fields = data.get("formFields", []) 9 | 10 | # 构造 .env.sample 文件内容 11 | env_sample_content = "" 12 | for item in form_fields: 13 | env_key = item.get("envKey", "") 14 | default_value = item.get("default", "") 15 | env_sample_content += f"{env_key}='{default_value}'\n" 16 | 17 | # 将 .env.sample 内容写入文件 18 | with open(".env.sample", "w") as f: 19 | f.write(env_sample_content) 20 | 21 | print(".env.sample 文件已生成。") 22 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/1.app-version_json2yml.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/1to2/1.app-version_json2yml.bat -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/1.app-version_json2yml.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yaml 3 | import json 4 | 5 | def convert_json_to_yaml(json_file_path, output_file_path, encoding='utf-8'): 6 | with open(json_file_path, 'r', encoding=encoding) as json_file: 7 | json_data = json.load(json_file) 8 | 9 | yaml_data = { 10 | "additionalProperties": { 11 | "formFields": [] 12 | } 13 | } 14 | 15 | for field in json_data["formFields"]: 16 | if "envKey" not in field: 17 | continue 18 | 19 | yaml_field = { 20 | "default": field.get("default", None), 21 | "envKey": field["envKey"], 22 | "labelEn": field.get("labelEn", ""), 23 | "labelZh": field.get("labelZh", ""), 24 | "required": field.get("required", False), 25 | "type": field.get("type", "") 26 | } 27 | 28 | if "random" in field: 29 | yaml_field["random"] = field["random"] 30 | if "rule" in field: 31 | yaml_field["rule"] = field["rule"] 32 | if "edit" in field: 33 | yaml_field["edit"] = field["edit"] 34 | 35 | yaml_data["additionalProperties"]["formFields"].append(yaml_field) 36 | 37 | with open(output_file_path, 'w', encoding=encoding) as output_file: 38 | yaml.dump(yaml_data, output_file, indent=4, allow_unicode=True) 39 | 40 | # 读取生成的YAML文件内容并替换字符串 41 | with open(output_file_path, 'r', encoding='utf-8') as f: 42 | yaml_content = f.readlines() 43 | 44 | # 对每行进行缩进处理 45 | indented_yaml_content = [] 46 | for line in yaml_content: 47 | if line.startswith(" ") and ":" in line: 48 | indented_line = " " + line[8:] 49 | indented_yaml_content.append(indented_line) 50 | else: 51 | indented_yaml_content.append(line) 52 | 53 | # 替换字符串 " - default" 为 " - default" 54 | for i in range(len(indented_yaml_content)): 55 | if indented_yaml_content[i].startswith(" - default"): 56 | indented_yaml_content[i] = indented_yaml_content[i].replace(" - default", " - default") 57 | 58 | # 将处理后的内容写回文件 59 | with open(output_file_path, 'w', encoding='utf-8') as f: 60 | f.writelines(indented_yaml_content) 61 | 62 | def convert_versions_to_yaml(directory_path): 63 | for root, dirs, files in os.walk(directory_path): 64 | if "versions" in root: 65 | for file in files: 66 | if file == "config.json": 67 | json_file_path = os.path.join(root, file) 68 | output_file_path = os.path.join(root, "data.yml") 69 | convert_json_to_yaml(json_file_path, output_file_path) 70 | print("Converted:", json_file_path, "to", output_file_path) 71 | 72 | # 输入源目录 73 | source_directory = "./appstore" 74 | 75 | # 遍历目录并转换versions目录下的config.json文件为YAML文件 76 | convert_versions_to_yaml(source_directory) 77 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/2.move-and-delete.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/1to2/2.move-and-delete.bat -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/2.move-and-delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | def move_files(src_directory, dest_directory): 5 | for file in os.listdir(src_directory): 6 | src_path = os.path.join(src_directory, file) 7 | dest_path = os.path.join(dest_directory, file) 8 | shutil.move(src_path, dest_path) 9 | 10 | def delete_directory(directory): 11 | shutil.rmtree(directory) 12 | 13 | def process_readme(directory): 14 | readme_files = [] 15 | for root, dirs, files in os.walk(directory): 16 | if "README.md" in files: 17 | readme_path = os.path.join(root, "README.md") 18 | readme_files.append(readme_path) 19 | 20 | if len(readme_files) > 0: 21 | # 按修改时间对README.md文件进行排序,保留最新版本 22 | readme_files.sort(key=lambda x: os.path.getmtime(x), reverse=True) 23 | newest_readme = readme_files[0] 24 | 25 | # 移动最新版本的README.md文件到./appstore/$A/目录下 26 | dest_path = os.path.join(directory, "README.md") 27 | shutil.move(newest_readme, dest_path) 28 | 29 | # 删除其他版本的README.md文件 30 | for readme_file in readme_files[1:]: 31 | os.remove(readme_file) 32 | 33 | appstore_directory = "./appstore" 34 | 35 | # 遍历所有$A文件夹 36 | for a_dir in os.listdir(appstore_directory): 37 | a_path = os.path.join(appstore_directory, a_dir) 38 | if os.path.isdir(a_path): 39 | # 处理metadata文件夹 40 | metadata_directory = os.path.join(a_path, "metadata") 41 | if os.path.exists(metadata_directory): 42 | move_files(metadata_directory, a_path) 43 | delete_directory(metadata_directory) 44 | 45 | # 处理versions文件夹 46 | versions_directory = os.path.join(a_path, "versions") 47 | if os.path.exists(versions_directory): 48 | for b_dir in os.listdir(versions_directory): 49 | b_path = os.path.join(versions_directory, b_dir) 50 | if os.path.isdir(b_path): 51 | dest_directory = os.path.join(a_path, b_dir) 52 | move_files(b_path, dest_directory) 53 | delete_directory(b_path) 54 | 55 | delete_directory(versions_directory) 56 | 57 | # 处理README.md文件 58 | process_readme(a_path) 59 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/3.app-json2yml.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/1to2/3.app-json2yml.bat -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/3.app-json2yml.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import yaml 4 | 5 | class IndentDumper(yaml.Dumper): 6 | def increase_indent(self, flow=False, indentless=False): 7 | return super().increase_indent(flow, False) 8 | 9 | def convert_json_to_yml(json_file): 10 | with open(json_file, 'r', encoding='utf-8') as f: 11 | data = json.load(f) 12 | 13 | tag_mapping = { 14 | 'Tool': '工具', 15 | } 16 | 17 | yml_template = { 18 | 'name': data['name'], 19 | 'tags': [ 20 | tag_mapping.get(tag, tag) for tag in data['tags'] 21 | ], 22 | 'title': data['shortDescZh'], 23 | 'type': '工具' if data['type'] == 'tool' else '建站', 24 | 'description': data['shortDescZh'], 25 | 'additionalProperties': { 26 | 'key': data['key'], 27 | 'name': data['name'], 28 | 'tags': [ 29 | 'Tool' 30 | ], 31 | 'shortDescZh': data['shortDescZh'], 32 | 'shortDescEn': data['shortDescEn'], 33 | 'type': 'tool', 34 | 'crossVersionUpdate': data['crossVersionUpdate'], 35 | 'limit': data['limit'], 36 | 'recommend': 0, 37 | 'website': data['website'], 38 | 'github': data['github'], 39 | 'document': data['document'] 40 | } 41 | } 42 | 43 | yml_file = os.path.join(os.path.dirname(json_file), 'data.yml') 44 | with open(yml_file, 'w', encoding='utf-8') as f: 45 | yaml.dump(yml_template, f, Dumper=IndentDumper, default_flow_style=False, sort_keys=False, allow_unicode=True, indent=4) 46 | 47 | print(f'Successfully converted {json_file} to {yml_file}') 48 | 49 | # 遍历appstore文件夹下的所有文件夹 50 | appstore_dir = './appstore' 51 | for folder_name in os.listdir(appstore_dir): 52 | folder_path = os.path.join(appstore_dir, folder_name) 53 | 54 | # 检查是否是文件夹 55 | if os.path.isdir(folder_path): 56 | json_file = os.path.join(folder_path, 'app.json') 57 | 58 | # 检查app.json文件是否存在 59 | if os.path.isfile(json_file): 60 | convert_json_to_yml(json_file) 61 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/1to2/README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | 这是个`1panel`1.x版本商店的本地应用转换为2.0版本商店配置的脚本。主要使用`python`,`bat`脚本仅仅是用于调用而已。 4 | 5 | 感谢`ChatGPT`。 6 | 7 | # 使用方法 8 | 9 | - 1.注意按顺序使用,脚本前带有顺序1>2>3; 10 | - 2.如果使用`bat`来运行,注意`py`文件名要和`bat`文件里的相对应,默认已一致; 11 | - 3.最终保留了原始`json`文件,方便与生成的`yaml`文件做校对,校对完成可以自行删除; 12 | - 4.需要把原旧版本应用放到一个`"appstore"`文件夹下,脚本与`"appstore"`文件夹是同一级别; 13 | 14 | > 文件夹结构如下 15 | >> "./appstore/旧版本应用" 16 | >> 17 | >> `bat`与`py`脚本 18 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/gen-yml/README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | 这是个`1panel`2.0版本商店生成应用配置`data.yml`的脚本。目的是由表格文档生成`data.yml`,主要使用`python`,`bat`脚本仅仅是用于调用而已。 4 | 5 | 感谢`ChatGPT`。 6 | 7 | 8 | **未完成,半成品** 9 | 10 | # 使用方法 11 | 12 | - 1.如果使用`bat`来运行,注意`py`文件名要和`bat`文件里的相对应,默认已一致; 13 | - 2.三个文件应当在同一个目录 14 | 15 | **抛砖引玉,欢迎大佬们来搞** 16 | 17 | ![](https://github.com/okxlin/ToolScript/blob/main/1Panel/1panel-appstore-2.x/gen-yml/%E9%9C%80%E6%B1%82.png?raw=true) 18 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/gen-yml/app-version.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/gen-yml/app-version.xlsx -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/gen-yml/app-version_xlsx2yml.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/gen-yml/app-version_xlsx2yml.bat -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/gen-yml/app-version_xlsx2yml.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | import yaml 3 | 4 | # 打开Excel文件 5 | workbook = openpyxl.load_workbook('app-version.xlsx') 6 | 7 | # 获取第一个工作表 8 | sheet = workbook.active 9 | 10 | # 读取表格数据 11 | data = [] 12 | for row in sheet.iter_rows(min_row=2, values_only=True): 13 | data.append(row) 14 | 15 | # 构建YAML数据结构 16 | yml_data = { 17 | 'additionalProperties': { 18 | 'formFields': [] 19 | } 20 | } 21 | 22 | # 根据Excel数据生成YAML配置 23 | for row in data: 24 | field = { 25 | 'default': str(row[0]), 26 | 'envKey': row[1], 27 | 'labelEn': row[2], 28 | 'labelZh': row[3], 29 | 'required': bool(row[4]), 30 | 'type': row[5] 31 | } 32 | 33 | if row[6]: 34 | field['random'] = bool(row[6]) 35 | 36 | if row[7]: 37 | field['rule'] = row[7] 38 | 39 | if row[8]: 40 | field['edit'] = bool(row[8]) 41 | 42 | yml_data['additionalProperties']['formFields'].append(field) 43 | 44 | # 生成YAML文件 45 | with open('data.yml', 'w', encoding='utf-8') as f: 46 | yaml.dump(yml_data, f, allow_unicode=True) 47 | 48 | -------------------------------------------------------------------------------- /1Panel/1panel-appstore-2.x/gen-yml/需求.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okxlin/ToolScript/83b926b4600c499cbb2ab72d024ed61dc707a775/1Panel/1panel-appstore-2.x/gen-yml/需求.png -------------------------------------------------------------------------------- /1Panel/1panel-auto-upgrade/.env: -------------------------------------------------------------------------------- 1 | # 通知方式1,不填不影响 2 | WEBHOOK_URL="YOUR_WEBHOOK_URL" # 替换为实际的Webhook URL 3 | 4 | # 通知方式2,不填不影响 5 | TELEGRAM_API_TOKEN="YOUR_TELEGRAM_BOT_API_TOKEN" # 替换为实际的Telegram Bot的API令牌 6 | TELEGRAM_CHAT_ID="YOUR_TELEGRAM_CHAT_ID" # 替换为实际的Telegram聊天ID 7 | 8 | # 通知用机器名备注 9 | HOST_REMARK="Your Host Remark" # 添加主机备注变量 -------------------------------------------------------------------------------- /1Panel/1panel-auto-upgrade/1panel-auto-upgrade.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=1Panel Auto Upgrade Service 3 | 4 | [Service] 5 | ExecStart=/usr/local/1panel-auto-upgrade/1panel-auto-upgrade.sh 6 | WorkingDirectory=/usr/local/1panel-auto-upgrade 7 | User=root 8 | Group=root 9 | Restart=always 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /1Panel/1panel-auto-upgrade/1panel-auto-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 从.env文件中读取配置信息 4 | if [ -f .env ]; then 5 | source .env 6 | else 7 | echo "未找到.env文件,请创建并配置.env文件。" 8 | exit 1 9 | fi 10 | 11 | # 定义一些常量 12 | LAST_CHECKED_VERSION="" 13 | SLEEP_MIN=30 14 | SLEEP_MAX=60 15 | 16 | # 函数:发送Webhook通知 17 | send_webhook_notification() { 18 | local status="$1" # 传入通知状态 19 | local message="$2" # 传入通知消息 20 | 21 | if [[ -z "$WEBHOOK_URL" ]]; then 22 | echo "Webhook URL 未配置,无法发送Webhook通知" 23 | else 24 | local payload="{\"status\":\"$status\",\"message\":\"#1Panel自动升级 $HOST_REMARK - $message\"}" 25 | curl -s -H "Content-Type: application/json" -d "$payload" "$WEBHOOK_URL" 26 | fi 27 | } 28 | 29 | # 函数:发送Telegram通知 30 | send_telegram_notification() { 31 | local message="$1" # 传入通知消息 32 | 33 | if [[ -z "$TELEGRAM_API_TOKEN" ]] || [[ -z "$TELEGRAM_CHAT_ID" ]]; then 34 | echo "Telegram通知未配置,无法发送Telegram通知" 35 | else 36 | local tag="#1Panel自动升级" # 添加标签文本 37 | local telegram_message="$tag $HOST_REMARK - $message" 38 | local url="https://api.telegram.org/bot$TELEGRAM_API_TOKEN/sendMessage" 39 | curl -s -X POST $url -d "chat_id=$TELEGRAM_CHAT_ID" -d "text=$telegram_message" 40 | fi 41 | } 42 | 43 | 44 | # 函数:检查是否具有 root 或 sudo 权限 45 | check_root() { 46 | if [[ $EUID -ne 0 ]]; then 47 | echo "请使用 root 或 sudo 权限运行此脚本" 48 | exit 1 49 | fi 50 | } 51 | 52 | # 函数:检查可用的包管理器 53 | check_package_manager() { 54 | if command -v apt >/dev/null 2>&1; then 55 | # 使用 apt 包管理器(Debian、Ubuntu等) 56 | apt_packages=("sqlite3" "sed" "curl" "tar" "grep") 57 | update_command="apt update" 58 | install_command="apt install -y" 59 | elif command -v yum >/dev/null 2>&1; then 60 | # 使用 yum 包管理器(CentOS、Rocky Linux等) 61 | yum_packages=("sqlite" "sed" "curl" "tar" "grep") 62 | update_command="yum update" 63 | install_command="yum install -y" 64 | else 65 | echo "无法确定包管理器" 66 | exit 1 67 | fi 68 | } 69 | 70 | # 函数:检查 1Panel 是否安装 71 | check_1panel_existence() { 72 | if [[ ! -f /usr/local/bin/1panel ]]; then 73 | echo "1Panel 未安装,不需要执行 1Panel 升级脚本!" 74 | exit 1 75 | fi 76 | } 77 | 78 | # 函数:更新包管理器并安装软件包 79 | update_and_install_packages() { 80 | $update_command 81 | if [[ ${#apt_packages[@]} -gt 0 ]]; then 82 | $install_command "${apt_packages[@]}" 83 | fi 84 | 85 | if [[ ${#yum_packages[@]} -gt 0 ]]; then 86 | $install_command "${yum_packages[@]}" 87 | fi 88 | } 89 | 90 | # 函数:下载 1Panel 91 | download_1panel() { 92 | mkdir -p ~/1pupdate-tmp 93 | cd ~/1pupdate-tmp 94 | PANELVER=$(curl -s https://resource.fit2cloud.com/1panel/package/stable/latest) 95 | INSTALL_MODE="stable" 96 | osCheck=$(uname -a) 97 | if [[ $osCheck =~ 'x86_64' ]]; then 98 | ARCH="amd64" 99 | elif [[ $osCheck =~ 'arm64' ]] || [[ $osCheck =~ 'aarch64' ]]; then 100 | ARCH="arm64" 101 | elif [[ $osCheck =~ 'armv7l' ]]; then 102 | ARCH="armv7" 103 | elif [[ $osCheck =~ 'ppc64le' ]]; then 104 | ARCH="ppc64le" 105 | elif [[ $osCheck =~ 's390x' ]]; then 106 | ARCH="s390x" 107 | else 108 | echo "暂不支持的系统架构,请参阅官方文档,选择受支持的系统。" 109 | exit 1 110 | fi 111 | package_file_name="1panel-${PANELVER}-linux-${ARCH}.tar.gz" 112 | package_download_url="https://resource.fit2cloud.com/1panel/package/${INSTALL_MODE}/${PANELVER}/release/${package_file_name}" 113 | echo "正在尝试下载 ${package_download_url}" 114 | curl -sSL -o ${package_file_name} "$package_download_url" || { 115 | echo "下载失败,切换到备选链接" 116 | package_download_url="https://github.com/wanghe-fit2cloud/1Panel/releases/download/${PANELVER}/${package_file_name}" 117 | echo "正在下载备选链接 ${package_download_url}" 118 | curl -sSL -o ${package_file_name} "$package_download_url" || { 119 | echo "备选链接下载失败" 120 | send_webhook_notification "Failure" "1Panel下载失败" 121 | send_telegram_notification "1Panel下载失败" 122 | exit 1 123 | } 124 | } 125 | tar zxvf ${package_file_name} --strip-components 1 126 | } 127 | 128 | # 函数:更新 1pctl 文件中的 BASE_DIR 129 | update_1pctl_basedir() { 130 | if [[ -f /usr/local/bin/1pctl ]]; then 131 | BASE_DIR=$(grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d '=' -f 2-) 132 | sed -i "s#BASE_DIR=.*#BASE_DIR=$BASE_DIR#" ~/1pupdate-tmp/1pctl 133 | else 134 | echo "/usr/local/bin/1pctl 文件不存在" 135 | exit 1 136 | fi 137 | } 138 | 139 | # 函数:更新数据库 140 | update_database() { 141 | if [[ -f $BASE_DIR/1panel/db/1Panel.db ]]; then 142 | # 备份数据库文件 143 | cp $BASE_DIR/1panel/db/1Panel.db $BASE_DIR/1panel/db/1Panel.db.bak 144 | 145 | # 使用 sqlite3 执行更新操作 146 | sqlite3 $BASE_DIR/1panel/db/1Panel.db < /etc/systemd/system/1panel-auto-upgrade.service < /usr/local/1panel-auto-upgrade/.env </dev/null && command -v 1pctl &>/dev/null; then 14 | echo "1Panel 已经安装在宿主机上,跳过安装步骤。" 15 | exit 0 16 | fi 17 | 18 | echo "1Panel 尚未在宿主机上安装,继续检查数据库文件。" 19 | 20 | read -p "请输入 1Panel 数据所在的顶层目录路径(默认为 /opt): " db_directory 21 | db_directory="${db_directory:-"/opt"}" 22 | local db_file="${db_directory}/1panel/db/1Panel.db" 23 | 24 | if [[ ! -f "$db_file" ]]; then 25 | echo "1Panel 未安装过,不需要执行迁移" 26 | exit 1 27 | else 28 | # 备份数据库文件 29 | local backup_dir="/opt/1panel-bak/db" 30 | mkdir -p "${backup_dir}" 31 | cp "$db_file" "${backup_dir}/1Panel.db" 32 | echo "已经备份旧数据库文件到 ${backup_dir}/1Panel.db" 33 | fi 34 | } 35 | 36 | # 函数:检查并安装缺失的组件 37 | check_and_install_dependencies() { 38 | local dependencies=("curl" "tar" "awk" "ping" "bc" "docker") 39 | local missing_dependencies=() 40 | 41 | for dep in "${dependencies[@]}"; do 42 | if ! command -v "$dep" &>/dev/null; then 43 | missing_dependencies+=("$dep") 44 | fi 45 | done 46 | 47 | if [[ ${#missing_dependencies[@]} -gt 0 ]]; then 48 | echo "缺少以下组件,将尝试安装:" 49 | printf '%s\n' "${missing_dependencies[@]}" 50 | if command -v apt-get &>/dev/null; then 51 | apt-get update && apt-get install -y "${missing_dependencies[@]}" 52 | elif command -v yum &>/dev/null; then 53 | yum install -y "${missing_dependencies[@]}" 54 | elif command -v dnf &>/dev/null; then 55 | dnf install -y "${missing_dependencies[@]}" 56 | else 57 | echo "无法确定包管理器,无法自动安装组件。请手动安装以下组件:" 58 | printf '%s\n' "${missing_dependencies[@]}" 59 | exit 1 60 | fi 61 | fi 62 | } 63 | 64 | # 函数:创建目录 65 | create_dir() { 66 | local panel_dir=~/1panel-install-dir 67 | mkdir -p "${panel_dir}" 68 | cd "${panel_dir}" || { echo "无法进入目录 ${panel_dir},脚本无法继续执行。"; exit 1; } 69 | } 70 | 71 | # 函数:下载 1Panel 72 | download_1panel() { 73 | local os_check=$(uname -a) 74 | local install_mode=${1:-"stable"} 75 | 76 | if [[ $os_check =~ 'x86_64' ]]; then 77 | local architecture="amd64" 78 | elif [[ $os_check =~ 'arm64' ]] || [[ $os_check =~ 'aarch64' ]]; then 79 | local architecture="arm64" 80 | elif [[ $os_check =~ 'armv7l' ]]; then 81 | local architecture="armv7" 82 | elif [[ $os_check =~ 'ppc64le' ]]; then 83 | local architecture="ppc64le" 84 | elif [[ $os_check =~ 's390x' ]]; then 85 | local architecture="s390x" 86 | else 87 | echo "暂不支持的系统架构,请参阅官方文档,选择受支持的系统。" 88 | return 1 89 | fi 90 | 91 | if [[ ${install_mode} != "dev" && ${install_mode} != "stable" ]]; then 92 | echo "请输入正确的安装模式(dev or stable)" 93 | return 1 94 | fi 95 | 96 | local version=$(curl -s https://resource.fit2cloud.com/1panel/package/${install_mode}/latest) 97 | local fit2cloud_url="https://resource.fit2cloud.com/1panel/package/${install_mode}/${version}/release/checksums.txt" 98 | local github_url="https://github.com/wanghe-fit2cloud/1Panel/releases/download/${version}/checksums.txt" 99 | 100 | if [[ "x${version}" == "x" ]]; then 101 | echo "获取最新版本失败,请稍候重试" 102 | return 1 103 | fi 104 | 105 | local package_file_name="1panel-${version}-linux-${architecture}.tar.gz" 106 | 107 | local fit2cloud_latency=$(ping -c 3 -q -w 2 resource.fit2cloud.com | awk -F'/' 'END{print $5}') 108 | local github_latency=$(ping -c 3 -q -w 2 github.com | awk -F'/' 'END{print $5}') 109 | 110 | local package_download_url="" 111 | if [[ $(echo "$fit2cloud_latency < $github_latency" | bc) -eq 1 ]]; then 112 | package_download_url="https://resource.fit2cloud.com/1panel/package/${install_mode}/${version}/release/${package_file_name}" 113 | else 114 | package_download_url="https://github.com/wanghe-fit2cloud/1Panel/releases/download/${version}/${package_file_name}" 115 | fi 116 | 117 | echo "开始下载 1Panel ${version} 版本在线安装包" 118 | echo "选用下载源:" 119 | if [[ $(echo "$fit2cloud_latency < $github_latency" | bc) -eq 1 ]]; then 120 | echo "resource.fit2cloud.com" 121 | else 122 | echo "github.com" 123 | fi 124 | echo "安装包下载地址: ${package_download_url}" 125 | 126 | curl -LOk -o "${package_file_name}" "${package_download_url}" 127 | if [ ! -f ${package_file_name} ]; then 128 | echo "下载安装包失败,请稍候重试。" 129 | cd ~ 130 | rm -rf "${panel_dir}" 131 | exit 1 132 | fi 133 | cd "${panel_dir}" || { echo "无法进入目录 ${panel_dir},脚本无法继续执行。"; exit 1; } 134 | tar zxvf "${package_file_name}" --strip-components 1 135 | if [ $? != 0 ]; then 136 | echo "解压安装包失败,请稍候重试。" 137 | cd ~ 138 | rm -rf "${panel_dir}" 139 | exit 1 140 | fi 141 | } 142 | 143 | # 函数:安装 1Panel 144 | install_1panel() { 145 | local panel_dir=~/1panel-install-dir 146 | cp "${panel_dir}/1panel" /usr/local/bin && chmod +x /usr/local/bin/1panel 147 | cp "${panel_dir}/1pctl" /usr/local/bin && chmod +x /usr/local/bin/1pctl 148 | cp "${panel_dir}/1panel.service" /etc/systemd/system 149 | if [[ ! -f /usr/bin/1panel ]]; then 150 | ln -s /usr/local/bin/1panel /usr/bin/1panel >/dev/null 2>&1 151 | fi 152 | if [[ ! -f /usr/bin/1pctl ]]; then 153 | ln -s /usr/local/bin/1pctl /usr/bin/1pctl >/dev/null 2>&1 154 | fi 155 | systemctl daemon-reload 156 | systemctl enable 1panel 157 | systemctl start 1panel 158 | echo "等待服务启动中…" 159 | sleep 8 160 | if systemctl status 1panel >/dev/null 2>&1; then 161 | echo "1Panel 服务已成功启动。" 162 | else 163 | echo "1Panel 服务启动失败。" 164 | fi 165 | } 166 | 167 | # 函数:移除容器 168 | remove_container() { 169 | local container_exists=false 170 | 171 | while [[ $container_exists == false ]]; do 172 | # 列出已创建容器及其对应镜像 173 | echo "已创建的 Docker 容器及其对应镜像:" 174 | docker ps -a --format "table {{.Names}}\t{{.Image}}" 175 | 176 | read -p "请输入要移除的 1Panel 容器名(默认为 '1panel';输入 'removed' 则表示已经手动移除过,将跳过移除): " container_name 177 | container_name="${container_name:-"1panel"}" 178 | 179 | if [[ $container_name != "removed" ]]; then 180 | if docker inspect "$container_name" &>/dev/null; then 181 | container_exists=true 182 | else 183 | echo "容器 $container_name 不存在。请重新输入。" 184 | fi 185 | fi 186 | done 187 | 188 | if [[ $container_name != "removed" ]]; then 189 | docker stop "$container_name" &>/dev/null 190 | docker rm "$container_name" &>/dev/null 191 | echo "容器 $container_name 已停止并移除。" 192 | fi 193 | } 194 | 195 | # 函数:返回用户根目录并删除临时文件 196 | cleanup_and_notify() { 197 | local panel_dir=~/1panel-install-dir 198 | cd ~ 199 | rm -rf "${panel_dir}" 200 | echo "1Panel 已经由 Docker 运行方式切换到宿主机直接运行" 201 | } 202 | 203 | # 调用函数 204 | function main(){ 205 | check_root 206 | check_1panel_installed 207 | check_and_install_dependencies 208 | create_dir 209 | download_1panel 210 | remove_container 211 | install_1panel 212 | cleanup_and_notify 213 | } 214 | main 215 | -------------------------------------------------------------------------------- /1Panel/1panel-restore/1panel_quick_restore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 出错立即退出 3 | set -e 4 | 5 | ###################################### 6 | # 用法说明 7 | # 示例(带认证,使用默认版本): 8 | # ./1panel_quick_restore.sh -u "http://your.alist.site/path/opt-1panel-data.zip" \ 9 | # -a "your_username" -p "your_password" -z "your_zip_password" 10 | # 11 | # 示例(不需要 HTTP 认证,并且获取最新 global 版本): 12 | # ./1panel_quick_restore.sh -u "http://your.alist.site/path/opt-1panel-data.zip" \ 13 | # -a "" -p "" -z "your_zip_password" -i latest 14 | # 15 | # 示例(手工指定其他版本): 16 | # ./1panel_quick_restore.sh -u "http://your.alist.site/path/opt-1panel-data.zip" \ 17 | # -a "user" -p "pass" -z "zip_password" -i "moelin/1panel:custom-version" 18 | ###################################### 19 | usage() { 20 | echo "Usage: $0 -u -a -p -z [-i ]" 21 | exit 1 22 | } 23 | 24 | # 默认 Docker 镜像版本 25 | DOCKER_IMAGE="moelin/1panel:global-v1.10.29-lts" 26 | 27 | # 解析命令行参数 28 | while getopts "u:a:p:z:i:" opt; do 29 | case "$opt" in 30 | u) FILE_URL="$OPTARG" ;; 31 | a) HTTP_USER="$OPTARG" ;; 32 | p) HTTP_PASS="$OPTARG" ;; 33 | z) ZIP_PASS="$OPTARG" ;; 34 | i) DOCKER_IMAGE="$OPTARG" ;; 35 | *) usage ;; 36 | esac 37 | done 38 | 39 | # 检查必须参数:file_url 与 zip_password 必须提供(HTTP认证信息可为空) 40 | if [ -z "$FILE_URL" ] || [ -z "$ZIP_PASS" ]; then 41 | usage 42 | fi 43 | 44 | ###################################### 45 | # 函数:安全转义参数(替代 printf "%q") 46 | ###################################### 47 | shquote() { 48 | printf "'%s' " "$(printf "%s" "$1" | sed "s/'/'\\\\''/g")" 49 | } 50 | 51 | ###################################### 52 | # 函数:从 Docker Hub 查询最新符合 global-v*-lts 格式的镜像标签 53 | ###################################### 54 | get_latest_global_version() { 55 | # 获取最多10个标签的 JSON 数据 56 | json=$(wget -qO- "https://registry.hub.docker.com/v2/repositories/moelin/1panel/tags/?page_size=10") 57 | latest=$(echo "$json" | jq -r '.results[] | select(.name | test("global-v.*-lts")) | .name' | sort -V | tail -n1) 58 | if [ -n "$latest" ]; then 59 | echo "moelin/1panel:$latest" 60 | else 61 | echo "moelin/1panel:global-v1.10.29-lts" 62 | fi 63 | } 64 | 65 | ###################################### 66 | # 函数:安装缺失的软件包(自动检测包管理器) 67 | ###################################### 68 | install_pkg() { 69 | pkg="$1" 70 | if command -v apt-get >/dev/null 2>&1; then 71 | echo "使用 apt-get 安装 $pkg ..." 72 | apt-get update && apt-get install -y "$pkg" 73 | elif command -v yum >/dev/null 2>&1; then 74 | echo "使用 yum 安装 $pkg ..." 75 | yum install -y "$pkg" 76 | elif command -v apk >/dev/null 2>&1; then 77 | echo "使用 apk 安装 $pkg ..." 78 | apk add --no-cache "$pkg" 79 | else 80 | echo "错误:无法识别的包管理器,请手动安装 $pkg." >&2 81 | exit 1 82 | fi 83 | } 84 | 85 | ###################################### 86 | # 函数:检测是否以 root 身份运行 87 | ###################################### 88 | check_root() { 89 | if [ "$(id -u)" -ne 0 ]; then 90 | echo "错误:需要以 root 身份运行此脚本!" >&2 91 | exit 1 92 | fi 93 | } 94 | 95 | ###################################### 96 | # 函数:检测系统资源(磁盘空间、内存) 97 | # 磁盘检查始终执行;内存检测仅在非容器环境下进行 98 | ###################################### 99 | check_system_resources() { 100 | free_space_kb=$(df -k / | awk 'NR==2 {print $4}') 101 | min_space_kb=1572864 102 | if [ "$free_space_kb" -lt "$min_space_kb" ]; then 103 | free_space_mb=$(echo "$free_space_kb / 1024" | awk '{printf "%.1f", $1}') 104 | echo "警告:根目录剩余磁盘空间不足!当前剩余:${free_space_mb} MB (要求至少1536 MB)。是否退出?[y/N]" 105 | read answer 106 | case "$answer" in 107 | [Yy]* ) exit 1 ;; 108 | * ) echo "继续执行..." ;; 109 | esac 110 | fi 111 | 112 | if [ ! -f /.dockerenv ]; then 113 | if [ -r /proc/meminfo ]; then 114 | total_mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') 115 | else 116 | echo "无法获取内存信息,请确认 /proc/meminfo 是否存在。" >&2 117 | exit 1 118 | fi 119 | min_mem_kb=$((384 * 1024)) 120 | if [ "$total_mem_kb" -lt "$min_mem_kb" ]; then 121 | total_mem_mb=$(echo "$total_mem_kb / 1024" | awk '{printf "%.1f", $1}') 122 | echo "警告:总内存不足!当前总内存:${total_mem_mb} MB (要求至少384 MB)。是否退出?[y/N]" 123 | read answer 124 | case "$answer" in 125 | [Yy]* ) exit 1 ;; 126 | * ) echo "继续执行..." ;; 127 | esac 128 | fi 129 | fi 130 | } 131 | 132 | ###################################### 133 | # 函数:检查必要程序是否存在 134 | # 对于 wget、unzip,缺失时自动安装;对于 docker 和 docker-compose 提示用户 135 | ###################################### 136 | check_programs() { 137 | for prog in jq wget unzip; do 138 | if ! command -v "$prog" >/dev/null 2>&1; then 139 | echo "程序 \"$prog\" 未安装,尝试自动安装..." 140 | install_pkg "$prog" 141 | else 142 | echo "程序 \"$prog\" 已安装。" 143 | fi 144 | done 145 | 146 | for prog in docker; do 147 | if ! command -v "$prog" >/dev/null 2>&1; then 148 | echo "错误:必需的程序 \"$prog\" 未安装。请先安装 $prog。" >&2 149 | exit 1 150 | fi 151 | done 152 | 153 | if ! command -v docker-compose >/dev/null 2>&1; then 154 | echo "警告:docker-compose 未安装。在 Alpine 下,通常需要通过 'docker-compose-plugin' 安装。" >&2 155 | fi 156 | } 157 | 158 | ###################################### 159 | # 函数:检查/opt/1panel/目录 160 | # 如果目录非空,则提示用户是否清空目录,删除内容时采用兼容 BusyBox 的方式删除所有文件(包括隐藏文件) 161 | ###################################### 162 | check_directory() { 163 | target_dir="/opt/1panel/" 164 | if [ -d "$target_dir" ]; then 165 | if [ "$(ls -A "$target_dir")" ]; then 166 | echo "目录 $target_dir 非空。是否删除所有内容?[y/N]" 167 | read answer 168 | case "$answer" in 169 | [Yy]* ) 170 | echo "正在删除 $target_dir 下的所有内容..." 171 | find "$target_dir" -mindepth 1 -delete 2>/dev/null || \ 172 | find "$target_dir" -path "$target_dir" -prune -o -exec rm -rf {} + 173 | ;; 174 | * ) 175 | echo "请清空目录 $target_dir 后重新运行脚本。" >&2 176 | exit 1 177 | ;; 178 | esac 179 | fi 180 | else 181 | mkdir -p "$target_dir" 182 | fi 183 | } 184 | 185 | ###################################### 186 | # 函数:下载文件并解压到 /opt/1panel/ 187 | # 使用新的 shquote() 函数处理参数转义 188 | ###################################### 189 | download_and_unzip() { 190 | target_dir="/opt/1panel/" 191 | echo "开始下载 opt-1panel-data.zip..." 192 | cd "$target_dir" 193 | 194 | wget_opts="" 195 | if [ -n "$HTTP_USER" ] || [ -n "$HTTP_PASS" ]; then 196 | # 使用安全转义函数 197 | escaped_user=$(shquote "$HTTP_USER") 198 | escaped_pass=$(shquote "$HTTP_PASS") 199 | wget_opts="--user=${escaped_user} --password=${escaped_pass}" 200 | fi 201 | 202 | wget $wget_opts "$FILE_URL" -O opt-1panel-data.zip 203 | if [ $? -ne 0 ]; then 204 | echo "错误:文件下载失败。" >&2 205 | exit 1 206 | fi 207 | 208 | echo "开始解压 opt-1panel-data.zip 到 $target_dir..." 209 | unzip -q -o -P "$ZIP_PASS" opt-1panel-data.zip 210 | if [ $? -ne 0 ]; then 211 | echo "错误:解压文件失败。" >&2 212 | exit 1 213 | fi 214 | } 215 | 216 | ###################################### 217 | # 函数:安装 docker-1panel 容器 218 | # 挂载目录使用 $HOME 代替硬编码 /root 219 | ###################################### 220 | install_docker_1panel() { 221 | echo "正在安装 docker-1panel 容器..." 222 | docker run -d \ 223 | --name 1panel \ 224 | --restart always \ 225 | --network host \ 226 | -v /var/run/docker.sock:/var/run/docker.sock \ 227 | -v /var/lib/docker/volumes:/var/lib/docker/volumes \ 228 | -v /opt:/opt \ 229 | -v "$HOME":/root \ 230 | -v /etc/docker:/etc/docker \ 231 | -e TZ=Asia/Shanghai \ 232 | "$DOCKER_IMAGE" 233 | if [ $? -ne 0 ]; then 234 | echo "错误:docker-1panel 容器安装失败。" >&2 235 | exit 1 236 | fi 237 | } 238 | 239 | ###################################### 240 | # 函数:递归查找 /opt/1panel/apps 下的 docker-compose.yml,并执行 docker-compose up -d 241 | ###################################### 242 | traverse_and_start_docker_compose() { 243 | base_dir="/opt/1panel/apps" 244 | if [ ! -d "$base_dir" ]; then 245 | echo "注意:目录 $base_dir 不存在,跳过 docker-compose 部分。" >&2 246 | return 247 | fi 248 | 249 | echo "查找目录 $base_dir 下的 docker-compose.yml,并启动对应服务..." 250 | find "$base_dir" -type f -name "docker-compose.yml" | while read compose_file; do 251 | compose_dir=$(dirname "$compose_file") 252 | echo "在目录 $compose_dir 下执行 docker-compose up -d" 253 | (cd "$compose_dir" && docker-compose up -d) 254 | done 255 | } 256 | 257 | ###################################### 258 | # 函数:任务完成后删除下载的压缩包 259 | ###################################### 260 | cleanup() { 261 | target_dir="/opt/1panel/" 262 | cd "$target_dir" 263 | if [ -f "opt-1panel-data.zip" ]; then 264 | echo "正在删除下载的压缩包 opt-1panel-data.zip..." 265 | rm -f opt-1panel-data.zip 266 | fi 267 | } 268 | 269 | ###################################### 270 | # 主函数 271 | ###################################### 272 | main() { 273 | check_root 274 | check_programs 275 | 276 | # 统一处理 latest 参数 277 | if [ "$DOCKER_IMAGE" = "latest" ]; then 278 | echo "正在从 Docker Hub 查询最新符合 global-v*-lts 格式的版本..." 279 | DOCKER_IMAGE=$(get_latest_global_version) 280 | echo "获取到 Docker 镜像:$DOCKER_IMAGE" 281 | fi 282 | 283 | if [ ! -f /.dockerenv ]; then 284 | check_system_resources 285 | fi 286 | check_directory 287 | download_and_unzip 288 | install_docker_1panel 289 | traverse_and_start_docker_compose 290 | cleanup 291 | echo "所有操作已成功执行。" 292 | } 293 | 294 | # 执行主函数 295 | main 296 | -------------------------------------------------------------------------------- /1Panel/1panel-upgrade/1panel-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查是否具有 root 或 sudo 权限 4 | check_root() { 5 | if [[ $EUID -ne 0 ]]; then 6 | echo "请使用 root 或 sudo 权限运行此脚本" 7 | exit 1 8 | fi 9 | } 10 | 11 | # 确认是否执行升级脚本 12 | confirm_upgrade() { 13 | while true; do 14 | read -p "是否执行 1Panel 升级操作?(y是/n否/q退出): " choice 15 | case "$choice" in 16 | [yY]) return 0 ;; 17 | [nN]) return 1 ;; 18 | [qQ]) 19 | echo "已取消升级并退出脚本" 20 | exit 0 21 | ;; 22 | *) 23 | echo "无效输入,请输入 y/n/q(不区分大小写)" 24 | ;; 25 | esac 26 | done 27 | } 28 | 29 | # 询问用户选择版本 30 | select_version_type() { 31 | while true; do 32 | read -p "请选择 1Panel 版本(1国际版/2国内版/q退出): " version_choice 33 | case "$version_choice" in 34 | 1) 35 | VERSION_TYPE="international" 36 | package_url_prefix="https://resource.1panel.pro" 37 | break 38 | ;; 39 | 2) 40 | VERSION_TYPE="domestic" 41 | package_url_prefix="https://resource.fit2cloud.com/1panel/package" 42 | break 43 | ;; 44 | [qQ]) 45 | echo "已取消版本选择并退出脚本" 46 | exit 0 47 | ;; 48 | *) 49 | echo "无效输入,请输入 1/2/q(不区分大小写)" 50 | ;; 51 | esac 52 | done 53 | } 54 | 55 | # 获取当前1Panel版本和模式 56 | get_current_version_and_mode() { 57 | if [[ -f /usr/local/bin/1panel ]]; then 58 | CURRENT_VERSION=$(1panel version | grep -oP 'version:\s*\K.*') 59 | CURRENT_MODE=$(1panel version | grep -oP 'mode:\s*\K.*') 60 | if [[ -z "$CURRENT_MODE" ]]; then 61 | echo "获取当前 1Panel 版本模式失败" 62 | exit 1 63 | fi 64 | else 65 | echo "无法获取当前1Panel版本和模式" 66 | exit 1 67 | fi 68 | } 69 | 70 | # 获取最新的1Panel版本 71 | get_latest_version() { 72 | if [[ $VERSION_TYPE == "international" ]]; then 73 | version_url="https://resource.1panel.pro/stable/latest" # 国际版仅有此版本 74 | elif [[ $VERSION_TYPE == "domestic" ]]; then 75 | version_url="https://resource.fit2cloud.com/1panel/package/${CURRENT_MODE}/latest" # 国内版使用 CURRENT_MODE 76 | fi 77 | PANELVER=$(curl -fsSL "$version_url") 78 | } 79 | 80 | # 比较版本 81 | compare_versions() { 82 | if [[ $CURRENT_VERSION == $PANELVER ]]; then 83 | echo "当前版本 $CURRENT_VERSION 已是最新版本 $PANELVER" 84 | exit 0 85 | fi 86 | } 87 | 88 | # 检查可用的包管理器 89 | check_package_manager() { 90 | if command -v apt >/dev/null 2>&1; then 91 | # 使用 apt 包管理器(Debian、Ubuntu等) 92 | packages=("sqlite3" "sed" "curl" "tar" "grep") 93 | update_command="apt update" 94 | install_command="apt install -y" 95 | elif command -v yum >/dev/null 2>&1; then 96 | # 使用 yum 包管理器(CentOS、Rocky Linux等) 97 | packages=("sqlite" "sed" "curl" "tar" "grep") 98 | update_command="yum update" 99 | install_command="yum install -y" 100 | else 101 | echo "无法确定包管理器" 102 | exit 1 103 | fi 104 | } 105 | 106 | # 检查 1Panel 是否安装 107 | check_1panel_existence() { 108 | if [[ ! -f /usr/local/bin/1panel ]]; then 109 | echo "1Panel 未安装,不需要执行 1Panel 升级脚本!" 110 | exit 1 111 | fi 112 | } 113 | 114 | # 更新包管理器并安装软件包 115 | update_and_install_packages() { 116 | if ! $update_command; then 117 | echo "包管理器更新失败!" 118 | exit 1 119 | fi 120 | if ! $install_command "${packages[@]}"; then 121 | echo "依赖安装失败!" 122 | exit 1 123 | fi 124 | } 125 | 126 | 127 | # 下载 1Panel 128 | download_1panel() { 129 | mkdir -p ~/1pupdate-tmp 130 | cd ~/1pupdate-tmp 131 | osCheck=$(uname -a) 132 | if [[ $osCheck =~ 'x86_64' ]]; then 133 | ARCH="amd64" 134 | elif [[ $osCheck =~ 'arm64' ]] || [[ $osCheck =~ 'aarch64' ]]; then 135 | ARCH="arm64" 136 | elif [[ $osCheck =~ 'armv7l' ]]; then 137 | ARCH="armv7" 138 | elif [[ $osCheck =~ 'ppc64le' ]]; then 139 | ARCH="ppc64le" 140 | elif [[ $osCheck =~ 's390x' ]]; then 141 | ARCH="s390x" 142 | else 143 | echo "暂不支持的系统架构,请参阅官方文档,选择受支持的系统。" 144 | exit 1 145 | fi 146 | package_file_name="1panel-${PANELVER}-linux-${ARCH}.tar.gz" 147 | package_download_url="${package_url_prefix}/${CURRENT_MODE}/${PANELVER}/release/${package_file_name}" 148 | echo "正在尝试下载 ${package_download_url}" 149 | curl -sSL -o ${package_file_name} "$package_download_url" || { 150 | echo "下载失败" 151 | exit 1 152 | } 153 | tar zxvf ${package_file_name} --strip-components 1 || { echo "解压失败"; exit 1; } 154 | } 155 | 156 | # 更新 1pctl 文件中的 BASE_DIR 157 | update_1pctl_basedir() { 158 | if [[ -f /usr/local/bin/1pctl ]]; then 159 | BASE_DIR=$(grep '^BASE_DIR=' /usr/local/bin/1pctl | cut -d '=' -f 2-) 160 | sed -i "s#BASE_DIR=.*#BASE_DIR=$BASE_DIR#" ~/1pupdate-tmp/1pctl 161 | else 162 | echo "/usr/local/bin/1pctl 文件不存在" 163 | exit 1 164 | fi 165 | } 166 | 167 | # 更新数据库 168 | update_database() { 169 | if [[ -f $BASE_DIR/1panel/db/1Panel.db ]]; then 170 | # 备份数据库文件 171 | cp $BASE_DIR/1panel/db/1Panel.db $BASE_DIR/1panel/db/1Panel.db.bak 172 | 173 | # 使用 sqlite3 执行更新操作 174 | sqlite3 $BASE_DIR/1panel/db/1Panel.db < "$cert_dir/private.ext" 59 | [ req ] 60 | default_bits = 2048 61 | distinguished_name = req_distinguished_name 62 | req_extensions = san 63 | extensions = san 64 | 65 | [ req_distinguished_name ] 66 | countryName = US 67 | stateOrProvinceName = California 68 | localityName = Los Angeles 69 | organizationName = My Organization 70 | commonName = ${input_array[0]} 71 | 72 | [SAN] 73 | subjectAltName = ${subject_alt_name} 74 | EOF 75 | 76 | # 使用 CSR 文件和 private.ext、以及根证书 CA.crt 创建证书 private.crt(将-days参数替换为${ssl_days}) 77 | openssl x509 -req -days "${ssl_days}" -in "$cert_dir/private.csr" -CA "$cert_dir/CA.crt" -CAkey "$cert_dir/CA.key" -CAcreateserial -sha256 -out "$cert_dir/private.crt" -extfile "$cert_dir/private.ext" -extensions SAN 78 | 79 | # 生成 Fullchain 证书文件 80 | cat "$cert_dir/private.crt" "$cert_dir/CA.crt" > "$cert_dir/fullchain.crt" 81 | 82 | # 生成 PKCS#12 格式证书文件 83 | openssl pkcs12 -export -out "$cert_dir/certificate.p12" -inkey "$cert_dir/private.key" -in "$cert_dir/private.crt" -certfile "$cert_dir/CA.crt" -passout pass:"$password" 84 | 85 | # 删除不再需要的临时文件 86 | rm "$cert_dir/private.csr" "$cert_dir/private.ext" "$cert_dir/CA.srl" 87 | 88 | # 提示证书文件创建完毕 89 | echo "ECDSA 证书文件生成完毕。" 90 | echo "存储在目录: $cert_dir" 91 | -------------------------------------------------------------------------------- /tools/generate-frp-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 提示用户输入域名列表或IP列表(以逗号分隔) 4 | read -p "请输入域名列表或IP列表(以逗号分隔): " input_list 5 | 6 | # 将逗号分隔的域名或IP列表转换为数组 7 | IFS=',' read -ra input_array <<< "$input_list" 8 | 9 | # 生成 subjectAltName 字段的内容 10 | san_entries=() 11 | for domain_or_ip in "${input_array[@]}"; do 12 | if [[ $domain_or_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 13 | san_entries+=("IP:${domain_or_ip}") 14 | else 15 | san_entries+=("DNS:${domain_or_ip}") 16 | fi 17 | done 18 | subject_alt_name=$(IFS=','; echo "${san_entries[*]}") 19 | 20 | # 使用第一个输入项作为文件夹名称 21 | cert_dir="frp-certs-${input_array[0]}" 22 | 23 | # 提示用户输入SSL证书的有效期(默认为100年) 24 | read -p "请输入SSL证书有效期天数(默认为100年,即36500天): " ssl_days 25 | ssl_days=${ssl_days:-36500} # 如果用户未输入,则使用默认值36500天(100年) 26 | 27 | # 检查是否存在旧的文件夹,如果存在,向用户进行确认 28 | if [ -d "$cert_dir" ]; then 29 | read -p "发现旧的文件夹 '$cert_dir',是否确认删除并继续 (y/n)? " confirm 30 | if [ "$confirm" = "y" ]; then 31 | rm -rf "$cert_dir"/* # 删除文件夹中的所有内容,但不删除文件夹本身 32 | else 33 | echo "操作已取消。" 34 | exit 1 35 | fi 36 | fi 37 | 38 | # 创建存储证书的文件夹 39 | mkdir -p "$cert_dir" 40 | 41 | # 创建openssl配置文件 42 | cat > "$cert_dir/openssl.cnf" << EOF 43 | [ ca ] 44 | default_ca = CA_default 45 | [ CA_default ] 46 | x509_extensions = usr_cert 47 | [ req ] 48 | default_md = sha384 49 | default_keyfile = privkey.pem 50 | distinguished_name = req_distinguished_name 51 | attributes = req_attributes 52 | x509_extensions = v3_ca 53 | string_mask = utf8only 54 | [ req_distinguished_name ] 55 | [ req_attributes ] 56 | [ usr_cert ] 57 | basicConstraints = CA:FALSE 58 | nsComment = "OpenSSL Generated Certificate" 59 | subjectKeyIdentifier = hash 60 | authorityKeyIdentifier = keyid,issuer 61 | [ v3_ca ] 62 | subjectKeyIdentifier = hash 63 | authorityKeyIdentifier = keyid:always,issuer 64 | basicConstraints = CA:true 65 | EOF 66 | 67 | # 生成根CA证书 68 | openssl ecparam -genkey -name secp384r1 -out "$cert_dir/ca.key" 69 | openssl req -x509 -new -nodes -key "$cert_dir/ca.key" -subj "/CN=example.ca.com" -days 36500 -sha384 -out "$cert_dir/ca.crt" 70 | 71 | # 生成frps证书 72 | openssl ecparam -genkey -name secp384r1 -out "$cert_dir/server.key" 73 | openssl req -new -sha384 -key "$cert_dir/server.key" \ 74 | -subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=${input_array[0]}" \ 75 | -reqexts SAN \ 76 | -config <(cat "$cert_dir/openssl.cnf" <(printf "\n[SAN]\nsubjectAltName=${subject_alt_name}")) \ 77 | -out "$cert_dir/server.csr" 78 | openssl x509 -req -days "${ssl_days}" -sha384 \ 79 | -in "$cert_dir/server.csr" -CA "$cert_dir/ca.crt" -CAkey "$cert_dir/ca.key" -CAcreateserial \ 80 | -extfile <(printf "subjectAltName=${subject_alt_name}") \ 81 | -out "$cert_dir/server.crt" 82 | 83 | # 生成frpc证书 84 | openssl ecparam -genkey -name secp384r1 -out "$cert_dir/client.key" 85 | openssl req -new -sha384 -key "$cert_dir/client.key" \ 86 | -subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=client.com" \ 87 | -reqexts SAN \ 88 | -config <(cat "$cert_dir/openssl.cnf" <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \ 89 | -out "$cert_dir/client.csr" 90 | openssl x509 -req -days "${ssl_days}" -sha384 \ 91 | -in "$cert_dir/client.csr" -CA "$cert_dir/ca.crt" -CAkey "$cert_dir/ca.key" -CAcreateserial \ 92 | -extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \ 93 | -out "$cert_dir/client.crt" 94 | 95 | # 删除不再需要的临时文件 96 | rm "$cert_dir/server.csr" "$cert_dir/client.csr" "$cert_dir/openssl.cnf" "$cert_dir/ca.srl" 97 | 98 | # 提示证书文件创建完毕 99 | echo "证书文件生成完毕。" 100 | echo "存储在目录: $cert_dir" 101 | 102 | 103 | -------------------------------------------------------------------------------- /tools/generate-rsa-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 提示用户输入域名或IP 4 | read -p "请输入域名或IP: " name 5 | 6 | # 提示用户输入SSL证书的有效期(默认为100年) 7 | read -p "请输入SSL证书有效期天数(默认为100年,即36500天): " ssl_days 8 | ssl_days=${ssl_days:-36500} # 如果用户未输入,则使用默认值36500天(100年) 9 | 10 | # 检查用户输入是否为IP地址 11 | if [[ $name =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 12 | subject_alt_name="IP:${name}" 13 | else 14 | subject_alt_name="DNS:${name}" 15 | fi 16 | 17 | # 生成的文件夹名称 18 | cert_dir="${name}-ssl-rsa" 19 | 20 | # 检查是否存在旧的文件夹,如果存在,向用户进行确认 21 | if [ -d "$cert_dir" ]; then 22 | read -p "发现旧的文件夹 '$cert_dir',是否确认删除并继续 (y/n)? " confirm 23 | if [ "$confirm" = "y" ]; then 24 | rm -rf "$cert_dir"/* # 删除文件夹中的所有内容,但不删除文件夹本身 25 | else 26 | echo "操作已取消。" 27 | exit 1 28 | fi 29 | fi 30 | 31 | # 创建存储证书的文件夹 32 | mkdir -p "$cert_dir" 33 | 34 | # 创建CA私钥 35 | openssl genpkey -algorithm RSA -out "$cert_dir/CA.key" 36 | 37 | # 创建CA自签名证书 38 | openssl req -new -x509 -days "$ssl_days" -key "$cert_dir/CA.key" -out "$cert_dir/CA.crt" -subj "/C=US/ST=California/L=Los Angeles/O=My Organization/CN=${name}" 39 | 40 | # 创建证书的RSA私钥 41 | openssl genpkey -algorithm RSA -out "$cert_dir/private.key" -pkeyopt rsa_keygen_bits:4096 42 | 43 | # 提示用户输入 PKCS#12 格式证书密码 44 | read -s -p "请输入 PKCS#12 格式证书密码: " password 45 | echo 46 | 47 | # 创建证书请求文件 csr 48 | openssl req -new -key "$cert_dir/private.key" -subj "/C=US/ST=California/L=Los Angeles/O=My Organization/CN=${name}" -sha256 -out "$cert_dir/private.csr" 49 | 50 | # 生成 private.ext 文件 51 | cat < "$cert_dir/private.ext" 52 | [ req ] 53 | default_bits = 2048 54 | distinguished_name = req_distinguished_name 55 | req_extensions = san 56 | extensions = san 57 | 58 | [ req_distinguished_name ] 59 | countryName = US 60 | stateOrProvinceName = California 61 | localityName = Los Angeles 62 | organizationName = My Organization 63 | commonName = ${name} 64 | 65 | [SAN] 66 | subjectAltName = ${subject_alt_name} 67 | EOF 68 | 69 | # 使用 CSR 文件和 private.ext、以及根证书 CA.crt 创建证书 private.crt 70 | openssl x509 -req -days "$ssl_days" -in "$cert_dir/private.csr" -CA "$cert_dir/CA.crt" -CAkey "$cert_dir/CA.key" -CAcreateserial -sha256 -out "$cert_dir/private.crt" -extfile "$cert_dir/private.ext" -extensions SAN 71 | 72 | # 生成 Fullchain 证书文件 73 | cat "$cert_dir/private.crt" "$cert_dir/CA.crt" > "$cert_dir/fullchain.crt" 74 | 75 | # 生成 PKCS#12 格式证书文件 76 | openssl pkcs12 -export -out "$cert_dir/certificate.p12" -inkey "$cert_dir/private.key" -in "$cert_dir/private.crt" -certfile "$cert_dir/CA.crt" -passout pass:"$password" 77 | 78 | # 删除不再需要的临时文件 79 | rm "$cert_dir/private.csr" "$cert_dir/private.ext" "$cert_dir/CA.srl" 80 | 81 | # 提示证书文件创建完毕 82 | echo "RSA 证书文件生成完毕。" 83 | echo "存储在目录: $cert_dir" 84 | -------------------------------------------------------------------------------- /tools/generate-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 生成证书的信息 4 | COUNTRY="US" 5 | STATE="Los Angeles" 6 | LOCALITY="Los Angeles" 7 | ORGANIZATION="Your Organization" 8 | ORG_UNIT="Your Organizational Unit" 9 | COMMON_NAME="example.com" 10 | EMAIL="admin@example.com" 11 | 12 | # 生成私钥 13 | openssl genrsa -out $COMMON_NAME.key 4096 14 | 15 | # 生成 CSR 16 | openssl req -new \ 17 | -key $COMMON_NAME.key \ 18 | -out $COMMON_NAME.csr \ 19 | -subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/OU=$ORG_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL" 20 | 21 | # 生成自签名证书 22 | openssl x509 -req \ 23 | -days 36500 \ 24 | -in $COMMON_NAME.csr \ 25 | -signkey $COMMON_NAME.key \ 26 | -out $COMMON_NAME.crt 27 | 28 | # 清理 CSR 文件 29 | rm $COMMON_NAME.csr 30 | 31 | # 输出结果 32 | echo "SSL 证书已创建:$COMMON_NAME.crt" 33 | echo "SSL 私钥已创建:$COMMON_NAME.key" 34 | -------------------------------------------------------------------------------- /tools/timesync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查是否以root身份运行脚本 4 | check_root() { 5 | if [ "$EUID" -ne 0 ]; then 6 | echo "请以root身份或使用sudo运行此脚本" 7 | exit 1 8 | fi 9 | } 10 | 11 | # 更新软件包列表 12 | update_package_list() { 13 | if [ -x "$(command -v apt-get)" ]; then 14 | apt-get update 15 | elif [ -x "$(command -v yum)" ]; then 16 | yum update 17 | else 18 | echo "不支持的Linux发行版,无法确定包管理器类型" 19 | exit 1 20 | fi 21 | } 22 | 23 | # 检查并安装软件包 24 | install_packages() { 25 | local packages="tzdata curl ntpdate" 26 | 27 | if [ -x "$(command -v dpkg-query)" ]; then 28 | # Debian/Ubuntu系统 29 | for pkg in $packages; do 30 | if ! dpkg-query -W -f='${Status}' $pkg 2>/dev/null | grep -q "ok installed"; then 31 | apt-get install -y $pkg 32 | else 33 | echo "$pkg 已经安装,跳过安装步骤。" 34 | fi 35 | done 36 | elif [ -x "$(command -v rpm)" ]; then 37 | # CentOS/RHEL系统 38 | for pkg in $packages; do 39 | if ! rpm -q $pkg &>/dev/null; then 40 | yum install -y $pkg 41 | else 42 | echo "$pkg 已经安装,跳过安装步骤。" 43 | fi 44 | done 45 | else 46 | echo "不支持的Linux发行版,无法确定包管理器类型" 47 | exit 1 48 | fi 49 | } 50 | 51 | 52 | # 检测并同步时间到时间服务器 53 | sync_time() { 54 | echo "正在同步系统时间到苹果的时间服务器..." 55 | ntpdate -u time.apple.com 56 | } 57 | 58 | # 设置系统时间 59 | set_system_time() { 60 | # 从网站获取GMT时间,并提取日期和时间部分 61 | GMT_TIME=$(curl -I 'https://www.apple.com/' 2>/dev/null | grep -i '^date:' | awk -F ' ' '{print $3 " " $4 " " $5 " " $6}') 62 | 63 | # 将GMT时间转换为时间戳 64 | GMT_TIMESTAMP=$(date -d "$GMT_TIME" '+%s') 65 | 66 | # 计算CST(中国标准时间)时间戳(GMT时间戳 + 8小时的秒数) 67 | CST_TIMESTAMP=$((GMT_TIMESTAMP + 8*3600)) 68 | 69 | # 将CST时间戳转换为CST时间并设置为系统时间 70 | TZ='Asia/Shanghai' date -s "@$CST_TIMESTAMP" 71 | } 72 | 73 | # 调整时区到上海 74 | adjust_timezone() { 75 | timedatectl set-timezone Asia/Shanghai 76 | echo "时区已设置为Asia/Shanghai" 77 | } 78 | 79 | # 主函数 80 | main() { 81 | check_root 82 | update_package_list 83 | install_packages 84 | adjust_timezone 85 | sync_time 86 | set_system_time 87 | 88 | # 获取当前时间 89 | current_time=$(date) 90 | 91 | # 输出提示消息:时间同步完成和当前时间 92 | echo "时间同步操作完成。当前时间:$current_time" 93 | } 94 | 95 | # 执行主函数 96 | main 97 | -------------------------------------------------------------------------------- /tools/vps-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 检查是否以root身份运行脚本 4 | check_root() { 5 | if [ "$EUID" -ne 0 ]; then 6 | echo "请以root身份或使用sudo运行此脚本 (Please run this script as root or using sudo)" 7 | exit 1 8 | fi 9 | } 10 | 11 | # 同步系统时间 12 | sync_system_time() { 13 | # 输出提示消息:时间同步 14 | echo "同步系统时间... (Syncing system time...)" 15 | 16 | # 检查是否安装了curl,如果没有,使用包管理器安装它 17 | if ! command -v curl &>/dev/null; then 18 | echo "未找到curl。将尝试安装... (curl not found. Attempting to install...)" 19 | $curl_install_command 20 | fi 21 | 22 | # 更新包管理器的软件包列表 23 | echo "正在更新包管理器的软件包列表... (Updating package manager's package list...)" 24 | $update_command 25 | 26 | # 安装tzdata包,用于设置系统时区 27 | echo "正在安装tzdata包... (Installing tzdata package...)" 28 | $install_command 29 | 30 | # 从网站获取GMT时间,并提取日期和时间部分 31 | GMT_TIME=$(curl -I 'https://www.apple.com/' 2>/dev/null | grep -i '^date:' | awk -F ' ' '{print $3 " " $4 " " $5 " " $6}') 32 | 33 | # 将GMT时间转换为时间戳 34 | GMT_TIMESTAMP=$(date -d "$GMT_TIME" '+%s') 35 | 36 | # 计算CST(中国标准时间)时间戳(GMT时间戳 + 8小时的秒数) 37 | CST_TIMESTAMP=$((GMT_TIMESTAMP + 8*3600)) 38 | 39 | # 将CST时间戳转换为CST时间并设置为系统时间 40 | TZ='Asia/Shanghai' date -s "@$CST_TIMESTAMP" 41 | 42 | # 获取当前时间 43 | current_time=$(date) 44 | 45 | # 输出提示消息:时间同步完成和当前时间 (Time synchronization completed. Current time:) 46 | echo "时间同步操作完成。当前时间:$current_time (Time synchronization completed. Current time: $current_time)" 47 | } 48 | 49 | # 内核启动bbr设置 50 | enable_bbr() { 51 | echo "启用BBR拥塞控制算法... (Enabling BBR congestion control...)" 52 | kernel_version=$(uname -r | cut -d. -f1) 53 | if [ "$kernel_version" -gt 4 ] || ([ "$kernel_version" -eq 4 ] && [ "$(uname -r | cut -d. -f2)" -ge 9 ]); then 54 | echo "启用 BBR... (Enabling BBR...)" 55 | cat > /etc/sysctl.conf << EOF 56 | net.core.default_qdisc=fq 57 | net.ipv4.tcp_congestion_control=bbr 58 | EOF 59 | sysctl -p 60 | echo "BBR 已启用 (BBR enabled)." 61 | else 62 | echo "内核版本低于 4.9。请使用脚本启用 BBR... (Kernel version is less than 4.9. Enabling BBR using script...)" 63 | echo "wget --no-check-certificate -O /opt/bbr.sh https://testingcf.jsdelivr.net/gh/teddysun/across@master/bbr.sh && chmod 755 /opt/bbr.sh && /opt/bbr.sh" 64 | fi 65 | } 66 | 67 | # 调整时区到上海 68 | adjust_timezone() { 69 | echo "调整时区... (Adjusting timezone...)" 70 | timedatectl set-timezone Asia/Shanghai 71 | echo "时区已设置为Asia/Shanghai (Timezone set to Asia/Shanghai)." 72 | } 73 | 74 | # 调整主机名 75 | adjust_hostname() { 76 | echo "调整主机名... (Adjusting hostname...)" 77 | if command -v lsb_release &>/dev/null; then 78 | os_name=$(lsb_release -si | tr '[:upper:]' '[:lower:]') 79 | elif [ -f /etc/os-release ]; then 80 | os_name=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"' | tr '[:upper:]' '[:lower:]') 81 | else 82 | os_name=$(uname -s | tr '[:upper:]' '[:lower:]') 83 | fi 84 | 85 | case $os_name in 86 | "ubuntu") 87 | new_hostname="ubuntu" 88 | ;; 89 | "debian") 90 | new_hostname="debian" 91 | ;; 92 | "centos") 93 | new_hostname="centos" 94 | ;; 95 | *) 96 | new_hostname="$os_name" 97 | ;; 98 | esac 99 | 100 | hostnamectl set-hostname $new_hostname 101 | echo "主机名已设置为 $new_hostname (Hostname set to $new_hostname)." 102 | } 103 | 104 | 105 | # 安装 Docker 106 | install_docker() { 107 | echo "正在安装 Docker... (Installing Docker...)" 108 | install_docker_with_docker_shell 109 | if ! command -v docker &>/dev/null; then 110 | echo "尝试使用官方脚本安装失败,尝试使用包管理器安装 Docker (Failed to install using official script, trying package manager)..." 111 | 112 | if command -v yum &>/dev/null; then 113 | echo "使用 yum 包管理器安装 Docker (Installing Docker using yum package manager)..." 114 | install_docker_with_yum_package_manager 115 | elif command -v apt-get &>/dev/null; then 116 | echo "使用 apt 包管理器安装 Docker (Installing Docker using apt package manager)..." 117 | install_docker_with_apt_package_manager 118 | else 119 | echo "无法找到适合的包管理器来安装 Docker (Unable to find suitable package manager to install Docker)." 120 | echo "请运行以下命令手动安装 Docker 或者尝试其他安装方式 (Please run the following command to install Docker manually or try another installation method)." 121 | echo "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)" 122 | fi 123 | fi 124 | } 125 | 126 | 127 | # 从docker官方脚本安装 Docker 128 | install_docker_with_docker_shell() { 129 | if command -v docker &>/dev/null; then 130 | echo "Docker已经安装 (Docker is already installed)." 131 | else 132 | docker_sources=( 133 | "https://mirrors.aliyun.com/docker-ce" 134 | "https://mirrors.tencent.com/docker-ce" 135 | "https://mirrors.163.com/docker-ce" 136 | "https://mirrors.cernet.edu.cn/docker-ce" 137 | "https://download.docker.com" 138 | ) 139 | 140 | docker_install_scripts=( 141 | "https://testingcf.jsdelivr.net/gh/docker/docker-install@master/install.sh" 142 | "https://cdn.jsdelivr.net/gh/docker/docker-install@master/install.sh" 143 | "https://fastly.jsdelivr.net/gh/docker/docker-install@master/install.sh" 144 | "https://gcore.jsdelivr.net/gh/docker/docker-install@master/install.sh" 145 | "https://raw.githubusercontent.com/docker/docker-install/master/install.sh" 146 | "https://get.docker.com" 147 | ) 148 | 149 | get_average_delay() { 150 | local source=$1 151 | local total_delay=0 152 | local iterations=2 153 | local timeout=2 154 | 155 | for ((i = 0; i < iterations; i++)); do 156 | delay=$(curl -o /dev/null -s -m $timeout -w "%{time_total}\n" "$source") 157 | if [ $? -ne 0 ]; then 158 | delay=$timeout 159 | fi 160 | total_delay=$(awk "BEGIN {print $total_delay + $delay}") 161 | done 162 | 163 | average_delay=$(awk "BEGIN {print $total_delay / $iterations}") 164 | echo "$average_delay" 165 | } 166 | 167 | min_delay=99999999 168 | selected_source="" 169 | 170 | for source in "${docker_sources[@]}"; do 171 | average_delay=$(get_average_delay "$source" &) 172 | 173 | if (( $(awk 'BEGIN { print '"$average_delay"' < '"$min_delay"' }') )); then 174 | min_delay=$average_delay 175 | selected_source=$source 176 | fi 177 | done 178 | wait 179 | 180 | if [ -n "$selected_source" ]; then 181 | echo "选择延迟最低的源 $selected_source,延迟为 $min_delay 秒 (Selecting source with minimum delay of $min_delay seconds)." 182 | export DOWNLOAD_URL="$selected_source" 183 | for script_source in "${docker_install_scripts[@]}"; do 184 | echo "正在尝试从 $script_source 下载安装脚本 (Attempting to download installation script from $script_source)..." 185 | curl -# -fsSL --retry 2 --retry-delay 3 --connect-timeout 5 --max-time 10 "$script_source" -o get-docker.sh 186 | if [ $? -eq 0 ]; then 187 | echo "成功从 $script_source 下载安装脚本 (Successfully downloaded installation script from $script_source)." 188 | sh get-docker.sh 189 | if [ $? -eq 0 ]; then 190 | echo "Docker安装成功 (Docker installed successfully)." 191 | return 192 | else 193 | echo "Docker安装失败 (Failed to install Docker)." 194 | echo "请运行以下命令手动安装 Docker 或者尝试其他安装方式 (Please run the following command to install Docker manually or try another installation method)." 195 | echo "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)" 196 | fi 197 | else 198 | echo "从 $script_source 下载安装脚本失败,尝试下一个链接 (Failed to download installation script from $script_source, trying next link)." 199 | fi 200 | done 201 | else 202 | echo "无法选择源进行安装 (Unable to select a source for installation)." 203 | fi 204 | fi 205 | } 206 | 207 | 208 | # 从包管理器安装 Docker(针对 yum) 209 | install_docker_with_yum_package_manager() { 210 | # 定义候选源列表 211 | docker_sources=( 212 | "https://mirrors.aliyun.com/docker-ce" 213 | "https://mirrors.tencent.com/docker-ce" 214 | "https://mirrors.163.com/docker-ce" 215 | "https://mirrors.cernet.edu.cn/docker-ce" 216 | "https://download.docker.com" 217 | ) 218 | 219 | # 定义待安装组件列表 220 | docker_components=( 221 | "docker-ce" 222 | "docker-ce-cli" 223 | "containerd.io" 224 | "docker-compose-plugin" 225 | "docker-ce-rootless-extras" 226 | "docker-buildx-plugin" 227 | ) 228 | 229 | # 定义函数:获取平均延迟 230 | get_average_delay() { 231 | local source=$1 232 | local total_delay=0 233 | local iterations=2 234 | local timeout=2 235 | 236 | for ((i = 0; i < iterations; i++)); do 237 | delay=$(curl -o /dev/null -s -m $timeout -w "%{time_total}\n" "$source") 238 | if [ $? -ne 0 ]; then 239 | delay=$timeout 240 | fi 241 | total_delay=$(awk "BEGIN {print $total_delay + $delay}") 242 | done 243 | 244 | average_delay=$(awk "BEGIN {print $total_delay / $iterations}") 245 | echo "$average_delay" 246 | } 247 | 248 | # 初始化最小延迟为一个大数 249 | min_delay=99999999 250 | selected_source="" 251 | 252 | # 并行测试所有源的延迟 253 | for source in "${docker_sources[@]}"; do 254 | average_delay=$(get_average_delay "$source" &) 255 | 256 | if (( $(awk 'BEGIN { print '"$average_delay"' < '"$min_delay"' }') )); then 257 | min_delay=$average_delay 258 | selected_source=$source 259 | fi 260 | done 261 | wait 262 | 263 | # 如果成功选择了源 264 | if [ -n "$selected_source" ]; then 265 | echo "选择延迟最低的源 $selected_source,延迟为 $min_delay 秒 (Selecting source with minimum delay of $min_delay seconds)." 266 | 267 | # 添加源并安装Docker组件 268 | yum-config-manager --add-repo "$selected_source/linux/centos/docker-ce.repo" 269 | if [ $? -eq 0 ]; then 270 | echo "Docker源添加成功 (Docker repository added successfully)." 271 | yum install -y "${docker_components[@]}" 272 | if [ $? -eq 0 ]; then 273 | echo "Docker安装成功 (Docker installed successfully)." 274 | 275 | # 启动 Docker 服务 276 | systemctl start docker 277 | 278 | if [ $? -eq 0 ]; then 279 | echo "Docker服务已启动 (Docker service started)." 280 | 281 | # 设置 Docker 服务在系统启动时自动启动 282 | systemctl enable docker 283 | 284 | if [ $? -eq 0 ]; then 285 | echo "Docker已设置为开机自启 (Docker set to start on boot)." 286 | else 287 | echo "无法设置Docker为开机自启 (Failed to set Docker to start on boot)." 288 | fi 289 | else 290 | echo "无法启动Docker服务 (Failed to start Docker service)." 291 | fi 292 | else 293 | echo "Docker安装失败 (Failed to install Docker)." 294 | echo "请运行以下命令手动安装 Docker 或者尝试其他安装方式 (Please run the following command to install Docker manually or try another installation method)." 295 | echo "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)" 296 | fi 297 | else 298 | echo "无法添加Docker源 (Failed to add Docker repository)." 299 | fi 300 | else 301 | echo "无法选择源进行安装 (Unable to select a source for installation)." 302 | fi 303 | } 304 | 305 | 306 | # 从包管理器安装 Docker(针对 apt) 307 | install_docker_with_apt_package_manager() { 308 | # 更新apt源 309 | apt-get update 310 | 311 | # 安装必要的组件 312 | apt-get install -y \ 313 | apt-transport-https \ 314 | ca-certificates \ 315 | curl \ 316 | gnupg \ 317 | lsb-release 318 | 319 | if [ $? -eq 0 ]; then 320 | echo "必需组件安装成功 (Necessary components installed successfully)." 321 | 322 | # 定义候选源列表 323 | docker_sources=( 324 | "https://mirrors.aliyun.com/docker-ce" 325 | "https://mirrors.tencent.com/docker-ce" 326 | "https://mirrors.163.com/docker-ce" 327 | "https://mirrors.cernet.edu.cn/docker-ce" 328 | "https://download.docker.com" 329 | ) 330 | 331 | # 定义函数:获取平均延迟 332 | get_average_delay() { 333 | local source=$1 334 | local total_delay=0 335 | local iterations=2 336 | local timeout=2 337 | 338 | for ((i = 0; i < iterations; i++)); do 339 | delay=$(curl -o /dev/null -s -m $timeout -w "%{time_total}\n" "$source") 340 | if [ $? -ne 0 ]; then 341 | delay=$timeout 342 | fi 343 | total_delay=$(awk "BEGIN {print $total_delay + $delay}") 344 | done 345 | 346 | average_delay=$(awk "BEGIN {print $total_delay / $iterations}") 347 | echo "$average_delay" 348 | } 349 | 350 | # 初始化最小延迟为一个大数 351 | min_delay=99999999 352 | selected_source="" 353 | 354 | # 并行测试所有源的延迟 355 | for source in "${docker_sources[@]}"; do 356 | average_delay=$(get_average_delay "$source" &) 357 | 358 | if (( $(awk 'BEGIN { print '"$average_delay"' < '"$min_delay"' }') )); then 359 | min_delay=$average_delay 360 | selected_source=$source 361 | fi 362 | done 363 | wait 364 | 365 | # 如果成功选择了源 366 | if [ -n "$selected_source" ]; then 367 | echo "选择延迟最低的源 $selected_source,延迟为 $min_delay 秒 (Selecting source with minimum delay of $min_delay seconds)." 368 | 369 | # 添加源并安装Docker组件 370 | curl -fsSL https://$selected_source/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 371 | 372 | echo \ 373 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://$selected_source/linux/ubuntu \ 374 | $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null 375 | 376 | apt-get update 377 | apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-ce-rootless-extras docker-buildx-plugin 378 | 379 | if [ $? -eq 0 ]; then 380 | echo "Docker安装成功 (Docker installed successfully)." 381 | 382 | systemctl start docker 383 | 384 | if [ $? -eq 0 ]; then 385 | echo "Docker服务已启动 (Docker service started)." 386 | 387 | systemctl enable docker 388 | 389 | if [ $? -eq 0 ]; then 390 | echo "Docker已设置为开机自启 (Docker set to start on boot)." 391 | else 392 | echo "无法设置Docker为开机自启 (Failed to set Docker to start on boot)." 393 | fi 394 | else 395 | echo "无法启动Docker服务 (Failed to start Docker service)." 396 | fi 397 | else 398 | echo "Docker安装失败 (Failed to install Docker)." 399 | echo "请运行以下命令手动安装 Docker 或者尝试其他安装方式 (Please run the following command to install Docker manually or try another installation method)." 400 | echo "bash <(curl -sSL https://linuxmirrors.cn/docker.sh)" 401 | fi 402 | else 403 | echo "无法选择源进行安装 (Unable to select a source for installation)." 404 | fi 405 | else 406 | echo "必需组件安装失败 (Failed to install necessary components)." 407 | fi 408 | } 409 | 410 | 411 | # 安装docker-compose 412 | install_docker_compose() { 413 | echo "正在安装 Docker Compose... (Installing Docker Compose...)" 414 | if command -v docker-compose &>/dev/null; then 415 | echo "Docker-compose已经安装 (Docker-compose already installed)." 416 | else 417 | latest_compose=$(curl -sL "https://api.github.com/repos/docker/compose/releases/latest" | grep -oP '"tag_name": "\K(.*)(?=")') 418 | download_url="https://github.com/docker/compose/releases/download/$latest_compose/docker-compose-$(uname -s)-$(uname -m)" 419 | proxy_sources=( 420 | "https://mirror.ghproxy.com" 421 | "https://ghproxy.net" 422 | "https://ghproxy.cc" 423 | ) 424 | 425 | # 使用timeout设置下载超时时间为30秒 426 | if timeout 30 bash -c "curl -fsSL $download_url > /usr/local/bin/docker-compose"; then 427 | chmod +x /usr/local/bin/docker-compose 428 | 429 | # 检查docker-compose是否可执行 430 | if docker-compose --version &>/dev/null; then 431 | # 检查软链接是否存在 432 | if [ ! -e /usr/bin/docker-compose ]; then 433 | # 创建软链接 434 | ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose 435 | echo "Docker-compose已安装 (Docker-compose installed)." 436 | else 437 | echo "软链接已存在。跳过创建 (Soft link already exists. Skipping creation)." 438 | fi 439 | else 440 | echo "安装的docker-compose不可执行,请检查下载源。(Installed docker-compose is not executable, please check the download source.)" 441 | fi 442 | else 443 | echo "下载docker-compose超时,使用代理重新下载... (Download timed out, retrying with proxy...)" 444 | 445 | # 尝试从多个代理源下载 446 | for proxy in "${proxy_sources[@]}"; do 447 | proxy_download_url="${proxy}/${download_url#https://}" 448 | if timeout 30 bash -c "wget --quiet --no-check-certificate -O /usr/local/bin/docker-compose $proxy_download_url"; then 449 | chmod +x /usr/local/bin/docker-compose 450 | 451 | # 检查docker-compose是否可执行 452 | if docker-compose --version &>/dev/null; then 453 | # 检查软链接是否存在 454 | if [ ! -e /usr/bin/docker-compose ]; then 455 | # 创建软链接 456 | ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose 457 | echo "Docker-compose已通过代理安装 (Docker-compose installed via proxy)." 458 | else 459 | echo "软链接已存在。跳过创建 (Soft link already exists. Skipping creation)." 460 | fi 461 | return 462 | else 463 | echo "安装的docker-compose不可执行,请检查下载源。(Installed docker-compose is not executable, please check the download source.)" 464 | fi 465 | else 466 | echo "从 $proxy 下载docker-compose失败,尝试下一个源 (Failed to download docker-compose from $proxy, trying next source)." 467 | fi 468 | done 469 | 470 | # 删除下载失败的文件和软链接 471 | \rm -rf -f /usr/local/bin/docker-compose /usr/bin/docker-compose 472 | echo "所有代理源均无法下载docker-compose (Failed to download docker-compose from all proxy sources)." 473 | echo "可以尝试执行以下命令手动下载并安装 docker-compose (Try running this command to install docker-compose manually):" 474 | 475 | arch=$(uname -m) 476 | if [ "$arch" == 'armv7l' ]; then 477 | arch='armv7' 478 | fi 479 | echo "curl -L https://resource.fit2cloud.com/docker/compose/releases/download/v2.26.1/docker-compose-$(uname -s | tr A-Z a-z)-$arch -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose && ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose" 480 | fi 481 | fi 482 | } 483 | 484 | 485 | # 安装常用软件 486 | install_utilities() { 487 | echo "安装常用工具... (Installing common utilities...)" 488 | if command -v yum &>/dev/null; then 489 | yum install -y curl wget mtr screen net-tools zip unzip tar lsof 490 | elif command -v apt-get &>/dev/null; then 491 | apt-get update 492 | apt-get install -y curl wget mtr screen net-tools zip unzip tar lsof 493 | else 494 | echo "不支持的包管理器。跳过工具安装 (Unsupported package manager. Utilities installation skipped)." 495 | fi 496 | 497 | echo "常用工具已安装 (Utilities installed)." 498 | } 499 | 500 | 501 | # 检测组件和设置是否正确 502 | check_components() { 503 | echo "检查系统组件... (Checking system components...)" 504 | # 定义要检查的软件列表 505 | components=("docker" "docker-compose" "curl" "wget" "mtr" "screen" "zip" "unzip" "tar" "lsof") 506 | 507 | # 遍历检查软件是否安装 508 | for component in "${components[@]}"; do 509 | if command -v "$component" &>/dev/null; then 510 | echo "$component 已正确安装 ($component is correctly installed)." 511 | else 512 | echo "警告:$component 未正确安装 (Warning: $component is not correctly installed)." 513 | fi 514 | done 515 | 516 | # 检测当前时间是否正确 517 | current_time=$(date) 518 | echo "当前时间为:$current_time (Current time is: $current_time)" 519 | 520 | # 检测BBR是否正确启用 521 | bbr_status=$(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') 522 | if [ "$bbr_status" = "bbr" ]; then 523 | echo "BBR 已正确启用 (BBR is correctly enabled)." 524 | else 525 | echo "警告:BBR 未正确启用 (Warning: BBR is not correctly enabled)." 526 | fi 527 | } 528 | 529 | # 设置swap 530 | setup_swap() { 531 | echo "设置交换空间... (Setting up swap space...)" 532 | # 检查是否已经存在swap 533 | if swapon --show | grep -q '^/'; then 534 | echo "Swap已存在。当前的swap大小为: (Swap already exists. Current swap size is:)" 535 | swapon --show 536 | else 537 | # 获取系统内存大小 (单位为KB) 538 | mem_total_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') 539 | mem_total_mb=$((mem_total_kb / 1024)) 540 | mem_total_gb=$((mem_total_mb / 1024)) 541 | 542 | # 获取根目录可用磁盘空间 (单位为KB) 543 | available_disk_space_kb=$(df / | tail -1 | awk '{print $4}') 544 | available_disk_space_mb=$((available_disk_space_kb / 1024)) 545 | available_disk_space_gb=$((available_disk_space_mb / 1024)) 546 | 547 | # 如果可用磁盘空间小于5GB,则设置swap为512MB 548 | if [ $available_disk_space_gb -lt 5 ]; then 549 | swap_size_mb=512 550 | else 551 | # 根据系统内存大小推荐swap大小 552 | if [ $mem_total_gb -le 1 ]; then 553 | recommended_swap_size_mb=$((1024)) 554 | elif [ $mem_total_gb -le 2 ]; then 555 | recommended_swap_size_mb=$((mem_total_mb * 2)) 556 | elif [ $mem_total_gb -le 8 ]; then 557 | recommended_swap_size_mb=$((mem_total_mb)) 558 | elif [ $mem_total_gb -le 64 ]; then 559 | recommended_swap_size_mb=4096 560 | else 561 | recommended_swap_size_mb=4096 562 | fi 563 | 564 | # 确保swap大小不超过可用磁盘空间的一半和8GB 565 | max_swap_size_mb=$((available_disk_space_mb / 2)) 566 | if [ $max_swap_size_mb -gt $((8 * 1024)) ]; then 567 | max_swap_size_mb=$((8 * 1024)) 568 | fi 569 | 570 | if [ $recommended_swap_size_mb -gt $max_swap_size_mb ]; then 571 | swap_size_mb=$max_swap_size_mb 572 | else 573 | swap_size_mb=$recommended_swap_size_mb 574 | fi 575 | fi 576 | 577 | # 创建swap文件 578 | swap_file="/swapfile" 579 | echo "未检测到swap。创建一个swap文件,大小为 $(($swap_size_mb / 1024))GB... (No swap detected. Creating a swap file with size $(($swap_size_mb / 1024))GB...)" 580 | dd if=/dev/zero of=$swap_file bs=1M count=$swap_size_mb 581 | 582 | # 设置swap文件 583 | chmod 600 $swap_file 584 | mkswap $swap_file 585 | swapon $swap_file 586 | 587 | # 添加到fstab以便重启后自动启用swap 588 | echo "$swap_file none swap sw 0 0" >> /etc/fstab 589 | 590 | echo "Swap创建成功。当前的swap大小为: (Swap created successfully. Current swap size is:)" 591 | swapon --show 592 | fi 593 | } 594 | 595 | 596 | # 提示用户批量安装所需组件 597 | prompt_install_components() { 598 | echo "即将安装组件以增强系统功能:(The following components will be installed to enhance system functionality:)" 599 | echo " - cloud-init: 初始化云实例。(Cloud instance initialization.)" 600 | echo " - qemu-guest-agent: 宿主机通信。(Host communication.)" 601 | echo " - cloud-utils: 磁盘管理工具。(Disk management tools.)" 602 | 603 | # 检查组件是否已安装 604 | if dpkg -l cloud-init qemu-guest-agent cloud-utils cloud-initramfs-growroot &>/dev/null || rpm -q cloud-init qemu-guest-agent cloud-utils cloud-utils-growpart &>/dev/null; then 605 | echo "组件已安装,跳过安装过程。(Components are already installed, skipping installation process.)" 606 | return 607 | fi 608 | 609 | if grep -q -i "ubuntu\|debian" /etc/*release; then 610 | echo " - cloud-initramfs-growroot: 调整文件系统大小。(Filesystem resizing.)" 611 | echo "" 612 | read -p "确认安装?(Confirm installation?) (y/n): " confirm 613 | if [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]]; then 614 | apt-get update 615 | apt-get install -y cloud-init qemu-guest-agent cloud-initramfs-growroot cloud-utils 616 | else 617 | echo "组件安装已取消。(Component installation canceled.)" 618 | fi 619 | elif grep -q -i "centos\|rhel" /etc/*release; then 620 | echo " - cloud-utils-growpart: 调整分区大小。(Partition resizing.)" 621 | echo "" 622 | read -p "确认安装?(Confirm installation?) (y/n): " confirm 623 | if [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]]; then 624 | yum install -y cloud-init qemu-guest-agent cloud-utils-growpart cloud-utils 625 | else 626 | echo "组件安装已取消。(Component installation canceled.)" 627 | fi 628 | else 629 | echo "操作系统不支持,需手动安装。(Unsupported OS, manual installation required.)" 630 | fi 631 | } 632 | 633 | 634 | # 主脚本逻辑 635 | main() { 636 | check_root 637 | 638 | while true; do 639 | echo "请选择要执行的操作: (Please choose the operation to execute:)" 640 | echo "1) 执行完整功能 (Execute full setup)" 641 | echo "2) 只安装 Docker 和 Docker Compose (Install only Docker and Docker Compose)" 642 | echo "3) 退出 (Exit)" 643 | read -p "请输入选项 (1, 2 or 3): " choice 644 | 645 | case $choice in 646 | 1) 647 | # 执行完整功能 648 | install_utilities 649 | adjust_timezone 650 | sync_system_time 651 | enable_bbr 652 | adjust_hostname 653 | install_docker 654 | install_docker_compose 655 | setup_swap 656 | check_components 657 | echo "设置完成 (Setup complete)." 658 | bash -l 659 | break 660 | ;; 661 | 2) 662 | # 只安装 Docker 和 Docker Compose 663 | install_docker 664 | install_docker_compose 665 | echo "设置完成 (Setup complete)." 666 | break 667 | ;; 668 | 3) 669 | # 退出 670 | echo "退出 (Exit)." 671 | exit 0 672 | ;; 673 | *) 674 | echo "无效的选项,请输入 1, 2 或 3 (Invalid option, please enter 1, 2 or 3)" 675 | ;; 676 | esac 677 | done 678 | } 679 | 680 | # 执行主脚本逻辑 681 | main --------------------------------------------------------------------------------