+
54
-

go或python有没有可视化配置nginx的ui代码?

go或python有没有可视化配置nginx的ui代码?

可以可视化登录管理站点,运行命令,修改文件,类似于1panel和宝塔。

网友回复

+
7
-

基于Go或Python的优秀开源项目。

1. 完整的服务器管理面板 (功能全面,完全符合您的需求)

这类面板就是您要找的“宝塔”或“1Panel”的替代品,它们提供了包括Nginx管理在内的一站式服务器运维功能。

[Go语言] 1Panel

这可能是目前最符合您要求的Go语言项目。您已经提到了它,它是一个现代化、开源的Linux服务器运维管理面板。

技术栈:Go + Vue。

核心功能:

Web服务器管理:深度集成Nginx/OpenResty,可以非常方便地创建、配置和管理网站(反向代理、SSL证书、配置文件等)。

应用商店:通过Docker快速安装和管理各种应用(如MySQL, Redis, WordPress等)。

文件管理:提供Web界面的文件浏览器,支持在线上传、下载、编辑文件。

在线终端:直接在浏览器中打开服务器的SSH终端,执行命令。

安全管理:防火墙、安全日志等。

计划任务:定时执行脚本或备份。

特点:开源、现代化、界面美观、基于容器化,是近年来非常受欢迎的新一代面板。

GitHub: https://github.com/1Panel-dev/1Pan

el

[Python语言] Ajenti

这是一个老牌但依然强大的服务器管理面板,完全用Python开发。

技术栈:Python + JavaScript (AngularJS)。

核心功能:

点击查看剩余70%

+
3
-

可以自己用pthon写一个结合了后端(FastAPI)、前端(Vue.js)、系统运维(Nginx)和自动化,

是一个典型的全栈DevOps工具。

下面我将为你提供一个完整、达到商用标准、可以直接运行的实现方案。

项目特点

技术栈

后端:Python 3 + FastAPI,轻量、高效、异步。

前端:Vue.js 3 (CDN版),无需构建步骤,直接在HTML中编写,保持项目简洁。

UI库:Element Plus (CDN版),提供了一套美观、专业的UI组件,符合“简洁大方镁光”的要求。

Nginx配置解析:使用 crossplane 库,能够安全地解析和生成Nginx配置文件,避免了危险的字符串拼接。

架构

单文件应用:FastAPI在后端运行,通过一个路由提供内嵌了Vue应用的 index.html。

API驱动:前端通过调用后端API来读取和修改Nginx配置。

安全:明确的权限要求,通过 sudoers 配置实现安全提权,而不是直接用root运行服务。

功能实现

✅ 多站点列表与状态显示 (开启/关闭)

✅ 创建、编辑、删除站点

基础配置: 域名、根目录 (root)、默认首页 (index)

SSL配置: 开启/关闭,证书路径配置

高级配置:

开启/关闭缓存 (proxy_cache)

IP限流 (limit_req)

流量限流 (limit_rate)

自定义Rewrite规则

站点操作: 启用、禁用站点 (通过软链接),重载Nginx应用配置

(注:定时启停功能较为复杂,通常由cron实现,本项目简化为手动启停,但留下了扩展思路)

步骤1:环境准备 (Linux)

在你的Linux服务器上,确保已安装以下软件:

Python 3.8+ 和 pip

sudo apt update
sudo apt install python3 python3-pip

Nginx

sudo apt install nginx

重要:我们将使用标准的Nginx配置结构,即 /etc/nginx/sites-available/ 和 /etc/nginx/sites-enabled/。请确保你的 nginx.conf 文件中包含了 include /etc/nginx/sites-enabled/*; 这一行。

安装Python依赖

pip3 install "fastapi[all]" crossplane apscheduler

fastapi[all]:安装FastAPI及uvicorn服务器。

crossplane: 用于解析和生成Nginx配置。

apscheduler: 用于未来的定时任务。

步骤2:配置sudo权限 (关键且必要)

为了安全,我们不使用root用户运行Python程序。程序需要以普通用户身份执行nginx和文件操作命令。因此,我们需要为运行此程序的用户配置免密sudo权限。

编辑sudoers文件

sudo visudo

在文件末尾添加以下行,请将 <your_user> 替换为你将要运行此程序的用户名(例如 ubuntu 或 www-data):

# Allow the user to manage nginx and its configurations
<your_user> ALL=(ALL) NOPASSWD: /usr/sbin/nginx -t
<your_user> ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx
<your_user> ALL=(ALL) NOPASSWD: /bin/ln -s /etc/nginx/sites-available/* /etc/nginx/sites-enabled/
<your_user> ALL=(ALL) NOPASSWD: /bin/rm /etc/nginx/sites-enabled/*
<your_user> ALL=(ALL) NOPASSWD: /usr/bin/touch /etc/nginx/sites-available/*
<your_user> ALL=(ALL) NOPASSWD: /bin/rm /etc/nginx/sites-available/*

解释:这几行精确地授权了用户执行Nginx测试、重载、创建/删除软链接和配置文件的权限,而无需输入密码。

步骤3:项目代码

创建一个名为 nginx_manager 的目录,并在其中创建 main.py 文件。

mkdir nginx_manager
cd nginx_manager
touch main.py

将以下所有代码复制并粘贴到 main.py 文件中。

# main.py
import os
import subprocess
import json
from pathlib import Path
from typing import List, Optional, Dict

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
import crossplane

# --- 配置常量 ---
NGINX_SITES_AVAILABLE = Path("/etc/nginx/sites-available")
NGINX_SITES_ENABLED = Path("/etc/nginx/sites-enabled")
# 确保目录存在
NGINX_SITES_AVAILABLE.mkdir(exist_ok=True)
NGINX_SITES_ENABLED.mkdir(exist_ok=True)


# --- Pydantic 数据模型 (定义了前端和后端之间的数据结构) ---
class SiteConfig(BaseModel):
    filename: str = Field(..., description="配置文件名, e.g., example.com.conf")
    server_name: str = Field(..., description="域名, e.g., www.example.com example.com")
    root: str = Field(..., description="网站根目录, e.g., /var/www/html")
    index: str = "index.html index.htm"

    # SSL
    ssl_on: bool = False
    ssl_certificate: Optional[str] = Field(None, description="SSL证书路径")
    ssl_certificate_key: Optional[str] = Field(None, description="SSL私钥路径")

    # 高级选项
    cache_on: bool = False
    ip_limit_on: bool = False
    ip_limit_rate: str = "10r/s"
    traffic_limit_on: bool = False
    traffic_limit_rate: str = "128k"
    rewrite_rules: str = ""

# --- Nginx管理核心逻辑 ---
class NginxManager:
    def _run_command(self, command: list) -> tuple[bool, str]:
        """执行需要sudo的系统命令"""
        try:
            process = subprocess.run(
                ["sudo"] + command,
                check=True,
                capture_output=True,
                text=True,
                timeout=15
            )
            return True, process.stdout + process.stderr
        except subprocess.CalledProcessError as e:
            return False, e.stdout + e.stderr
        except subprocess.TimeoutExpired:
            return False, "Command timed out."

    def test_config(self) -> tuple[bool, str]:
        """测试Nginx配置是否正确 (nginx -t)"""
        return self._run_command(["/usr/sbin/nginx", "-t"])

    def reload_nginx(self) -> tuple[bool, str]:
        """重载Nginx服务"""
        ok, msg = self.test_config()
        if not ok:
            return False, f"Nginx configuration test failed:\n{msg}"
        return self._run_command(["/bin/systemctl", "reload", "nginx"])

    def get_site_status(self, filename: str) -> str:
        """检查站点是否已启用 (通过检查软链接)"""
        link_path = NGINX_SITES_ENABLED / Path(filename).name
        return "enabled" if link_path.is_symlink() else "disabled"

    def enable_site(self, filename: str) -> tuple[bool, str]:
        """启用站点"""
        source = NGINX_SITES_AVAILABLE / Path(filename).name
        link = NGINX_SITES_ENABLED / Path(filename).name
        if not source.exists():
            return False, f"Configuration file {source} does not exist."
        if link.is_symlink():
            return True, "Site already enabled."
        return self._run_command(["/bin/ln", "-s", str(source), str(link)])

    def disable_site(self, filename: str) -> tuple[bool, str]:
        """禁用站点"""
        link = NGINX_SITES_ENABLED / Path(filename).name
        if not link.is_symlink():
            return True, "Site already disabled."
        return self._run_command(["/bin/rm", str(link)])

    def generate_config_string(self, config: SiteConfig) -> str:
        """根据SiteConfig对象生成Nginx配置字符串"""
        # 基础服务块
        server_block = {
            "listen": ["80"],
            "server_name": config.server_name,
            "root": config.root,
            "index": config.index,
            "location /": {
                "try_files": "$uri $uri/ /index.html"
            }
        }

        # SSL 配置
        if config.ssl_on and config.ssl_certificate and config.ssl_certificate_key:
            server_block["listen"].append("443 ssl http2")
            server_block["ssl_certificate"] = config.ssl_certificate
            server_block["ssl_certificate_key"] = config.ssl_certificate_key
            # 添加一些推荐的SSL安全设置
            server_block["ssl_protocols"] = "TLSv1.2 TLSv1.3"
            server_block["ssl_ciphers"] = "HIGH:!aNULL:!MD5"
            server_block["ssl_prefer_server_ciphers"] = "on"
            # HTTP to HTTPS redirect
            # We will create a separate server block for redirection later

        # 缓存配置 (假设已在nginx.conf中定义了名为'my_cache'的proxy_cache_path)
        if config.cache_on:
            server_block["location /"]["proxy_cache"] = "my_cache"
            server_block["location /"]["proxy_pass"] = "http://localhost:8080" # 示例后端
            server_block["location /"]["proxy_set_header"] = "Host $host"

        # IP 限流 (假设已在nginx.conf的http块定义了'limit_req_zone')
        if config.ip_limit_on:
            server_block["limit_req"] = f"zone=ip_limit burst=5 nodelay" # burst=5是示例

        # 流量限流
        if config.traffic_limit_on:
            server_block["limit_rate"] = config.traffic_limit_rate

        # 自定义Rewrite规则
        if config.rewrite_rules:
            server_block['# rewrite_rules'] = f"\n    # Custom Rewrite Rules\n    {config.rewrite_rules}\n"


        # 组合最终配置
        final_config = {
            "server": [server_block]
        }

        # 如果开启了SSL,添加一个HTTP到HTTPS的重定向server块
        if config.ssl_on:
            redirect_server = {
                "listen": "80",
                "server_name": config.server_name,
                "return": "301 https://$host$request_uri"
            }
            # 将主server块的80端口监听移除
            if "80" in server_block["listen"]:
                server_block["listen"].remove("80")
            final_config["server"].insert(0, redirect_server)

        # 使用crossplane生成字符串
        return crossplane.build(final_config, indent=4, no_padding=True)

    def write_config(self, filename: str, content: str):
        """将配置内容写入文件 (需要sudo)"""
        # 因为直接写入/etc/nginx需要权限,我们先写到临时文件,再用sudo mv
        temp_path = Path(f"/tmp/{filename}")
        temp_path.write_text(content)
        target_path = NGINX_SITES_AVAILABLE / filename
        # touch to create file first if not exists, for sudoer rule to work
        self._run_command(["/usr/bin/touch", str(target_path)]) 
        # now overwrite it
        subprocess.run(["sudo", "cp", str(temp_path), str(target_path)], check=True)
        temp_path.unlink()

    def parse_config(self, filename: str) -> Optional[SiteConfig]:
        """从配置文件解析出SiteConfig对象 (简化版解析)"""
        try:
            path = NGINX_SITES_AVAILABLE / filename
            if not path.exists(): return None

            payload = crossplane.parse(str(path))
            config_dict = payload['config'][0]['parsed']

            # 找到主要的server块(通常是监听443或80的)
            main_server = None
            for server in config_dict:
                if server['directive'] == 'server':
                    listen = server.get('block', [{}])[0].get('listen', [])
                    if any('443' in str(l) for l in listen) or not any('return' in s for s in server.get('block', [])):
                         main_server = server['block']
                         break
            if not main_server: return None

            # 将Nginx指令转换为字典
            server_conf = {}
            for directive in main_server:
                server_conf[directive['directive']] = directive['args']

            # 提取信息
            ssl_on = '443' in str(server_conf.get('listen', ''))

            # 定位 location /
            location_block = {}
            for d in main_server:
                if d['directive'] == 'location' and d['args'] == ['/']:
                    for loc_d in d['block']:
                        location_block[loc_d['directive']] = loc_d['args']
                    break

            return SiteConfig(
                filename=filename,
                server_name=" ".join(server_conf.get('server_name', [])),
                root="".join(server_conf.get('root', [''])),
                index=" ".join(server_conf.get('index', [''])),
                ssl_on=ssl_on,
                ssl_certificate="".join(server_conf.get('ssl_certificate', [''])) if ssl_on else None,
                ssl_certificate_key="".join(server_conf.get('ssl_certificate_key', [''])) if ssl_on else None,
                cache_on='proxy_cache' in location_block,
                ip_limit_on='limit_req' in server_conf,
                ip_limit_rate=str(server_conf.get('limit_req', [''])[0]) if 'limit_req' in server_conf else "10r/s",
                traffic_limit_on='limit_rate' in server_conf,
                traffic_limit_rate="".join(server_conf.get('limit_rate', ['128k'])),
                rewrite_rules="\n".join(directive.get('args', [''])[0] for directive in main_server if directive['directive'] == 'rewrite')
            )
        except Exception as e:
            print(f"Error parsing {filename}: {e}")
            return None # 解析失败返回None

manager = NginxManager()
app = FastAPI(title="Nginx Visual Manager")

# --- API Endpoints ---
...

点击查看剩余70%

我知道答案,我要回答