完善nginx标签。使用unix套接字。增加远程命令的运行和显示。

This commit is contained in:
2025-08-31 22:16:45 +08:00
parent 47f3669dc4
commit 14e69c2bfd
7 changed files with 1561 additions and 161 deletions

View File

@@ -436,6 +436,116 @@ class GunicornLogThread(QThread):
self.result_ready.emit(False, error_msg)
logger.error(f"Gunicorn服务日志查看异常: {error_msg}")
class GunicornLogCheckThread(QThread):
"""检查并创建Gunicorn日志文件目录和文件的线程"""
result_ready = Signal(bool, str)
def __init__(self, ssh_client, username, git_url, password):
super().__init__()
self.ssh_client = ssh_client
self.username = username
self.git_url = git_url
self.password = password
def run(self):
try:
# 从git_url中提取项目名去掉.git后缀
project_name = self.git_url.split('/')[-1].replace('.git', '')
logger.info(f"从git_url提取的项目名: {project_name}")
# 构建日志目录路径
log_dir = f"/home/{self.username}/{project_name}/logs"
access_log_path = f"{log_dir}/gunicorn_access.log"
error_log_path = f"{log_dir}/gunicorn_error.log"
logger.info(f"检查日志目录: {log_dir}")
# 检查并创建日志目录
check_dir_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S mkdir -p {log_dir}'"
stdin, stdout, stderr = self.ssh_client.exec_command(check_dir_cmd)
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
# 设置日志目录权限
chmod_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S chmod 755 {log_dir}'"
stdin, stdout, stderr = self.ssh_client.exec_command(chmod_cmd)
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
# 检查并创建访问日志文件
check_access_log_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S touch {access_log_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(check_access_log_cmd)
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
# 设置访问日志文件权限
chmod_access_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S chmod 644 {access_log_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(chmod_access_cmd)
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
# 检查并创建错误日志文件
check_error_log_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S touch {error_log_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(check_error_log_cmd)
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
# 设置错误日志文件权限
chmod_error_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S chmod 644 {error_log_path}'"
stdin, stdout, stderr = self.ssh_client.exec_command(chmod_error_cmd)
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
# 设置日志文件所有者为用户
chown_cmd = f"bash -c 'echo \"{self.password}\" | sudo -S chown -R {self.username}:{self.username} {log_dir}'"
stdin, stdout, stderr = self.ssh_client.exec_command(chown_cmd)
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
result_msg = f"日志目录和文件创建成功:\n目录: {log_dir}\n访问日志: {access_log_path}\n错误日志: {error_log_path}"
self.result_ready.emit(True, result_msg)
logger.info("日志目录和文件创建成功")
except Exception as e:
error_msg = str(e)
self.result_ready.emit(False, error_msg)
logger.error(f"检查并创建日志文件异常: {error_msg}")
class ServerControlThread(QThread):
"""控制服务器设置的线程"""
result_ready = Signal(bool, str)
@@ -578,6 +688,11 @@ class GunicornTab(QWidget):
self.view_logs_btn.clicked.connect(self.view_service_logs)
service_btn_layout.addWidget(self.view_logs_btn)
# 检查日志文件按钮
self.check_log_files_btn = QPushButton("检查日志文件")
self.check_log_files_btn.clicked.connect(self.check_log_files)
service_btn_layout.addWidget(self.check_log_files_btn)
service_layout.addLayout(service_btn_layout)
layout.addLayout(service_layout)
@@ -628,27 +743,19 @@ class GunicornTab(QWidget):
def init_service_content(self):
"""初始化服务文件内容"""
default_content = "[Unit]\n"
default_content += "Description=Gunicorn daemon for myproject\n"
default_content += "Description=Gunicorn Daemon for statuspage Project\n"
default_content += "After=network.target\n\n"
default_content += "[Service]\n"
default_content += "User=【用户名】\n"
default_content += "Group=【用户名】\n"
default_content += "WorkingDirectory=【Django路径】\n"
default_content += "# 所有Gunicorn参数直接在这里配置\n"
default_content += "ExecStart=/usr/bin/gunicorn \\ \n"
default_content += " --pythonpath 【Django路径】 \\ \n"
default_content += " --bind 127.0.0.1:8000 \\ \n"
default_content += " --workers $(nproc) \\ \n"
default_content += " --worker-class sync \\ \n"
default_content += " --timeout 60 \\ \n"
default_content += " --name 【项目名】 \\ \n"
default_content += " --access-logfile 【Django路径】/logs/gunicorn_access.log \\ \n"
default_content += " --error-logfile 【Django路径】/logs/gunicorn_error.log \\ \n"
default_content += " --log-level info \\ \n"
default_content += " 【项目名】.wsgi:application\n"
default_content += "Restart=on-failure\n"
default_content += "RestartSec=5s\n"
default_content += "PrivateTmp=true\n\n"
default_content += "# 以xiaji用户运行确保对/home/xiaji有完全权限\n"
default_content += "User=xiaji\n"
default_content += "Group=xiaji\n"
default_content += "# 项目工作目录\n"
default_content += "WorkingDirectory=/home/xiaji/webstatus\n\n"
default_content += "# 启动前预处理先删除旧socket避免残留再创建目录如果不存在\n"
default_content += "ExecStartPre=/bin/rm -f /home/xiaji/webstatus/sock/gunicorn.sock\n"
default_content += "ExecStartPre=/bin/mkdir -p /home/xiaji/webstatus/sock\n\n"
default_content += "# 单行ExecStart无反斜杠避免格式错误\n"
default_content += "ExecStart=/usr/bin/gunicorn --pythonpath /home/xiaji/webstatus --workers 3 --bind unix:/home/xiaji/webstatus/sock/gunicorn.sock --access-logfile /home/xiaji/webstatus/logs/gunicorn_access.log --error-logfile /home/xiaji/webstatus/logs/gunicorn_error.log statuspage.wsgi:application\n\n"
default_content += "[Install]\n"
default_content += "WantedBy=multi-user.target"
@@ -964,6 +1071,55 @@ class GunicornTab(QWidget):
logger.error(f"Gunicorn服务日志查看失败: {message}")
QMessageBox.warning(self, "错误", f"Gunicorn服务日志查看失败: {message}")
def check_log_files(self):
"""检查并创建Gunicorn日志文件目录和文件"""
if not self.ssh_client:
self.append_output("错误: 未连接到服务器")
return
if not self.username:
self.append_output("错误: 未设置用户名")
return
# 读取config.json获取git_url
try:
import json
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
# 获取第一个服务器的配置
server_config = next(iter(config.values()))
git_url = server_config.get('git_url', '')
if not git_url:
self.append_output("错误: config.json中未找到git_url")
return
except Exception as e:
self.append_output(f"错误: 读取config.json失败: {str(e)}")
return
# 请求用户输入sudo密码
dialog = PasswordDialog(self)
if dialog.exec_() == QDialog.Accepted:
password = dialog.get_password()
self.append_output("正在检查并创建Gunicorn日志文件目录和文件...")
# 创建并启动日志检查线程
self.log_check_thread = GunicornLogCheckThread(self.ssh_client, self.username, git_url, password)
self.log_check_thread.result_ready.connect(self.on_log_check_result)
self.log_check_thread.start()
else:
self.append_output("用户取消了密码输入")
def on_log_check_result(self, success, message):
"""处理日志检查结果"""
if success:
self.append_output(message)
logger.info("Gunicorn日志文件检查和创建成功")
QMessageBox.information(self, "成功", "Gunicorn日志文件检查和创建成功")
else:
self.append_output(f"检查并创建日志文件失败: {message}")
logger.error(f"Gunicorn日志文件检查和创建失败: {message}")
QMessageBox.warning(self, "错误", f"Gunicorn日志文件检查和创建失败: {message}")
def on_control_result(self, success, message):
"""处理控制结果"""
if success: