【Lucky内网穿透】折腾3天终于搞这定了NAS博客的外网访问

wordpress学习73字数 6061阅读20分12秒阅读模式

先说一下基本架构:NAS+Virtual Machine Manager+CentOS-Strem9+BT面板+Lucky实现大内网穿透

群晖自带的WebStation安装完Wordpress后,各种不方便,并且输完IP后还要手动加上5000才能访问DSM,这一点让我非常不爽,于是还是想在熟悉的Linux上装博客算了,于是先在虚拟机里装上CENTOS,然后安装BT面板

重点是怎么实现STUN穿透后IP和端口的不固定还能访问,我的办法是用Python实现自动登录上Lucky,然后把获取到的IP端口信息,更新到mysql数据库和Nginx配置文件中。

在ChatGPT的帮助下,写好了Python代码。

主要实现以下几个功能:

1、自动模拟登录Lucky来获取新的IP和端口

2、获取到信息同时webhook到微信,实现随时可以查看最新信息,原来想用邮件,这个比邮件更快。

3、比对上一次获取到的信息,如果没有变化就啥也不做

4、自动将新IP端口写入Mysql

5、自动将新端口写到网站nginx配置信息里,然后重载nginx让新配置生效

然后在BT的计划任务中,设置成每5分钟跑一次。cd /root&&python refresh-stun.py

以下是所有代码分享和记录,省得以后忘记了,博客的意义就在于此了。以下代码存为XX.PY后,还要和网站的.conf文件放一起,然后放到服务器某个目录下面,就可以用了。

import os
import datetime
import mysql.connector
import re
import subprocess
import requests
from playwright.sync_api import sync_playwright
import time

def send_mail(Alist_100, nas100, Blog100):
    url = "http://wxpusher.zjiecode.com/api/send/message"
    headers = {
        "Content-Type": "application/json"
    }
    payload = {
        "appToken": "xxxxxxxxxxxxxxxxxxxx",
        "title": "公网IP变化通知",
        "content": "<h1>公网信息变化了</h1><br><h2>Alist_100新IP:{0}<br><copy data-clipboard-text=http://{0}>点击复制Alist_100 IP</copy><br><br>nas100新IP:{1}<br><copy data-clipboard-text=http://{1}>点击复制nas100 IP</copy><br><br>Blog100新IP:{2}<br><copy data-clipboard-text=http://wp.fangfa.me:{3}>点击复制wp.fangfa.me新地址</copy><br><br></h2>".format(Alist_100, nas100, Blog100, Blog100.split(":")[-1]), 
        "contentType": 2,
        "uids": ["xxxxxxxxxxxxx"]
    }

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:
        print("数据发送成功")
        return "数据发送成功"
    else:
        print(f"数据发送失败,状态码: {response.status_code}")
        print(f"响应内容: {response.text}")
        return f"数据发送失败,状态码: {response.status_code}"


def log_action(log_file_path, timestamp, Alist_100, nas100, Blog100, action, mail_status, db_status):
    max_lines = 1000
    lines = []

    if os.path.exists(log_file_path):
        with open(log_file_path, "r") as log_file:
            lines = log_file.readlines()
            if len(lines) >= max_lines:
                lines = []

    lines.append(f"{timestamp} {Alist_100} {nas100} {Blog100} {action} {mail_status} {db_status}\n")

    with open(log_file_path, "w") as log_file:
        log_file.writelines(lines)


def read_last_values(log_file_path):
    if os.path.exists(log_file_path):
        with open(log_file_path, "r") as log_file:
            lines = log_file.readlines()
            for line in reversed(lines):
                parts = line.strip().rsplit(" ", 6)
                if len(parts) == 7 and parts[-3] not in ["出错了:", ""]:
                    return parts[1], parts[2], parts[3]
    return "", "", ""


