Files
django.remote/nginx_tab.py

1125 lines
51 KiB
Python
Raw Normal View History

2025-08-31 20:35:59 +08:00
import os
import sys
import json
2025-08-31 20:35:59 +08:00
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
QLabel, QTextEdit, QFileDialog, QMessageBox,
QLineEdit, QDialog, QDialogButtonBox)
from PySide6.QtCore import QThread, Signal
from loguru import logger
class PasswordDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("输入密码")
self.setMinimumWidth(300)
layout = QVBoxLayout()
# 提示标签
label = QLabel("请输入sudo密码:")
layout.addWidget(label)
# 密码输入框
self.password_input = QLineEdit()
self.password_input.setEchoMode(QLineEdit.Password)
layout.addWidget(self.password_input)
# 按钮
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.setLayout(layout)
def get_password(self):
return self.password_input.text()
class NginxInstallThread(QThread):
"""安装Nginx的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, password):
super().__init__()
self.ssh_client = ssh_client
self.password = password
def run(self):
try:
logger.info("开始安装Nginx")
# 使用bash -c将命令组合在一起确保密码对所有sudo命令有效
stdin, stdout, stderr = self.ssh_client.exec_command(f"bash -c 'echo \"{self.password}\" | sudo -S apt update && echo \"{self.password}\" | sudo -S apt install -y nginx'")
exit_status = stdout.channel.recv_exit_status()
output = stdout.read().decode()
error = stderr.read().decode()
if exit_status == 0:
# 验证安装
logger.info("验证Nginx安装")
stdin, stdout, stderr = self.ssh_client.exec_command("nginx -v")
version_exit_status = stdout.channel.recv_exit_status()
nginx_version = stdout.read().decode().strip()
version_error = stderr.read().decode()
logger.info(f"Nginx版本检查状态: {version_exit_status}")
logger.info(f"Nginx版本信息: {nginx_version}")
if version_error:
logger.error(f"Nginx版本检查错误: {version_error}")
if nginx_version:
self.result_ready.emit(True, f"Nginx安装成功: {nginx_version}")
logger.info(f"Nginx安装成功: {nginx_version}")
else:
self.result_ready.emit(False, "Nginx安装后无法获取版本信息")
logger.error("Nginx安装后无法获取版本信息")
else:
self.result_ready.emit(False, f"Nginx安装失败: {error}")
logger.error(f"Nginx安装失败: {error}")
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"Nginx安装异常: {error_msg}")
class NginxConfigThread(QThread):
"""处理Nginx配置文件的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, config_content, config_path, password, operation="upload"):
super().__init__()
self.ssh_client = ssh_client
self.config_content = config_content
self.config_path = config_path
self.password = password
self.operation = operation # "upload" 或 "download"
def run(self):
try:
logger.info(f"开始处理Nginx配置文件: {self.config_path}, 操作: {self.operation}")
if self.operation == "upload":
# 上传配置文件
# 创建临时文件
temp_file = "/tmp/nginx_config.conf"
# 将配置内容写入临时文件
stdin, stdout, stderr = self.ssh_client.exec_command(f"cat > {temp_file} << 'EOF'\n{self.config_content}\nEOF")
exit_status = stdout.channel.recv_exit_status()
if exit_status != 0:
error = stderr.read().decode()
self.result_ready.emit(False, f"创建临时文件失败: {error}")
logger.error(f"创建临时文件失败: {error}")
return
# 备份原配置文件
backup_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S cp {self.config_path} {self.config_path}.bak'"
stdin, stdout, stderr = self.ssh_client.exec_command(backup_cmd)
backup_status = stdout.channel.recv_exit_status()
if backup_status != 0:
error = stderr.read().decode()
logger.warning(f"备份原配置文件失败: {error}")
# 移动临时文件到目标位置
move_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S mv {temp_file} {self.config_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(move_cmd)
move_status = stdout.channel.recv_exit_status()
if move_status == 0:
self.result_ready.emit(True, f"配置文件上传成功: {self.config_path}")
logger.info(f"配置文件上传成功: {self.config_path}")
else:
error = stderr.read().decode()
self.result_ready.emit(False, f"配置文件上传失败: {error}")
logger.error(f"配置文件上传失败: {error}")
elif self.operation == "download":
# 下载配置文件
download_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S cat {self.config_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(download_cmd)
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
config_content = stdout.read().decode()
self.result_ready.emit(True, config_content)
logger.info(f"配置文件下载成功: {self.config_path}")
else:
error = stderr.read().decode()
self.result_ready.emit(False, f"配置文件下载失败: {error}")
logger.error(f"配置文件下载失败: {error}")
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"Nginx配置文件处理异常: {error_msg}")
class NginxControlThread(QThread):
"""控制Nginx服务的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, password, action):
super().__init__()
self.ssh_client = ssh_client
self.password = password
self.action = action # "restart", "enable", "disable", "status"
def run(self):
try:
logger.info(f"开始执行Nginx服务操作: {self.action}")
if self.action == "restart":
command = f"bash -c 'echo \"{self.password}\" | sudo -S systemctl restart nginx'"
elif self.action == "enable":
command = f"bash -c 'echo \"{self.password}\" | sudo -S systemctl enable nginx'"
elif self.action == "disable":
command = f"bash -c 'echo \"{self.password}\" | sudo -S systemctl disable nginx'"
elif self.action == "status":
command = f"bash -c 'echo \"{self.password}\" | sudo -S systemctl status nginx'"
elif self.action == "configtest":
command = f"bash -c 'echo \"{self.password}\" | sudo -S nginx -t'"
else:
self.result_ready.emit(False, f"不支持的操作: {self.action}")
logger.error(f"不支持的操作: {self.action}")
return
stdin, stdout, stderr = self.ssh_client.exec_command(command)
exit_status = stdout.channel.recv_exit_status()
output = stdout.read().decode()
error = stderr.read().decode()
if exit_status == 0:
self.result_ready.emit(True, f"Nginx {self.action} 操作成功\n{output}")
logger.info(f"Nginx {self.action} 操作成功")
else:
self.result_ready.emit(False, f"Nginx {self.action} 操作失败: {error}")
logger.error(f"Nginx {self.action} 操作失败: {error}")
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"Nginx服务控制异常: {error_msg}")
class NginxPermissionsThread(QThread):
"""设置Nginx权限的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, commands, password):
super().__init__()
self.ssh_client = ssh_client
self.commands = commands # 命令列表
self.password = password
def run(self):
try:
logger.info("开始执行Nginx权限设置操作")
# 执行所有命令
for i, command in enumerate(self.commands):
logger.info(f"执行命令 {i+1}/{len(self.commands)}: {command}")
# 使用bash -c将命令组合在一起确保密码对所有sudo命令有效
# 使用-S选项让sudo从标准输入读取密码
full_command = f"bash -c 'echo \"{self.password}\" | sudo -S {command}'"
stdin, stdout, stderr = self.ssh_client.exec_command(full_command)
exit_status = stdout.channel.recv_exit_status()
output = stdout.read().decode()
error = stderr.read().decode()
if exit_status != 0:
error_msg = f"命令执行失败: {command}\n错误信息: {error}"
self.result_ready.emit(False, error_msg)
logger.error(error_msg)
return
logger.info(f"命令执行成功: {command}")
# 所有命令执行成功
success_msg = "所有权限设置命令执行成功"
self.result_ready.emit(True, success_msg)
logger.info(success_msg)
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"Nginx权限设置异常: {error_msg}")
2025-08-31 20:35:59 +08:00
class NginxSiteThread(QThread):
"""处理Nginx站点配置的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, site_config, site_name, password, operation="create"):
super().__init__()
self.ssh_client = ssh_client
self.site_config = site_config
self.site_name = site_name
self.password = password
self.operation = operation # "create" 或 "enable" 或 "download"
2025-08-31 20:35:59 +08:00
def run(self):
try:
logger.info(f"开始处理Nginx站点配置: {self.site_name}, 操作: {self.operation}")
if self.operation == "create":
# 创建站点配置文件
site_path = f"/etc/nginx/sites-available/{self.site_name}"
# 创建临时文件
temp_file = f"/tmp/{self.site_name}.conf"
# 将配置内容写入临时文件
stdin, stdout, stderr = self.ssh_client.exec_command(f"cat > {temp_file} << 'EOF'\n{self.site_config}\nEOF")
exit_status = stdout.channel.recv_exit_status()
if exit_status != 0:
error = stderr.read().decode()
self.result_ready.emit(False, f"创建临时文件失败: {error}")
logger.error(f"创建临时文件失败: {error}")
return
# 移动临时文件到目标位置
move_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S mv {temp_file} {site_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(move_cmd)
move_status = stdout.channel.recv_exit_status()
if move_status == 0:
self.result_ready.emit(True, f"站点配置文件创建成功: {site_path}")
logger.info(f"站点配置文件创建成功: {site_path}")
else:
error = stderr.read().decode()
self.result_ready.emit(False, f"站点配置文件创建失败: {error}")
logger.error(f"站点配置文件创建失败: {error}")
elif self.operation == "enable":
# 启用站点配置
# 首先检查目标链接是否存在,如果存在则先删除
check_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S test -f /etc/nginx/sites-enabled/{self.site_name}'"
stdin, stdout, stderr = self.ssh_client.exec_command(check_cmd)
check_status = stdout.channel.recv_exit_status()
if check_status == 0:
# 目标链接存在,先删除
remove_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S rm -f /etc/nginx/sites-enabled/{self.site_name}'"
stdin, stdout, stderr = self.ssh_client.exec_command(remove_cmd)
remove_status = stdout.channel.recv_exit_status()
if remove_status != 0:
error = stderr.read().decode()
self.result_ready.emit(False, f"删除现有符号链接失败: {error}")
logger.error(f"删除现有符号链接失败: {error}")
return
# 创建符号链接
2025-08-31 20:35:59 +08:00
enable_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S ln -s /etc/nginx/sites-available/{self.site_name} /etc/nginx/sites-enabled/'"
stdin, stdout, stderr = self.ssh_client.exec_command(enable_cmd)
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
self.result_ready.emit(True, f"站点配置启用成功: {self.site_name}")
logger.info(f"站点配置启用成功: {self.site_name}")
else:
error = stderr.read().decode()
self.result_ready.emit(False, f"站点配置启用失败: {error}")
logger.error(f"站点配置启用失败: {error}")
elif self.operation == "download":
# 下载站点配置文件
site_path = f"/etc/nginx/sites-enabled/{self.site_name}"
download_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S cat {site_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(download_cmd)
exit_status = stdout.channel.recv_exit_status()
if exit_status == 0:
config_content = stdout.read().decode()
self.result_ready.emit(True, config_content)
logger.info(f"站点配置文件下载成功: {site_path}")
else:
error = stderr.read().decode()
self.result_ready.emit(False, f"站点配置文件下载失败: {error}")
logger.error(f"站点配置文件下载失败: {error}")
2025-08-31 20:35:59 +08:00
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"Nginx站点配置处理异常: {error_msg}")
class NginxTab(QWidget):
def __init__(self):
super().__init__()
self.ssh_client = None
self.username = ""
self.project_name = ""
self.server_ip = ""
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
# Nginx安装区域
install_layout = QHBoxLayout()
# 安装Nginx按钮
self.install_nginx_btn = QPushButton("安装Nginx")
self.install_nginx_btn.clicked.connect(self.install_nginx)
install_layout.addWidget(self.install_nginx_btn)
layout.addLayout(install_layout)
# Nginx主配置文件区域
main_config_layout = QVBoxLayout()
main_config_layout.addWidget(QLabel("Nginx主配置文件编辑器:"))
# 主配置文件编辑文本框
self.main_config_editor = QTextEdit()
self.main_config_editor.setReadOnly(False)
main_config_layout.addWidget(self.main_config_editor)
# 主配置文件操作按钮
main_config_btn_layout = QHBoxLayout()
# 下载主配置文件按钮
self.download_main_config_btn = QPushButton("下载主配置文件")
self.download_main_config_btn.clicked.connect(self.download_main_config)
main_config_btn_layout.addWidget(self.download_main_config_btn)
# 上传主配置文件按钮
self.upload_main_config_btn = QPushButton("上传主配置文件")
self.upload_main_config_btn.clicked.connect(self.upload_main_config)
main_config_btn_layout.addWidget(self.upload_main_config_btn)
main_config_layout.addLayout(main_config_btn_layout)
layout.addLayout(main_config_layout)
# Nginx站点配置区域
site_config_layout = QVBoxLayout()
site_config_layout.addWidget(QLabel("Nginx站点配置编辑器:"))
# 站点配置编辑文本框
self.site_config_editor = QTextEdit()
self.site_config_editor.setReadOnly(False)
site_config_layout.addWidget(self.site_config_editor)
# 站点配置操作按钮
site_config_btn_layout = QHBoxLayout()
# 创建站点配置按钮
self.create_site_config_btn = QPushButton("创建站点配置")
self.create_site_config_btn.clicked.connect(self.create_site_config)
site_config_btn_layout.addWidget(self.create_site_config_btn)
# 启用站点配置按钮
self.enable_site_config_btn = QPushButton("启用站点配置")
self.enable_site_config_btn.clicked.connect(self.enable_site_config)
site_config_btn_layout.addWidget(self.enable_site_config_btn)
# 下载站点配置按钮
self.download_site_config_btn = QPushButton("下载站点配置")
self.download_site_config_btn.clicked.connect(self.download_site_config)
site_config_btn_layout.addWidget(self.download_site_config_btn)
# 添加静态文件映射按钮
self.add_static_mappings_btn = QPushButton("添加静态文件映射")
self.add_static_mappings_btn.clicked.connect(self.add_static_mappings)
site_config_btn_layout.addWidget(self.add_static_mappings_btn)
# 修改为Unix socket连接按钮
self.modify_to_unix_socket_btn = QPushButton("修改为Unix Socket连接")
self.modify_to_unix_socket_btn.clicked.connect(self.modify_to_unix_socket)
site_config_btn_layout.addWidget(self.modify_to_unix_socket_btn)
2025-08-31 20:35:59 +08:00
site_config_layout.addLayout(site_config_btn_layout)
layout.addLayout(site_config_layout)
# Nginx控制区域
control_layout = QHBoxLayout()
# 重启Nginx按钮
self.restart_nginx_btn = QPushButton("重启Nginx")
self.restart_nginx_btn.clicked.connect(self.restart_nginx)
control_layout.addWidget(self.restart_nginx_btn)
# 查看Nginx状态按钮
self.check_nginx_status_btn = QPushButton("查看Nginx状态")
self.check_nginx_status_btn.clicked.connect(self.check_nginx_status)
control_layout.addWidget(self.check_nginx_status_btn)
# 一键赋予权限按钮
self.set_permissions_btn = QPushButton("一键赋予权限")
self.set_permissions_btn.clicked.connect(self.set_permissions)
control_layout.addWidget(self.set_permissions_btn)
2025-08-31 20:35:59 +08:00
layout.addLayout(control_layout)
# 输出区域
self.output_text = QTextEdit()
self.output_text.setReadOnly(True)
layout.addWidget(self.output_text)
self.setLayout(layout)
# 初始化配置文件内容
self.init_config_content()
def init_config_content(self):
"""初始化配置文件内容"""
# 初始化主配置文件内容
main_config_content = '''# 全局块配置Nginx进程的基本运行参数
user www-data; # Nginx进程运行的用户/组默认是www-data不是你的登录用户xiaji
worker_processes auto; # 工作进程数auto表示自动匹配CPU核心数
pid /run/nginx.pid; # Nginx进程PID文件路径
include /etc/nginx/modules-enabled/*.conf; # 加载启用的模块配置
# events块配置Nginx与客户端的网络连接
events {
worker_connections 768; # 每个工作进程的最大并发连接数
# multi_accept on; # 可选:允许工作进程同时接受多个连接(默认注释)
}
# http块配置HTTP服务的全局参数核心部分
http {
##
# 基础配置
##
sendfile on; # 启用高效文件传输模式
tcp_nopush on; # 配合sendfile使用优化TCP传输
tcp_nodelay on; # 禁用Nagle算法减少小数据包延迟
keepalive_timeout 65; # 长连接超时时间(秒)
types_hash_max_size 2048; # 类型哈希表的最大大小优化MIME类型查找
include /etc/nginx/mime.types; # 加载MIME类型映射文件
default_type application/octet-stream; # 默认MIME类型未匹配时返回二进制流
##
# 日志配置
##
access_log /var/log/nginx/access.log; # 访问日志路径
error_log /var/log/nginx/error.log; # 错误日志路径
##
# SSL/TLS配置全局默认值
##
ssl_protocols TLSv1.2 TLSv1.3; # 支持的SSL/TLS协议禁用不安全的旧协议
ssl_prefer_server_ciphers on; # 优先使用服务器端指定的加密套件
##
# Gzip压缩配置
##
gzip on; # 启用Gzip压缩
# gzip_vary on; # 可选在响应头添加Vary: Accept-Encoding默认注释
# gzip_proxied any; # 可选:对代理请求也启用压缩(默认注释)
# gzip_comp_level 6; # 可选压缩级别1-9默认6默认注释
# gzip_buffers 16 8k; # 可选:压缩缓冲区大小(默认注释)
# gzip_http_version 1.1; # 可选支持的HTTP版本默认注释
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # 可选指定压缩的MIME类型默认注释
##
# 加载站点配置关键默认站点配置不在nginx.conf里而是通过include引入
##
include /etc/nginx/conf.d/*.conf; # 加载conf.d目录下的自定义配置
include /etc/nginx/sites-enabled/*; # 加载启用的站点配置(默认站点在这里)
}
# 可选加载流处理配置如TCP/UDP代理默认注释
# include /etc/nginx/streams-enabled/*.conf;'''
self.main_config_editor.setPlainText(main_config_content)
# 初始化站点配置文件内容
site_config_content = "server {\n"
site_config_content += " listen 80;\n"
site_config_content += " server_name 【IP地址】;\n\n"
site_config_content += " # 转发动态请求到Gunicorn\n"
site_config_content += " location / {\n"
site_config_content += " proxy_pass http://【IP地址】:8000;\n"
site_config_content += " proxy_set_header Host $host;\n"
site_config_content += " proxy_set_header X-Real-IP $remote_addr;\n"
site_config_content += " }\n"
site_config_content += "}\n"
self.site_config_editor.setPlainText(site_config_content)
def update_config_content(self):
"""更新配置文件内容"""
# 更新主配置文件内容
main_config_content = self.main_config_editor.toPlainText()
main_config_content = main_config_content.replace("xiaji", self.username)
self.main_config_editor.setPlainText(main_config_content)
# 更新站点配置文件内容
site_config_content = self.site_config_editor.toPlainText()
site_config_content = site_config_content.replace("【IP地址】", self.server_ip)
self.site_config_editor.setPlainText(site_config_content)
def set_ssh_client(self, ssh_client):
"""设置SSH客户端"""
self.ssh_client = ssh_client
logger.info("Nginx标签页已设置SSH客户端")
def set_username(self, username):
"""设置用户名"""
self.username = username
logger.info(f"Nginx标签页已设置用户名: {username}")
def set_project_info(self, project_name, server_ip):
"""设置项目信息"""
self.project_name = project_name
self.server_ip = server_ip
logger.info(f"Nginx标签页已设置项目信息: {project_name}, {server_ip}")
# 更新配置文件内容
self.update_config_content()
def append_output(self, text):
"""添加输出到文本框"""
self.output_text.append(text)
def install_nginx(self):
"""安装Nginx"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在安装Nginx...")
# 创建并启动Nginx安装线程
self.install_thread = NginxInstallThread(self.ssh_client, password)
self.install_thread.result_ready.connect(self.on_install_result)
self.install_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_install_result(self, success, message):
"""处理安装结果"""
if success:
self.append_output(f"安装成功: {message}")
logger.info(f"Nginx安装成功: {message}")
QMessageBox.information(self, "成功", message)
else:
self.append_output(f"安装失败: {message}")
logger.error(f"Nginx安装失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx安装失败: {message}")
def download_main_config(self):
"""下载主配置文件"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在下载Nginx主配置文件...")
# 创建并启动配置文件下载线程
self.config_thread = NginxConfigThread(self.ssh_client, "", "/etc/nginx/nginx.conf", password, "download")
self.config_thread.result_ready.connect(self.on_download_main_config_result)
self.config_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_download_main_config_result(self, success, message):
"""处理下载主配置文件结果"""
if success:
self.main_config_editor.setPlainText(message)
self.append_output("主配置文件下载成功")
logger.info("Nginx主配置文件下载成功")
else:
self.append_output(f"主配置文件下载失败: {message}")
logger.error(f"Nginx主配置文件下载失败: {message}")
def upload_main_config(self):
"""上传主配置文件"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
config_content = self.main_config_editor.toPlainText()
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在上传Nginx主配置文件...")
# 创建并启动配置文件上传线程
self.config_thread = NginxConfigThread(self.ssh_client, config_content, "/etc/nginx/nginx.conf", password, "upload")
self.config_thread.result_ready.connect(self.on_upload_main_config_result)
self.config_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_upload_main_config_result(self, success, message):
"""处理上传主配置文件结果"""
if success:
self.append_output(f"上传成功: {message}")
logger.info(f"Nginx主配置文件上传成功: {message}")
QMessageBox.information(self, "成功", message)
# 上传主配置文件后检查配置文件语法
self.append_output("正在检查Nginx配置文件语法...")
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
# 创建并启动Nginx配置检查线程
self.check_thread = NginxControlThread(self.ssh_client, password, "configtest")
self.check_thread.result_ready.connect(self.on_upload_configtest_result)
self.check_thread.start()
else:
self.append_output("用户取消了密码输入")
else:
self.append_output(f"上传失败: {message}")
logger.error(f"Nginx主配置文件上传失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx主配置文件上传失败: {message}")
def on_upload_configtest_result(self, success, message):
"""处理上传主配置文件后的配置测试结果"""
if success:
self.append_output("配置文件语法检查通过")
logger.info("Nginx主配置文件上传后语法检查通过")
else:
self.append_output(f"配置文件语法检查失败: {message}")
logger.error(f"Nginx主配置文件上传后语法检查失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx配置文件语法检查失败: {message}")
def create_site_config(self):
"""创建站点配置"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
if not self.project_name:
self.append_output("错误: 未设置项目名")
return
site_config = self.site_config_editor.toPlainText()
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output(f"正在创建站点配置文件: {self.project_name}...")
# 创建并启动站点配置创建线程
self.site_thread = NginxSiteThread(self.ssh_client, site_config, self.project_name, password, "create")
self.site_thread.result_ready.connect(self.on_create_site_config_result)
self.site_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_create_site_config_result(self, success, message):
"""处理创建站点配置结果"""
if success:
self.append_output(f"创建成功: {message}")
logger.info(f"Nginx站点配置创建成功: {message}")
QMessageBox.information(self, "成功", message)
# 创建站点配置文件后检查配置文件语法
self.append_output("正在检查Nginx配置文件语法...")
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
# 创建并启动Nginx配置检查线程
self.check_thread = NginxControlThread(self.ssh_client, password, "configtest")
self.check_thread.result_ready.connect(self.on_create_configtest_result)
self.check_thread.start()
else:
self.append_output("用户取消了密码输入")
else:
self.append_output(f"创建失败: {message}")
logger.error(f"Nginx站点配置创建失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx站点配置创建失败: {message}")
def on_create_configtest_result(self, success, message):
"""处理创建站点配置文件后的配置测试结果"""
if success:
self.append_output("配置文件语法检查通过")
logger.info("Nginx站点配置文件创建后语法检查通过")
else:
self.append_output(f"配置文件语法检查失败: {message}")
logger.error(f"Nginx站点配置文件创建后语法检查失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx配置文件语法检查失败: {message}")
def enable_site_config(self):
"""启用站点配置"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
if not self.project_name:
self.append_output("错误: 未设置项目名")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output(f"正在启用站点配置: {self.project_name}...")
# 创建并启动站点配置启用线程
self.site_thread = NginxSiteThread(self.ssh_client, "", self.project_name, password, "enable")
self.site_thread.result_ready.connect(self.on_enable_site_config_result)
self.site_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_enable_site_config_result(self, success, message):
"""处理启用站点配置结果"""
if success:
self.append_output(f"启用成功: {message}")
logger.info(f"Nginx站点配置启用成功: {message}")
QMessageBox.information(self, "成功", message)
# 启用站点后检查配置文件语法
self.append_output("正在检查Nginx配置文件语法...")
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
# 创建并启动Nginx配置检查线程
self.check_thread = NginxControlThread(self.ssh_client, password, "configtest")
self.check_thread.result_ready.connect(self.on_enable_configtest_result)
self.check_thread.start()
else:
self.append_output("用户取消了密码输入")
else:
self.append_output(f"启用失败: {message}")
logger.error(f"Nginx站点配置启用失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx站点配置启用失败: {message}")
def on_enable_configtest_result(self, success, message):
"""处理启用站点后的配置测试结果"""
if success:
self.append_output("配置文件语法检查通过")
logger.info("Nginx站点配置启用后语法检查通过")
else:
self.append_output(f"配置文件语法检查失败: {message}")
logger.error(f"Nginx站点配置启用后语法检查失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx配置文件语法检查失败: {message}")
def download_site_config(self):
"""下载站点配置文件"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
if not self.project_name:
self.append_output("错误: 未设置项目名")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output(f"正在下载站点配置文件: {self.project_name}...")
# 创建并启动站点配置下载线程
self.site_thread = NginxSiteThread(self.ssh_client, "", self.project_name, password, "download")
self.site_thread.result_ready.connect(self.on_download_site_config_result)
self.site_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_download_site_config_result(self, success, message):
"""处理下载站点配置结果"""
if success:
# 将下载的配置内容显示在站点配置编辑器中
self.site_config_editor.setPlainText(message)
self.append_output("站点配置文件下载成功")
logger.info("Nginx站点配置文件下载成功")
# 尝试添加静态文件映射配置
self.add_static_mappings()
else:
self.append_output(f"下载失败: {message}")
logger.error(f"Nginx站点配置文件下载失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx站点配置文件下载失败: {message}")
def add_static_mappings(self):
"""添加静态文件映射配置"""
# 读取config.json文件获取项目信息
try:
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# 获取第一个服务器配置(假设只有一个服务器配置)
server_config = next(iter(config.values()))
username = server_config.get('username', '')
project_name = server_config.get('project', '')
git_url = server_config.get('git_url', '')
# 从git_url中提取项目名.git前的值
if git_url:
# 提取git_url中最后一个/和.git之间的部分
if '/' in git_url:
git_project_name = git_url.split('/')[-1]
if git_project_name.endswith('.git'):
git_project_name = git_project_name[:-4] # 移除.git后缀
else:
git_project_name = 'webstatus' # 默认值
else:
git_project_name = 'webstatus' # 默认值
# 根据要求构建静态文件路径
# 静态文件结构为: /home/[username]/[git_url中的项目名]/[project]/static
# 媒体文件结构为: /home/[username]/[git_url中的项目名]/[project]/media
static_path = f"/home/{username}/{git_project_name}/static"
media_path = f"/home/{username}/{git_project_name}/media"
# 获取当前配置内容
current_config = self.site_config_editor.toPlainText()
# 检查是否已经包含静态文件映射
if "location /static/" not in current_config:
# 添加静态文件映射配置
static_mapping = f"\n # 静态文件映射 (路径: /home/{username}/{git_project_name}/{project_name}/static)\n"
static_mapping += f" location /static/ {{\n"
static_mapping += f" alias {static_path}/;\n"
static_mapping += f" expires 30d;\n"
static_mapping += f" }}\n"
# 添加媒体文件映射配置
static_mapping += f"\n # 媒体文件映射 (路径: /home/{username}/{git_project_name}/{project_name}/media)\n"
static_mapping += f" location /media/ {{\n"
static_mapping += f" alias {media_path}/;\n"
static_mapping += f" expires 30d;\n"
static_mapping += f" }}\n"
# 在server块的末尾添加静态文件映射在最后一个}之前)
last_brace_pos = current_config.rfind('}')
if last_brace_pos != -1:
new_config = current_config[:last_brace_pos] + static_mapping + current_config[last_brace_pos:]
self.site_config_editor.setPlainText(new_config)
self.append_output("已添加静态文件映射配置")
logger.info("已添加静态文件映射配置")
else:
self.append_output("无法添加静态文件映射配置未找到server块的结束位置")
logger.warning("无法添加静态文件映射配置未找到server块的结束位置")
else:
self.append_output("配置文件已包含静态文件映射")
logger.info("配置文件已包含静态文件映射")
except Exception as e:
error_msg = str(e)
self.append_output(f"添加静态文件映射失败: {error_msg}")
logger.error(f"添加静态文件映射失败: {error_msg}")
def modify_to_unix_socket(self):
"""修改站点配置文件中的proxy_pass为Unix socket连接"""
# 读取config.json文件获取项目信息
try:
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# 获取第一个服务器配置(假设只有一个服务器配置)
server_config = next(iter(config.values()))
username = server_config.get('username', '')
git_url = server_config.get('git_url', '')
# 从git_url中提取项目名.git前的值
if git_url:
# 提取git_url中最后一个/和.git之间的部分
if '/' in git_url:
project_name = git_url.split('/')[-1]
if project_name.endswith('.git'):
project_name = project_name[:-4] # 移除.git后缀
else:
project_name = 'webstatus' # 默认值
else:
project_name = 'webstatus' # 默认值
# 构建Unix socket路径
unix_socket_path = f"http://unix:/home/{username}/{project_name}/sock/gunicorn.sock"
# 获取当前配置内容
current_config = self.site_config_editor.toPlainText()
# 查找并替换proxy_pass的值
import re
# 使用正则表达式匹配proxy_pass行
pattern = r'(\s+proxy_pass\s+)(http://[^;]+|unix:[^;]+);'
replacement = f'\1{unix_socket_path};'
new_config = re.sub(pattern, replacement, current_config)
if new_config != current_config:
# 更新配置内容
self.site_config_editor.setPlainText(new_config)
self.append_output(f"已将proxy_pass修改为: {unix_socket_path}")
logger.info(f"已将proxy_pass修改为: {unix_socket_path}")
else:
# 检查是否已经是Unix socket连接
if unix_socket_path in current_config:
self.append_output("配置文件已经是Unix socket连接")
logger.info("配置文件已经是Unix socket连接")
else:
self.append_output("未找到可修改的proxy_pass配置")
logger.warning("未找到可修改的proxy_pass配置")
except Exception as e:
error_msg = str(e)
self.append_output(f"修改为Unix socket连接失败: {error_msg}")
logger.error(f"修改为Unix socket连接失败: {error_msg}")
2025-08-31 20:35:59 +08:00
def restart_nginx(self):
"""重启Nginx"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在检查Nginx配置文件语法...")
# 创建并启动Nginx配置检查线程
self.check_thread = NginxControlThread(self.ssh_client, password, "configtest")
self.check_thread.result_ready.connect(self.on_configtest_result)
self.check_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_configtest_result(self, success, message):
"""处理配置测试结果"""
if success:
self.append_output("配置文件语法检查通过正在重启Nginx...")
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
# 创建并启动Nginx控制线程
self.control_thread = NginxControlThread(self.ssh_client, password, "restart")
self.control_thread.result_ready.connect(self.on_control_result)
self.control_thread.start()
else:
self.append_output("用户取消了密码输入")
else:
self.append_output(f"配置文件语法检查失败: {message}")
logger.error(f"Nginx配置文件语法检查失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx配置文件语法检查失败: {message}")
def check_nginx_status(self):
"""查看Nginx状态"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在查看Nginx状态...")
# 创建并启动Nginx控制线程
self.control_thread = NginxControlThread(self.ssh_client, password, "status")
self.control_thread.result_ready.connect(self.on_control_result)
self.control_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_control_result(self, success, message):
"""处理控制结果"""
if success:
self.append_output(f"操作成功: {message}")
logger.info(f"Nginx服务控制成功: {message}")
QMessageBox.information(self, "成功", message)
else:
self.append_output(f"操作失败: {message}")
logger.error(f"Nginx服务控制失败: {message}")
QMessageBox.warning(self, "错误", f"Nginx服务控制失败: {message}")
def set_permissions(self):
"""一键赋予权限"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
# 读取config.json文件获取username和git_url
try:
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# 获取第一个服务器配置(假设只有一个服务器配置)
server_config = next(iter(config.values()))
username = server_config.get('username', '')
git_url = server_config.get('git_url', '')
# 从git_url中提取项目名.git前的值
if git_url:
# 提取git_url中最后一个/和.git之间的部分
if '/' in git_url:
project_name = git_url.split('/')[-1]
if project_name.endswith('.git'):
project_name = project_name[:-4] # 移除.git后缀
else:
project_name = 'webstatus' # 默认值
else:
project_name = 'webstatus' # 默认值
logger.info(f"从配置文件获取用户名: {username}, 项目名: {project_name}")
except Exception as e:
error_msg = str(e)
self.append_output(f"读取配置文件失败: {error_msg}")
logger.error(f"读取配置文件失败: {error_msg}")
QMessageBox.warning(self, "错误", f"读取配置文件失败: {error_msg}")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在设置权限...")
# 构建命令序列
commands = [
f"sudo chmod g+x /home/{username}/",
f"sudo chown -R {username}:www-data /home/{username}/{project_name}",
f"sudo chmod g+x /home/{username}/{project_name}",
f"sudo chown -R {username}:www-data /home/{username}/{project_name}/sock",
f"sudo chmod -R 770 /home/{username}/{project_name}/sock",
# 修复/home/xiaji目录权限
f"sudo chmod o+r /home/{username}",
# 修复后验证权限应显示drwxr-xr-x
f"ls -ld /home/{username}",
# 验证www-data能否正常访问目录
f"sudo -u www-data ls /home/{username}/{project_name}/static/admin/css/"
]
# 创建并启动权限设置线程
self.permissions_thread = NginxPermissionsThread(self.ssh_client, commands, password)
self.permissions_thread.result_ready.connect(self.on_permissions_result)
self.permissions_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_permissions_result(self, success, message):
"""处理权限设置结果"""
if success:
self.append_output(f"权限设置成功: {message}")
logger.info(f"权限设置成功: {message}")
QMessageBox.information(self, "成功", message)
else:
self.append_output(f"权限设置失败: {message}")
logger.error(f"权限设置失败: {message}")
QMessageBox.warning(self, "错误", f"权限设置失败: {message}")