用Python和PHP 7.4实现与宝塔面板API完全互通的整站程序

📅 2026-08-14 👍 0点赞 💬 0 条评论

一、前言

宝塔面板是目前国内最流行的服务器管理面板之一,提供了丰富的API接口,允许我们通过编程方式管理网站、数据库、FTP、文件等。本文将教你如何用Python作为后端调度器,PHP 7.4作为Web前端展示层,构建一套能与宝塔面板API完全互通的整站程序。

核心思路:

  • Python负责调用宝塔API、执行定时任务、处理复杂逻辑
  • PHP负责展示前端页面、接收用户操作请求、转发给Python
  • 两者通过本地Socket或HTTP通信实现互联

二、准备工作

2.1 环境要求

  • Linux服务器(已安装宝塔面板)
  • Python 3.6+
  • PHP 7.4
  • Nginx/Apache(宝塔自带)

2.2 获取宝塔API密钥

  1. 登录宝塔面板 → 设置 → API接口
  2. 开启API访问,记录下 API_KEYAPI_SECRET
  3. 设置允许访问的IP(建议填 127.0.0.1 或内网IP)

三、整体架构设计

┌─────────────────────────────────────────────┐
│             用户浏览器 (前端)                 │
└────────────────┬────────────────────────────┘
                 │ HTTP请求
                 ▼
┌─────────────────────────────────────────────┐
│          PHP 7.4 Web服务 (Nginx)             │
│    - 用户登录/权限验证                       │
│    - 展示网站列表、数据库状态                │
│    - 提交操作指令                           │
└────────────────┬────────────────────────────┘
                 │ Socket/HTTP
                 ▼
┌─────────────────────────────────────────────┐
│         Python 后端服务 (常驻进程)            │
│    - 调用宝塔API                             │
│    - 签名计算                                │
│    - 缓存管理                                │
│    - 异步任务调度                            │
└────────────────┬────────────────────────────┘
                 │ HTTPS请求
                 ▼
┌─────────────────────────────────────────────┐
│          宝塔面板 API 接口                    │
└─────────────────────────────────────────────┘

四、Python后端开发

4.1 创建项目结构

mkdir bt-panel-proxy
cd bt-panel-proxy
mkdir python php logs
touch python/server.py python/bt_api.py python/config.py
touch php/index.php php/api.php php/config.php

4.2 配置文件 python/config.py

# 宝塔面板配置
BT_PANEL_HOST = "http://127.0.0.1:8888"  # 宝塔面板地址
BT_API_KEY = "你的API_KEY"
BT_API_SECRET = "你的API_SECRET"

# Python服务监听端口
PYTHON_SERVICE_PORT = 9501

# 日志配置
LOG_FILE = "../logs/python_service.log"

4.3 宝塔API封装 python/bt_api.py

import time
import json
import hashlib
import requests
from config import BT_PANEL_HOST, BT_API_KEY, BT_API_SECRET

class BTPanelAPI:
    def __init__(self):
        self.host = BT_PANEL_HOST
        self.key = BT_API_KEY
        self.secret = BT_API_SECRET
    
    def _get_md5(self, data):
        """计算MD5签名"""
        sign_str = data + self.secret
        return hashlib.md5(sign_str.encode('utf-8')).hexdigest()
    
    def _request(self, endpoint, data=None):
        """发送API请求"""
        if data is None:
            data = {}
        
        timestamp = str(int(time.time()))
        data['request_token'] = self._get_md5(timestamp)
        data['request_time'] = timestamp
        
        url = f"{self.host}/api/{endpoint}"
        headers = {
            'Content-Type': 'application/json'
        }
        
        try:
            response = requests.post(url, json=data, headers=headers, timeout=30)
            return response.json()
        except Exception as e:
            return {"status": False, "msg": str(e)}
    
    # -------- 常用接口封装 --------
    
    def get_sites(self):
        """获取网站列表"""
        return self._request("sites")
    
    def create_site(self, domain, path, php_version="74", db_type="mysql"):
        """创建网站"""
        data = {
            "webname": json.dumps({
                "domain": domain,
                "domainlist": [],
                "count": 0
            }),
            "path": path,
            "type_id": 0,
            "type": "PHP",
            "version": php_version,
            "port": 80,
            "ps": "",
            "ftp": False,
            "sql": True,
            "codeing": "utf8",
            "db_type": db_type,
            "db_access": "127.0.0.1"
        }
        return self._request("siteAdd", data)
    
    def delete_site(self, site_id, webname):
        """删除网站"""
        data = {
            "id": site_id,
            "webname": webname
        }
        return self._request("siteDelete", data)
    
    def get_databases(self):
        """获取数据库列表"""
        return self._request("databases")
    
    def create_database(self, name, username, password, codeing="utf8mb4"):
        """创建数据库"""
        data = {
            "name": name,
            "username": username,
            "password": password,
            "codeing": codeing
        }
        return self._request("databaseAdd", data)
    
    def get_ssl_info(self, site_id):
        """获取SSL证书信息"""
        data = {"id": site_id}
        return self._request("getSslInfo", data)
    
    def apply_letsencrypt(self, site_id, domains):
        """申请Let's Encrypt证书"""
        data = {
            "id": site_id,
            "domains": domains,
            "auth_to": "dns"  # 或 http
        }
        return self._request("applyLetEncrypt", data)

4.4 Python主服务 python/server.py

import json
import logging
import socketserver
from urllib.parse import parse_qs
from bt_api import BTPanelAPI
from config import PYTHON_SERVICE_PORT, LOG_FILE

# 配置日志
logging.basicConfig(
    filename=LOG_FILE,
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

api = BTPanelAPI()

class RequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        try:
            data = self.request.recv(65536).decode('utf-8')
            
            # 解析HTTP请求
            lines = data.split('\r\n')
            if not lines:
                return
            
            method_line = lines[0]
            parts = method_line.split(' ')
            if len(parts) < 2:
                return
            
            path = parts[1]
            
            # 提取POST数据
            body = ''
            for i, line in enumerate(lines):
                if line == '' and i + 1 < len(lines):
                    body = '\n'.join(lines[i+1:])
                    break
            
            # 解析JSON请求体
            params = {}
            if body:
                try:
                    params = json.loads(body)
                except:
                    pass
            
            # 路由分发
            result = self.route(path, params)
            response_body = json.dumps(result)
            
            response = (
                "HTTP/1.1 200 OK\r\n"
                "Content-Type: application/json; charset=utf-8\r\n"
                f"Content-Length: {len(response_body)}\r\n"
                "Access-Control-Allow-Origin: *\r\n"
                "\r\n"
                f"{response_body}"
            )
            
            self.request.sendall(response.encode('utf-8'))
            logging.info(f"Request: {path} - Success")
            
        except Exception as e:
            error_msg = json.dumps({"status": False, "msg": str(e)})
            self.request.sendall(error_msg.encode('utf-8'))
            logging.error(f"Error: {str(e)}")
    
    def route(self, path, params):
        """路由分发"""
        routes = {
            '/api/sites': lambda: api.get_sites(),
            '/api/create_site': lambda: api.create_site(
                params.get('domain', ''),
                params.get('path', ''),
                params.get('php_version', '74')
            ),
            '/api/delete_site': lambda: api.delete_site(
                params.get('id', 0),
                params.get('webname', '')
            ),
            '/api/databases': lambda: api.get_databases(),
            '/api/create_database': lambda: api.create_database(
                params.get('name', ''),
                params.get('username', ''),
                params.get('password', '')
            ),
            '/api/health': lambda: {"status": True, "msg": "Python service running"}
        }
        
        handler = routes.get(path)
        if handler:
            return handler()
        else:
            return {"status": False, "msg": "Route not found"}

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True

if __name__ == '__main__':
    server = ThreadedTCPServer(('127.0.0.1', PYTHON_SERVICE_PORT), RequestHandler)
    print(f"Python service started on port {PYTHON_SERVICE_PORT}")
    logging.info(f"Service started on port {PYTHON_SERVICE_PORT}")
    
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()
        print("Service stopped")

五、PHP 7.4前端开发

5.1 PHP配置文件 php/config.php

<?php
define('PYTHON_SERVICE_HOST', '127.0.0.1');
define('PYTHON_SERVICE_PORT', 9501);
define('SESSION_TIMEOUT', 3600); // 1小时

session_start();
?>

5.2 PHP API代理 php/api.php

<?php
require_once 'config.php';

header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');

// 简单的鉴权(生产环境请加强)
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
    echo json_encode(['status' => false, 'msg' => '未登录']);
    exit;
}

$action = $_GET['action'] ?? '';
$params = $_POST;

function callPythonService($endpoint, $data = []) {
    $host = PYTHON_SERVICE_HOST;
    $port = PYTHON_SERVICE_PORT;
    
    $jsonData = json_encode($data);
    $length = strlen($jsonData);
    
    $httpRequest = "POST {$endpoint} HTTP/1.1\r\n";
    $httpRequest .= "Host: {$host}:{$port}\r\n";
    $httpRequest .= "Content-Type: application/json\r\n";
    $httpRequest .= "Content-Length: {$length}\r\n";
    $httpRequest .= "Connection: close\r\n";
    $httpRequest .= "\r\n";
    $httpRequest .= $jsonData;
    
    $socket = @fsockopen($host, $port, $errno, $errstr, 10);
    if (!$socket) {
        return ['status' => false, 'msg' => "无法连接Python服务: {$errstr}"];
    }
    
    fwrite($socket, $httpRequest);
    
    $response = '';
    while (!feof($socket)) {
        $response .= fgets($socket, 4096);
    }
    fclose($socket);
    
    // 提取响应体
    $parts = explode("\r\n\r\n", $response, 2);
    if (isset($parts[1])) {
        return json_decode($parts[1], true) ?: ['status' => false, 'msg' => '响应解析失败'];
    }
    
    return ['status' => false, 'msg' => '无响应'];
}

switch ($action) {
    case 'login':
        $password = $_POST['password'] ?? '';
        if ($password === 'your_admin_password') { // 请修改为强密码
            $_SESSION['logged_in'] = true;
            $_SESSION['login_time'] = time();
            echo json_encode(['status' => true, 'msg' => '登录成功']);
        } else {
            echo json_encode(['status' => false, 'msg' => '密码错误']);
        }
        break;
    
    case 'logout':
        session_destroy();
        echo json_encode(['status' => true, 'msg' => '已退出']);
        break;
    
    case 'get_sites':
        $result = callPythonService('/api/sites');
        echo json_encode($result);
        break;
    
    case 'create_site':
        $required = ['domain', 'path'];
        foreach ($required as $field) {
            if (empty($params[$field])) {
                echo json_encode(['status' => false, 'msg' => "缺少参数: {$field}"]);
                exit;
            }
        }
        $result = callPythonService('/api/create_site', [
            'domain' => $params['domain'],
            'path' => $params['path'],
            'php_version' => $params['php_version'] ?? '74'
        ]);
        echo json_encode($result);
        break;
    
    case 'delete_site':
        $result = callPythonService('/api/delete_site', [
            'id' => intval($params['id'] ?? 0),
            'webname' => $params['webname'] ?? ''
        ]);
        echo json_encode($result);
        break;
    
    case 'get_databases':
        $result = callPythonService('/api/databases');
        echo json_encode($result);
        break;
    
    case 'create_database':
        $required = ['name', 'username', 'password'];
        foreach ($required as $field) {
            if (empty($params[$field])) {
                echo json_encode(['status' => false, 'msg' => "缺少参数: {$field}"]);
                exit;
            }
        }
        $result = callPythonService('/api/create_database', [
            'name' => $params['name'],
            'username' => $params['username'],
            'password' => $params['password']
        ]);
        echo json_encode($result);
        break;
    
    default:
        echo json_encode(['status' => false, 'msg' => '未知操作']);
}
?>