def replace_port_in_conf(port):
    with open('./seofangfa.com.conf', "r", encoding="utf-8") as file:
        content = file.read()
    updated_content = re.sub(r'XXXXXXXXXXXXXX', port, content)

    with open('/www/server/panel/vhost/nginx/seofangfa.com.conf', "w", encoding="utf-8") as file:
        file.write(updated_content)

    try:
        result = subprocess.run(['sudo', 'nginx', '-s', 'reload'], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        print(f"Nginx 配置已重载: {result.stdout.decode()}")
    except subprocess.CalledProcessError as e:
        print(f"重载 Nginx 配置失败: {e.stderr.decode()}")


def update_database(Blog100):
    conn = None
    try:
        print("正在连接到数据库...")
        conn = mysql.connector.connect(
            host="192.168.1.50",
            user="ssssssssssssss",
            password="ssssssssssssssss",
            database="sssssssssssssssss"
        )
        cursor = conn.cursor()
        print("数据库连接成功")

        port = Blog100.split(":")[-1]
        # replace_port_in_conf(port)

        cursor.execute("UPDATE wp_options SET option_value = %s WHERE option_name = 'siteurl'", (f"http://blog.fangfa.me:{port}",))
        cursor.execute("UPDATE wp_options SET option_value = %s WHERE option_name = 'home'", (f"http://blog.fangfa.me:{port}",))

        conn.commit()
        print("数据库更新成功")
        return "数据库更新成功"
    except mysql.connector.Error as err:
        print(f"数据库更新失败: {err}")
        return f"数据库更新失败: {err}"
    finally:
        if conn and conn.is_connected():
            cursor.close()
            conn.close()
            print("数据库连接已关闭")


def run(playwright):
    browser = playwright.chromium.launch(headless=True)
    context = browser.new_context()
    page = context.new_page()

    log_file_path = "run_log.txt"

    try:
        for attempt in range(3):
            print("打开登录页面")
            page.goto("http://192.168.1.50:16601/ssssssssss/#/login")
            page.locator("input").first.wait_for()

            print("输入用户名和密码")
            page.locator("input").first.fill('ssssssssssss')
            page.locator("input[type='password']").fill('ssssssssssssssssss')
            print("点击登录按钮")
            page.locator("button").first.click()
            page.wait_for_load_state('networkidle')
            content = page.content()

            if '退出登录' in content:
                print('登录成功')
                page.goto("http://192.168.1.50:16601/sssssssssss/#/stun")
                page.wait_for_load_state('networkidle')

                print("获取第一个目标元素的值")
                Alist_100 = page.locator("#pane-list table tbody tr:nth-child(2) td.el-descriptions__content > span.el-tag.el-tooltip__trigger > span").first.inner_text()
                print(f'Alist_100: {Alist_100}')
                print("获取第二个目标元素的值")
                nas100 = page.locator("#pane-list table tbody tr:nth-child(7) td.el-descriptions__content > span.el-tag.el-tooltip__trigger > span").first.inner_text()
                print(f'nas100: {nas100}')
                print("获取第三个目标元素的值")
                Blog100 = page.locator("#pane-list table tbody tr:nth-child(12) td.el-descriptions__content > span.el-tag.el-tooltip__trigger > span").first.inner_text()
                print(f'Blog: {Blog100}')

                timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

                print("读取上一次的值")
                last_Alist_100, last_nas100, last_Blog100 = read_last_values(log_file_path)

                db_status = "未变化,无需更新数据库"
                print("比较新值和旧值")
                if (Alist_100 == last_Alist_100 and nas100 == last_nas100 and Blog100 == last_Blog100):
                    action = "值没有变化,不发送通知"
                    mail_status = "无"
                    print(action)
                else:
                    action = "值变化了,发送通知"
                    print(action)
                    mail_status = send_mail(Alist_100, nas100, Blog100)
                    # db_status = update_database(Blog100)
                    port = Blog100.split(":")[-1]
                    replace_port_in_conf(port)

                print("记录日志")
                log_action(log_file_path, timestamp, Alist_100, nas100, Blog100, action, mail_status, db_status)
                
                break  # 成功获取到值后跳出重试循环

            else:
                print('登录失败')
                log_action(log_file_path, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "N/A", "N/A", "N/A", '登录失败', "N/A", "N/A")
                
                if Alist_100 == "未获取" and nas100 == "未获取" and Blog100 == "未获取":
                    print("所有值均为未获取,尝试重启 Lucky 并重试")
                    subprocess.run('sudo nmcli connection up ens3', capture_output=True, text=True, shell=True)
                    subprocess.run('sudo systemctl restart NetworkManager', capture_output=True, text=True, shell=True)
                    subprocess.run('sudo systemctl restart lucky.daji', capture_output=True, text=True, shell=True)
                    time.sleep(5)  # 等待5秒钟
                    continue  # 重试
                else:
                    break  # 如果不是所有值均为未获取,则不重试

    except Exception as e:
        print(f'出错了: {e}')
        log_action(log_file_path, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "N/A", "N/A", "N/A", f'出错了: {e}', "N/A", "N/A")
    finally:
        print("关闭浏览器")
        browser.close()


with sync_playwright() as playwright:
    run(playwright)

 最后更新:2025-2-25
  • 本文由 asdfasd 发表于 2025-02-1221:54:19
  • 转载请务必保留本文链接:http://wp.fangfa.me/wordpress-study/%e6%8a%98%e8%85%be3%e5%a4%a9%e7%bb%88%e4%ba%8e%e6%90%9e%e8%bf%99%e5%ae%9a%e4%ba%86nas%e5%8d%9a%e5%ae%a2%e7%9a%84%e5%a4%96%e7%bd%91%e8%ae%bf%e9%97%ae.html