5.3 前端首页 php/index.php

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>宝塔面板管理系统</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: 'Segoe UI', sans-serif; background: #f0f2f5; color: #333; }
        .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
        .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 0; text-align: center; }
        .header h1 { font-size: 28px; margin-bottom: 10px; }
        .card { background: white; border-radius: 12px; padding: 24px; margin: 16px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
        .card h2 { margin-bottom: 16px; color: #444; }
        .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; }
        .btn { display: inline-block; padding: 10px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; transition: all 0.3s; }
        .btn-primary { background: #667eea; color: white; }
        .btn-primary:hover { background: #5a67d8; }
        .btn-danger { background: #e53e3e; color: white; }
        .btn-danger:hover { background: #c53030; }
        .input-group { margin-bottom: 12px; }
        .input-group label { display: block; margin-bottom: 4px; font-weight: 500; }
        .input-group input { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
        table { width: 100%; border-collapse: collapse; margin-top: 12px; }
        th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #eee; }
        th { background: #f8f9fa; font-weight: 600; }
        .loading { opacity: 0.6; pointer-events: none; }
        .error { color: #e53e3e; padding: 8px; background: #fff5f5; border-radius: 6px; }
        .success { color: #38a169; padding: 8px; background: #f0fff4; border-radius: 6px; }
        .login-form { max-width: 400px; margin: 60px auto; }
        .hidden { display: none; }
        .nav { background: white; padding: 12px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .nav-inner { max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; padding: 0 20px; }
        .tab { display: inline-block; padding: 8px 16px; cursor: pointer; border-bottom: 2px solid transparent; margin-right: 8px; }
        .tab.active { border-bottom-color: #667eea; color: #667eea; font-weight: 600; }
        .tab-content { display: none; }
        .tab-content.active { display: block; }
    </style>
</head>
<body>

<div id="loginPage" class="container login-form">
    <div class="card">
        <h2 style="text-align:center;">管理员登录</h2>
        <div class="input-group">
            <label>密码</label>
            <input type="password" id="loginPassword" placeholder="请输入管理密码">
        </div>
        <button class="btn btn-primary" style="width:100%;" onclick="login()">登录</button>
        <div id="loginMsg" style="margin-top:12px;"></div>
    </div>
</div>

<div id="mainPage" class="hidden">
    <div class="nav">
        <div class="nav-inner">
            <span style="font-weight:600;color:#667eea;">宝塔面板管理系统</span>
            <button class="btn btn-danger" onclick="logout()">退出登录</button>
        </div>
    </div>

    <div class="container">
        <!-- Tabs -->
        <div style="background:white;border-radius:12px 12px 0 0;padding:16px 24px 0;">
            <span class="tab active" onclick="switchTab('sites')">网站管理</span>
            <span class="tab" onclick="switchTab('databases')">数据库管理</span>
            <span class="tab" onclick="switchTab('system')">系统状态</span>
        </div>

        <!-- 网站管理 -->
        <div id="tab-sites" class="tab-content active">
            <div class="card">
                <h2>添加新站点</h2>
                <div style="display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:12px;">
                    <div class="input-group">
                        <label>域名</label>
                        <input type="text" id="newDomain" placeholder="example.com">
                    </div>
                    <div class="input-group">
                        <label>路径</label>
                        <input type="text" id="newPath" placeholder="/www/wwwroot/example.com">
                    </div>
                    <div class="input-group">
                        <label>PHP版本</label>
                        <input type="text" id="newPhpVersion" value="74">
                    </div>
                    <button class="btn btn-primary" style="align-self:flex-end;" onclick="createSite()">创建</button>
                </div>
                <div id="siteMsg"></div>
            </div>
            
            <div class="card">
                <h2>网站列表</h2>
                <div id="siteList"><p style="color:#999;">加载中...</p></div>
            </div>
        </div>

        <!-- 数据库管理 -->
        <div id="tab-databases" class="tab-content">
            <div class="card">
                <h2>新建数据库</h2>
                <div style="display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:12px;">
                    <div class="input-group">
                        <label>库名</label>
                        <input type="text" id="newDbName" placeholder="mydb">
                    </div>
                    <div class="input-group">
                        <label>用户名</label>
                        <input type="text" id="newDbUser" placeholder="myuser">
                    </div>
                    <div class="input-group">
                        <label>密码</label>
                        <input type="password" id="newDbPass" placeholder="********">
                    </div>
                    <button class="btn btn-primary" style="align-self:flex-end;" onclick="createDatabase()">创建</button>
                </div>
                <div id="dbMsg"></div>
            </div>
            
            <div class="card">
                <h2>数据库列表</h2>
                <div id="dbList"><p style="color:#999;">加载中...</p></div>
            </div>
        </div>

        <!-- 系统状态 -->
        <div id="tab-system" class="tab-content">
            <div class="card">
                <h2>系统运行状态</h2>
                <div id="systemStatus">
                    <p>Python服务:<span id="pyStatus">检测中...</span></p>
                    <p>PHP版本:<?php echo phpversion(); ?></p>
                    <p>宝塔面板:<span id="btStatus">检测中...</span></p>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
let currentTab = 'sites';

// 检查登录状态
document.addEventListener('DOMContentLoaded', function() {
    checkLogin();
});

function checkLogin() {
    fetch('api.php?action=check_login')
        .then(r => r.json())
        .then(data => {
            if (data.status) {
                showMain();
                loadSites();
                loadDatabases();
                checkSystem();
            }
        })
        .catch(() => {});
}

function login() {
    const pwd = document.getElementById('loginPassword').value;
    const msgDiv = document.getElementById('loginMsg');
    
    fetch('api.php?action=login', {
        method: 'POST',
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: `password=${encodeURIComponent(pwd)}`
    })
    .then(r => r.json())
    .then(data => {
        if (data.status) {
            showMain();
            loadSites();
            loadDatabases();
            checkSystem();
        } else {
            msgDiv.innerHTML = `<div class="error">${data.msg}</div>`;
        }
    });
}

function logout() {
    fetch('api.php?action=logout')
        .then(() => location.reload());
}

function showMain() {
    document.getElementById('loginPage').classList.add('hidden');
    document.getElementById('mainPage').classList.remove('hidden');
}

function switchTab(tab) {
    currentTab = tab;
    document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
    document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
    document.querySelector(`.tab[onclick*="${tab}"]`).classList.add('active');
    document.getElementById(`tab-${tab}`).classList.add('active');
}

function loadSites() {
    const container = document.getElementById('siteList');
    container.innerHTML = '<p style="color:#999;">加载中...</p>';
    
    fetch('api.php?action=get_sites')
        .then(r => r.json())
        .then(data => {
            if (data.status && data.data) {
                let html = '<table><thead><tr><th>ID</th><th>域名</th><th>状态</th><th>操作</th></tr></thead><tbody>';
                data.data.forEach(site => {
                    html += `<tr>
                        <td>${site.id}</td>
                        <td>${site.name}</td>
                        <td>${site.status === '1' ? '✅ 运行中' : '⛔ 已停止'}</td>
                        <td><button class="btn btn-danger btn-sm" onclick="deleteSite(${site.id},'${site.name}')">删除</button></td>
                    </tr>`;
                });
                html += '</tbody></table>';
                container.innerHTML = html;
            } else {
                container.innerHTML = `<div class="error">${data.msg || '获取失败'}</div>`;
            }
        })
        .catch(err => {
            container.innerHTML = `<div class="error">请求失败: ${err.message}</div>`;
        });
}

function createSite() {
    const domain = document.getElementById('newDomain').value;
    const path = document.getElementById('newPath').value;
    const phpVer = document.getElementById('newPhpVersion').value;
    const msgDiv = document.getElementById('siteMsg');
    
    if (!domain || !path) {
        msgDiv.innerHTML = '<div class="error">请填写域名和路径</div>';
        return;
    }
    
    fetch('api.php?action=create_site', {
        method: 'POST',
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: `domain=${encodeURIComponent(domain)}&path=${encodeURIComponent(path)}&php_version=${phpVer}`
    })
    .then(r => r.json())
    .then(data => {
        if (data.status) {
            msgDiv.innerHTML = '<div class="success">创建成功!</div>';
            document.getElementById('newDomain').value = '';
            document.getElementById('newPath').value = '';
            loadSites();
        } else {
            msgDiv.innerHTML = `<div class="error">${data.msg}</div>`;
        }
    });
}

function deleteSite(id, name) {
    if (!confirm(`确定要删除站点 "${name}" 吗?此操作不可恢复!`)) return;
    
    fetch('api.php?action=delete_site', {
        method: 'POST',
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: `id=${id}&webname=${encodeURIComponent(name)}`
    })
    .then(r => r.json())
    .then(data => {
        if (data.status) {
            loadSites();
        } else {
            alert('删除失败: ' + data.msg);
        }
    });
}

function loadDatabases() {
    const container = document.getElementById('dbList');
    container.innerHTML = '<p style="color:#999;">加载中...</p>';
    
    fetch('api.php?action=get_databases')
        .then(r => r.json())
        .then(data => {
            if (data.status && data.data) {
                let html = '<table><thead><tr><th>库名</th><th>用户名</th><th>编码</th></tr></thead><tbody>';
                data.data.forEach(db => {
                    html += `<tr><td>${db.name}</td><td>${db.username}</td><td>${db.codeing}</td></tr>`;
                });
                html += '</tbody></table>';
                container.innerHTML = html;
            } else {
                container.innerHTML = `<div class="error">${data.msg || '获取失败'}</div>`;
            }
        });
}

function createDatabase() {
    const name = document.getElementById('newDbName').value;
    const user = document.getElementById('newDbUser').value;
    const pass = document.getElementById('newDbPass').value;
    const msgDiv = document.getElementById('dbMsg');
    
    if (!name || !user || !pass) {
        msgDiv.innerHTML = '<div class="error">请填写完整信息</div>';
        return;
    }
    
    fetch('api.php?action=create_database', {
        method: 'POST',
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: `name=${encodeURIComponent(name)}&username=${encodeURIComponent(user)}&password=${encodeURIComponent(pass)}`
    })
    .then(r => r.json())
    .then(data => {
        if (data.status) {
            msgDiv.innerHTML = '<div class="success">创建成功!</div>';
            document.getElementById('newDbName').value = '';
            document.getElementById('newDbUser').value = '';
            document.getElementById('newDbPass').value = '';
            loadDatabases();
        } else {
            msgDiv.innerHTML = `<div class="error">${data.msg}</div>`;
        }
    });
}

function checkSystem() {
    fetch('api.php?action=health')
        .then(r => r.json())
        .then(data => {
            document.getElementById('pyStatus').innerHTML = data.status ? '✅ 正常运行' : '❌ 离线';
        })
        .catch(() => {
            document.getElementById('pyStatus').innerHTML = '❌ 无法连接';
        });
    
    document.getElementById('btStatus').innerHTML = '✅ 在线(需手动确认)';
}
</script>
</body>
</html>

六、部署与启动

6.1 上传文件

将整个 bt-panel-proxy 目录上传到服务器的 /www/wwwroot/ 下(或其他你指定的路径)。

6.2 配置Nginx站点

在宝塔面板中添加一个站点,指向 php 目录,并设置伪静态(可选)。

6.3 启动Python服务

cd /www/wwwroot/bt-panel-proxy/python
nohup python3 server.py > ../logs/nohup.out 2>&1 &

可以用 supervisor 做进程守护:

[program:bt_proxy]
command=python3 /www/wwwroot/bt-panel-proxy/python/server.py
directory=/www/wwwroot/bt-panel-proxy/python
autorestart=true
redirect_stderr=true
stdout_logfile=/www/wwwroot/bt-panel-proxy/logs/supervisor.log

6.4 测试连通性

访问 http://你的域名/api.php?action=health,应返回:

{"status":true,"msg":"Python service running"}

6.5 访问后台

浏览器打开站点域名,输入你在 api.php 中设置的密码即可登录管理。

七、扩展功能建议

完成基础搭建后,你可以继续扩展:

  1. SSL证书管理:一键申请Let's Encrypt、查看到期时间
  2. FTP管理:创建/删除FTP账号
  3. 文件管理:列出目录、上传下载文件
  4. 计划任务:通过Python调度执行备份、清理等
  5. 多服务器管理:Python端连接多个宝塔面板
  6. 日志审计:记录所有操作日志

八、注意事项

  1. 安全第一:生产环境务必加强鉴权,建议使用JWT Token替代明文密码
  2. Python服务保活:用supervisor或systemd确保Python进程始终运行
  3. API限流:宝塔API有频率限制,不要在循环中高频调用
  4. HTTPS传输:前后端通信建议全走HTTPS
  5. 错误处理:实际使用中需要完善各类异常情况的处理

九、总结

这套方案实现了:

  • ✅ Python后端封装宝塔全部API接口
  • ✅ PHP前端提供可视化操作界面
  • ✅ 两者通过本地Socket高速通信
  • ✅ 支持网站、数据库的增删查改
  • ✅ 可扩展性强,方便接入更多功能

💬 评论列表 (0)

暂无评论,快来抢沙发吧!

发表评论

😀😂🤣😊😍😘😁😉😎😢😭😡😤👍🙏🤝👏🎉💔❤️🤔🙄🤢🤮🔥💯🚀
验证码
×