作者: yangyang

  • 我的GitHub第一个项目

    🛡️ SpartanHost Monitor (Universal Edition)

    一款专为 Spartan Host 设计的工业级库存监控系统,支持多平台 Linux 自动适配。提供交互式部署流程、自动邮件提醒及 RESTful API 支持,能够轻松完成库存监控应用的搭建。


    License
    Issues
    Stars
    Node
    Lint
    Architecture
    Style
    Security

    🚀 技术栈支持


    ✨ 核心特性

    •  全系统适配:一键支持 Ubuntu, Debian, CentOS, AlmaLinux, Rocky, Fedora。
    •  交互式部署:安装时动态配置邮箱及密码,实现零代码基础配置。
    •  智能提醒:支持 Gmail 等 SMTP 服务,内置防骚扰冷却机制。
    •  安全加固:管理密码支持自定义或强随机生成,接口受鉴权保护。
    •  进程守护:基于 PM2 实现开机自启、崩溃重启及实时日志监控。
    •  RESTful API:预留库存数据及订阅者管理接口,方便二次开发。

    🚀 快速开始

    1️⃣ 克隆项目 (Git 方式)

    git clone https://github.com/yokopro/spartanhost-monitor.git
    cd spartanhost-monitor

    2️⃣ 执行一键部署脚本

    将自动识别系统环境并安装 Node.js 与 PM2

    # 修复换行符并赋予权限
    sed -i 's/\r$//' deploy.sh && chmod +x deploy.sh
    
    # 运行交互式安装
    ./deploy.sh

    3️⃣ 查看管理密码

    如果在安装时选择了随机生成密码,请运行以下命令查看密码:

    pm2 logs spartan-monitor --lines 50

    🛠️ 运维管理指令

    需求指令
    实时日志pm2 logs spartan-monitor
    状态面板pm2 status
    重启应用pm2 restart spartan-monitor
    停止监控pm2 stop spartan-monitor
    资源监控pm2 monit
    彻底卸载pm2 delete spartan-monitor && rm -rf $(pwd)

    🔗 API 接口文档

    系统默认运行在 3000 端口。

    1️⃣ 实时库存数据

    • EndpointGET /api/stock
    • 说明: 返回当前监控的所有产品及其库存状态。

    2️⃣ 查看订阅者清单

    • EndpointGET /api/subscribers
    • 认证: 需在 Request Header 中添加:password: 你的管理密码

    3️⃣ 系统健康检查

    • EndpointGET /health

    📂 项目结构

    ├── public/                # Web 前端页面 (订阅及展示)
    ├── server.js              # 后端核心逻辑与 API 服务
    ├── config.js              # 自动生成的配置文件 (由 deploy.sh 生成)
    ├── deploy.sh              # 终极全能交互式部署脚本
    ├── package.json           # 项目依赖清单
    └── subscribers.json       # 订阅用户数据存储 (本地 JSON)
    

    ⚠️ 注意事项

    1. Gmail 用户
      • 请务必开启“两步验证”并使用 16 位应用专用密码,而非邮箱登录密码。
    2. 防火墙设置
      • 本脚本会自动尝试开放 3000 端口,若无法访问,请检查云服务商的安全组设置。
    3. 隐私保护
      • .gitignore 已默认忽略 config.js,请勿手动取消,防止授权码泄露至公共仓库。

    联系我

    📧邮件:mail.yaoyuan(@)gmail.com 如果还有其他需求或特色添加,请随时告诉我! 😊

  • 为好兄弟修一张千禧风格照片

    原图

    调整后


    调整前

    调整后


    Camera Raw /滤镜

    调色后的故事“这两张呢,是他提供的照片里拍摄的角度以及构图中是最佳的!在后期影像中改变了画面的构图(16:9)。”

    从照片的色彩中一眼就看到了90年代(千禧)。那时的人们开始为自己做决定,人生的路径高度可预测。从个体户、下海、转行发生了巨大的改变,命运的齿轮开始由自己把握。

    那是一个新旧交替、充满野性与烟火的时代。“芳华”考虑到整体构图协调,我添加了这两个字。我理解的芳华,不是盛放的瞬间,而时当时未被珍惜、后来无法复制的那段时光。“芳华”不是为了赞美当下,其真正的寓意是“人们是身在其中并不自知,但有时回望时,那才是最好的一段时间。”

    总之这两个字有时间差、有距离感。最后这是一张90年代——千禧初期的纪实胶片感。”

  • 年末了

    今年许多事情不尽人意,跌跌撞撞快到小马先生家里了。充满未知且惊险的故事马上要开始了……没有预先排练,只能临场发挥!各位主角(zhǔ jué)准备好了吗?

  • 使用Simple Icons

    结合Gemini设计一款wordpress页脚,我的理念是以简约、留白、高级、apple风格为主。因为是个人blog所以特别鸣谢开源软件提供一切的驱动’‘wordpress、docker’‘以及我最喜爱之一的SpartanHost主机服务商。

    HTMl代码

    <div class="footer-credits" style="text-align: center; font-size: 12px; color: #86868b; margin-top: 25px; display: flex; align-items: center; justify-content: center; gap: 20px; flex-wrap: wrap; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif; -webkit-font-smoothing: antialiased;">
        
        <a href="https://wordpress.com/" target="_blank" style="display: inline-flex; align-items: center; color: #1d1d1f; text-decoration: none; transition: opacity 0.3s ease;">
            <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" style="margin-right: 6px;">
                <title>WordPress</title>
                <path d="M21.469 6.825c.84 1.537 1.318 3.3 1.318 5.175 0 3.979-2.156 7.456-5.363 9.325l3.295-9.527c.615-1.54.82-2.771.82-3.864 0-.405-.026-.78-.07-1.11m-7.981.105c.647-.03 1.232-.105 1.232-.105.582-.075.514-.93-.067-.899 0 0-1.755.135-2.88.135-1.064 0-2.85-.15-2.85-.15-.585-.03-.661.855-.075.885 0 0 .54.061 1.125.09l1.68 4.605-2.37 7.08L5.354 6.9c.649-.03 1.234-.1 1.234-.1.585-.075.516-.93-.065-.896 0 0-1.746.138-2.874.138-.2 0-.438-.008-.69-.015C4.911 3.15 8.235 1.215 12 1.215c2.809 0 5.365 1.072 7.286 2.833-.046-.003-.091-.009-.141-.009-1.06 0-1.812.923-1.812 1.914 0 .89.513 1.643 1.06 2.531.411.72.89 1.643.89 2.977 0 .915-.354 1.994-.821 3.479l-1.075 3.585-3.9-11.61.001.014zM12 22.784c-1.059 0-2.081-.153-3.048-.437l3.237-9.406 3.315 9.087c.024.053.05.101.078.149-1.12.393-2.325.609-3.582.609M1.211 12c0-1.564.336-3.05.935-4.39L7.29 21.709C3.694 19.96 1.212 16.271 1.211 12M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0"/>
            </svg>
            <span style="font-weight: 500;">WordPress</span>
        </a>
    
        <span style="color: #d2d2d7; user-select: none;">|</span>
    
        <a href="https://www.docker.com/" target="_blank" style="display: inline-flex; align-items: center; color: #1d1d1f; text-decoration: none; transition: opacity 0.3s ease;">
            <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" style="margin-right: 6px;">
                <title>Docker</title>
                <path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"/>
            </svg>
            <span style="font-weight: 500;">Docker</span>
        </a>
    
        <span style="color: #d2d2d7; user-select: none;">|</span>
    
        <a href="https://billing.spartanhost.net/aff.php?aff=2704" target="_blank" style="display: inline-flex; align-items: center; color: #1d1d1f; text-decoration: none; transition: opacity 0.3s ease;">
            <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" style="margin-right: 6px;">
                <title>iCloud</title>
                <path d="M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z"/>
            </svg>
            <span style="font-weight: 500;">SpartanHost</span>
        </a>
    
    </div>
    
    <style>
        /* 模拟 Apple 官网的悬停效果 */
        .footer-credits a:hover {
            opacity: 0.7;
        }
    </style>

    说明:

    1. 矢量清晰度:使用内联 SVG 替代 <img>,在 4K 或移动端 Retinia 屏幕上永远保持锐利。
    2. 响应速度:由于 SVG 直接写在 HTML 中,减少了三次网络图片请求,页面加载会更快。
    3. SVG图标:引用simpleicons图标库,开源且免费。

    现在,这款blog的布局、排版都是由我自主设计,风格上参考apple。哎,谁让apple的审美如此时尚呢。

  • 用通俗易懂的方式讲解充电、电流、电压、电池相关知识,帮你建立基础电力概念。


    1️⃣ 电压 (Voltage, V)

    • 作用:就像水管里的水压,推动电流流动。
    • 单位:伏特 (V)
    • 例子:你电动车电池是 60 V,意思是电池能推动电流流动的“压力”大约是 60 伏特。

    2️⃣ 电流 (Current, A)

    • 作用:就像水管里的水流量,决定每秒钟有多少电流流过。
    • 单位:安培 (A)
    • 例子:你的充电器输出 2.6 A,意思是每秒钟有 2.6 安培的电流流进电池,给电池充电。
    • 注意:电流太大会烧坏电池,太小充电会很慢。2.6 A 对 20 Ah 铅酸电池来说是安全且温和的充电速度。

    3️⃣ 功率 (Power, W)

    • 作用:电压 × 电流,表示“电力工作量”或者“用电速度”。
    • 公式

    功率(W)=电压(V)×电流(A)功率(W) = 电压(V) \times 电流(A)功率(W)=电压(V)×电流(A)

    • 例子:你的充电器输出 68–74 V,2.6 A,大约功率是:

    68×2.6177W,74×2.6192W68 \times 2.6 \approx 177 W, \quad 74 \times 2.6 \approx 192 W68×2.6≈177W,74×2.6≈192W

    和标称 200 W 非常接近,说明数据匹配。


    4️⃣ 电池容量 (Capacity, Ah)

    • 作用:就像水桶大小,表示电池能储存多少电量。
    • 单位:安培小时 (Ah)
    • 例子:20 Ah 的电池,理论上可以输出 20 A 电流 1 小时,或者 2 A 电流 10 小时。

    5️⃣ 铅酸电池充电方式

    • 铅酸电池有标称电压(12 V/块),5 块串联就是 60 V。
    • 充电电压通常比标称电压略高(68–74 V),保证电池能充满。
    • 充电电流不宜过大,0.1–0.3C(C 是电池容量 Ah)是安全的充电速度。
      • 你 20 Ah 电池,0.1C ≈ 2 A,0.3C ≈ 6 A → 2.6 A 很安全。

    6️⃣ 插排额定功率

    • 插排上写的 2500 W,表示它能承受的最大功率是 2500 W。
    • 你的充电器只有 200 W,插排承受能力远大于充电器消耗,所以安全无忧

    7️⃣ 电压、电流、功率的关系

    你可以把它类比成水管系统

    概念类比数值对应
    电压 V水压推动水流的压力
    电流 A水流量每秒流过的水量
    功率 W水的能量流速水压 × 流量

    这样理解,就能知道为什么充电器功率低、电压对、插排大功率都没问题。


    “电压、电流、功率、电池容量”概念的水管示意图

    图片由CHATGPT提供

  • 稻盛和夫:成功没有捷径,唯有付出不亚于任何人的努力

    我通过盛和塾的机缘得以结识了各地的企业经营者,并为他们做各类咨询。

    他们当中有许多人的问题都是:“如何才能够实现像京瓷和KDDI这样的成功?”

    这些经营者之所以会提出这个问题,是因为他们认为成功一定有秘诀可寻。

    对于这样的问题,我都会一律答道:

    “成功并没有什么特别的方法,如果你能够以自己为核心,与手下员工共同付出不亚于任何人的努力,那么你就一定能够取得成功。”

    尤其是不少中小企业的经营者,往往都会以自己的公司只不过是“其他企业的供应商”、“规模太小”、“既无技术又无资金”等为借口,认为公司经营不好是理所当然的事。

    然而当企业的经营者一旦产生这种念头时,手下员工就会随之丧失工作积极性,从而导致企业真的陷入萎靡之中。

    事实上,若想要获得成功,一家企业越是身处困境之中,这家企业的经营者就越是应该以身作则。

    率领全体员工付出不亚于任何人的勤勉和努力,除此之外别无他法。

    并且这种努力要必须足以感动上天,并让上天进而施与援手。

    然而要想让企业员工自发生出上面所说的这种工作热情却又是件不容易的事情,企业经营者必须自己首先燃起对于成功的热忱。

    在率先垂范、持之以恒地付出卓越努力的同时,还应该摒除私心,提升自身人格,以期赢得员工的信赖和尊敬。

    大家都熟知的二宫尊德(日本江户末期的农村实践家。—— 译者注)在江户时代,没有用任何奇策异术就把土地和人心都几近荒芜的众多贫困村落改造成为了富饶之地。

    他所用的方法就是躬身亲行,一把锄头,一柄铁锹,从清晨劳作到深夜。

    与此同时又不断向村民们宣讲勤勉、正直、诚实等这些立身处世的最重要的道德伦理观念。

    然后村民们对二宫尊德产生了信赖和尊敬,并开始与他共同辛勤劳作,终于使得整个村庄在物质和精神两方面都变得丰饶起来。

    正如明治时期的思想家内村鉴三所指出的那样:“精诚所至,感天动地”,二宫尊德坚信只要至诚努力,必然能够获得天地的帮助。

    如果还没有得到的话,那也是因为自己的诚意不够,因此二宫尊德任何时候都在毫不松懈地勤奋工作。

    从我自身创办的京瓷和KDDI这两家公司的成长和发展历程也足以证明二宫尊德的教诲和实践都是真理。

    并且这个真理不仅有助于企业经营,对于我们的人生也同样极其重要。

    人是脆弱的动物,一旦遭遇困难,不是从正面去挑战,而是马上寻找借口,意图逃避。

    这样做决不可能成功。

    不管处于何种严峻的状况之中,我们都要从正面接受,竭尽诚意,持续付出不亚于任何人的努力。

    这种态度是成功所必需的。

  • 使用Docker部署WordPress

    一、获取docker安装文档(基于Debian13)

    https://docs.docker.com/engine/install/debian

    二、创建目录(方便管理项目文件以及配置)

    mkdir -p /root/wordpress-app && cd /root/wordpress-app

    三、创建 docker-compose.yml

    使用 nano docker-compose.yml 命令创建文件,并将以下内容粘贴进去。这个配置包含了 WordPress 容器和 MySQL 8.0 数据库。

    services:
      db:
        image: mysql:8.0
        container_name: wordpress_db
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: your_root_password  # 修改为你的数据库管理员密码
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordpress_user
          MYSQL_PASSWORD: wordpress_password       # 修改为你的数据库用户密码
        volumes:
          - db_data:/var/lib/mysql
        networks:
          - wp_network
    
      wordpress:
        image: wordpress:latest
        container_name: wordpress_app
        restart: always
        ports:
          - "8080:80"  # 外部访问端口,如果你想用 80 端口,改为 "80:80"
        environment:
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_USER: wordpress_user
          WORDPRESS_DB_PASSWORD: wordpress_password # 必须与上面 db 处的密码一致
          WORDPRESS_DB_NAME: wordpress
        volumes:
          - wp_data:/var/www/html
        depends_on:
          - db
        networks:
          - wp_network
    
    networks:
      wp_network:
        driver: bridge
    
    volumes:
      db_data:
      wp_data:

    四、启动部署

    在当前目录下运行以下命令:(cd /root/wordpress-app)

    docker compose up -d

    -d 表示在后台运行

    五、验证运行状态

    检查容器是否正常启动:

    docker compose ps

    能看到 wordpress_appwordpress_db 两个容器的状态都是 Up

    六、访问与配置

    1. 打开浏览器,输入服务器 IP 地址和端口:http://你的服务器IP:8080
    2. 你将看到 WordPress 的安装界面。
    3. 按照提示选择语言、设置网站标题、管理员账号和密码。

    七、部署Nginx

    想通过域名(如 blog.youer.com)访问,需要您安装最新版的Nginx,配置一个反向代理:

    先执行

    apt update
    apt install -y nginx

    创建反向代理配置文件

    Nginx 的配置建议每个站点一个文件。创建一个新文件(以你的域名命名,例如 blog.conf):

    nano /etc/nginx/sites-available/wordpress

    将以下内容粘贴进去(注意修改 server_name):

    server {
        listen 80;
        server_name blog.youer.com; # 这里改成你解析好的域名
    
        # 限制上传文件大小(WordPress 上传插件或主题需要)
        client_max_body_size 64M;
    
        location / {
            proxy_pass http://127.0.0.1:8080; # 对应 Docker 中的端口
            
            # 传递真实的客户端信息给后端
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            # 解决 WordPress 登录重定向循环的问题
            proxy_redirect off;
        }
    }

    启用配置并重启

    在 Debian/Ubuntu 系统中,你需要将文件从 sites-available 软链接到 sites-enabled

    # 1. 建立软链接启用配置
    ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
    
    # 2. 测试配置文件语法是否正确
    nginx -t
    
    # 3. 如果显示 syntax is ok,则重启 Nginx
    systemctl restart nginx

    开启 HTTPS (强烈建议)

    既然用了反代,用 Certbot 申请免费的 Let’s Encrypt 证书非常简单:

    # 安装 Certbot
    apt install -y python3-certbot-nginx
    
    # 运行申请脚本(按提示输入邮箱,选 A 和 Y)
    certbot --nginx -d blog.youer.com

    Certbot 会自动修改你的 Nginx 配置文件,把 80 端口强制跳转到 443

    八、常用维护命令

    查看日志(排查报错):docker compose logs -f

    停止并删除容器docker compose down

    重启服务docker compose restart

    备份数据

    • 数据库数据存在 db_data 卷中。
    • 网页文件存在 wp_data 卷中。
    • 物理路径通常在 /var/lib/docker/volumes/ 下。

  • 使用Python3+Nginx写一个监控补货站点

    第一阶段:基础环境准备

    在开始之前,确保你的 Linux 服务器(Ubuntu/Debian 推荐)环境是干净且完备的。

    • 安装必要系统组件
    # 更新系统
    apt update && apt upgrade -y
    # 安装 Python 环境及工具
    apt install -y python3 python3-pip google-chrome-stable nginx curl psmisc
    • 安装 Python 依赖库
    # DrissionPage: 网页自动化
    # Flask/Flask-CORS: 订阅接口
    # pytz: 上海时区处理
    pip3 install DrissionPage Flask flask_cors pytz --break-system-packages
    

    1.5权限与防火墙配置

    在脚本运行前,必须打通 80 端口访问通道:

    • 创建目录并赋权(使用 nano 编辑时可直接执行以下命令):
    mkdir -p /var/www/html
    chown -R www-data:www-data /var/www/html
    chmod -R 755 /var/www/html
    
    • 放行端口(确保云服务器防火墙和系统防火墙双重放行):
    ufw allow 80/tcp    # 网页访问
    ufw allow 5000/tcp  # 订阅接口
    ufw allow 587/tcp   # 邮件发送

    第二阶段:核心代码部署

    我们需要在 /root 目录下创建两个 Python 文件。

    1. 订阅后端 (sub_server.py)

    nano /root/sub_server.py

    用于接收用户在网页上填写的邮箱。

    • 代码要点:监听 5000 端口,将邮箱写入 subscribers.txt
    • 启动命令nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &

    2. 库存监测器 (spartan_monitor.py)

    nano /root/spartan_monitor.py

    这是最核心的脚本。

    • 代码要点
      • MONITOR_CONFIG: 填入你要监控的 PID(ProductID)。
      • MAIL_SETTINGS: 填入你的 QQ 邮箱和 16位授权码
      • SERVER_IP: 必须填入你的公网 IP,否则网页点击订阅会没反应。
      • SAVE_PATH: 默认 /var/www/html/index.html

    第三阶段:文件与目录结构检查

    请确认你的服务器文件布局如下:

    文件路径作用说明备注
    /root/spartan_monitor.py监测脚本手动运行一次测试逻辑
    /root/sub_server.py订阅 API必须保持后台持续运行
    /root/subscribers.txt邮箱数据库权限建议 644,每行一个邮箱
    /root/stock_status.json状态机缓存脚本自动生成,对比上次库存
    /var/www/html/index.html前端展示页确保 Nginx 拥有读取权限

    第四阶段:自动化与持久化 (关键点)

    • 让订阅接口永不掉线

    使用 nohup 启动后,即使你关闭 SSH 窗口,它也会运行。

    nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &
    • 让监测脚本定时运行

    我们使用 Crontab 来实现每 5 分钟自动巡检一次。

    crontab -e

    在文件最下方加入:

    # 每5分钟运行一次监测脚本,并输出日志到 cron_log.log 方便排错
    */5 * * * * /usr/bin/python3 /root/spartan_monitor.py >> /root/cron_log.log 2>&1

    第五阶段:如何进行一次完整的“补货测试”?

    为了确认邮件能不能发出来,你不需要等官方补货,可以手动触发:

    • 添加测试邮箱:手动在 /root/subscribers.txt 填入你的测试邮箱。
    • 修改缓存骗过脚本
      • 打开 /root/stock_status.json
      • 找一个现在状态是 AVAILABLE 的产品,把值改成 "SOLD OUT"
    • 手动运行脚本
    python3 /root/spartan_monitor.py

    查看结果:脚本会发现状态从 “SOLD OUT” 变回了真实网页的 “AVAILABLE”,从而判定为“刚刚补货”,立刻发信。

    第六阶段:常见问题排查 (故障手册)

    • 网页时钟不走:检查网页源代码里的 iso_time 是否正确生成,且浏览器没报错。
    • 订阅显示“后端未连接”:检查服务器 5000 端口是否在防火墙(如腾讯云/阿里云的安全组)放行。
    • 邮件报 SSL 错误:确认代码里使用的是 server.starttls() 而不是 SMTP_SSL(对于 587 端口)。
    • DrissionPage 启动失败:通常是因为残留的 Chrome 进程太多,代码里的 pkill -9 chrome 会尝试清理,如果还不行,请增加服务器 Swap 交换内存。

    日常维护与测试

    • 文件与路径一览表
    文件路径作用运维操作
    /root/sub_server.py订阅 API若点击订阅没反应,重启此脚本
    /root/subscribers.txt邮箱数据库可用 nano 手动增删邮箱
    /root/stock_status.json库存记录删掉此文件可强制脚本触发一次提醒
    /var/www/html/index.html前端展示页直接浏览器访问 http://你的IP 查看
    • 如何重启订阅后端?

    如果你改了代码或者发现后端死掉了,先杀掉它再重启:

    pkill -f sub_server.py
    nohup python3 /root/sub_server.py > /root/sub_server.log 2>&1 &

    核心逻辑复习

    本系统采用 “状态机对比” 模式:

    • 第 1 步:从 stock_status.json 获取上一次状态。
    • 第 2 步:爬取 Spartan 官网获取当前状态。
    • 第 3 步:对比。只有 [之前是 SOLD OUT][现在是 AVAILABLE] 时,才会发送邮件。
    • 第 4 步:更新 JSON 文件和网页。

    这套流程现在已经刻在你的服务器里了。下一步:建议你在浏览器打开你的 IP 地址,亲自测试一下订阅功能是否能正常将邮箱写入 /root/subscribers.txt

    注意❗

    Traceback (most recent call last):
    
      File "/root/spartan_monitor.py", line 234, in <module>
    
        check_stock()
    
      File "/root/spartan_monitor.py", line 96, in check_stock
    
        page = ChromiumPage(co)
    
               ^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_pages/chromium_page.py", line 23, in __new__
    
        browser = Chromium(addr_or_opts=addr_or_opts)
    
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_base/chromium.py", line 41, in __new__
    
        is_headless, browser_id, is_exists, ws_only = run_browser(opt)
    
                                                      ^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_base/chromium.py", line 501, in run_browser
    
        is_exists = connect_browser(chromium_options)
    
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
      File "/usr/local/lib/python3.11/dist-packages/DrissionPage/_functions/browser.py", line 38, in connect_browser
    
        raise BrowserConnectError(_S._lang.BROWSER_CONNECT_ERR1_, port, port, ADDRESS=address)
    
    DrissionPage.errors.BrowserConnectError: 
    
    Browser connect failed, check whether the port 9222 is a browser and "--remote-debugging-port=9222" startup item is added.
    
    Address: 127.0.0.1:9222
    
    Version: 4.1.1.2
    

    这个错误 DrissionPage.errors.BrowserConnectError 是自动化脚本中最常见的报错之一。它意味着 DrissionPage 无法连接到 Chrome 浏览器

    由于你是在 Linux 服务器上以 root 身份运行,通常由以下三个原因造成:

    1. 核心原因:Chrome 无法在 root 模式下正常启动

    Chrome 默认禁止以 root 身份运行,除非添加 --no-sandbox 参数。虽然代码里写了,但如果后台有残留的 Chrome 进程(死锁状态),DrissionPage 尝试启动新实例时会失败。

    解决方法:彻底清理残留进程 在终端运行:

    # 强制杀死所有 Chrome 进程
    pkill -9 chrome
    pkill -9 chromium

    2. 环境兼容性:缺少运行依赖

    在 Linux (Ubuntu/Debian) 上运行 Headless Chrome 需要一些底层的图形库支持(即使你不使用显示器)。

    解决方法:补充安装依赖

    apt-get update
    apt-get install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 libasound2

    3. 代码微调:显式指定无沙盒模式

    请使用 nano /root/spartan_monitor.py 检查你的 check_stock 函数,确保 co (ChromiumOptions) 的设置部分是这样的:

    def check_stock():
        # 强制清理(双重保险)
        os.system("pkill -9 chrome > /dev/null 2>&1")
        
        co = ChromiumOptions()
        co.set_browser_path(CHROME_PATH)
        
        # 必须包含以下几项
        co.set_argument('--no-sandbox')            # root 运行必备
        co.set_argument('--headless')              # 服务器无界面必备
        co.set_argument('--disable-gpu')           # 减少资源占用
        co.set_argument('--disable-dev-shm-usage') # 防止内存不足导致的崩溃
        co.set_argument('--remote-debugging-port=9222') # 显式指定端口
        
        # 如果还是报错,尝试添加这一行,指定一个干净的用户数据目录
        co.set_user_data_path('/tmp/chrome_user_data')
        
        page = ChromiumPage(co)
        # ... 后续代码

    4. 权限问题:/tmp 目录

    有时候 Chrome 无法在默认的 /tmp 目录下创建临时文件。 你可以尝试手动创建刚才指定的目录并赋权:

    mkdir -p /tmp/chrome_user_data
    chmod -R 777 /tmp/chrome_user_data
    

    验证步骤

    修改完代码并清理进程后,直接手动运行:

    python3 /root/spartan_monitor.py

    如果看到以下输出,说明修好了: [2025-xx-xx] 检查中...

  • 使用Docker-Compose部署Nginx Proxy Manager

    本次教程基于Debian12_x86系统

    1. 更新系统

    首先,确保你的系统是最新的。

    apt update && apt upgrade -y

    2. 安装 Docker 和 Docker Compose

    安装 Docker 的必要依赖包

    apt install -y apt-transport-https ca-certificates curl software-properties-common

    添加 Docker 的 GPG 密钥

    curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

    设置 Docker 的 stable 仓库

    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

    更新包列表并安装 Docker :

    apt update
    apt install -y docker-ce docker-ce-cli containerd.io

    安装 Docker Compose

    sudo curl -L "https://github.com/docker/compose/releases/download/v2.39.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose

    创建一个项目目录
    首先,创建一个独立的目录来存放所有相关文件,避免文件混乱。

    mkdir -p ~/nginx-proxy-manager
    cd ~/nginx-proxy-manager

    创建 docker-compose.yml 文件

    nano docker-compose.yml

    粘贴下方代码

    services:
    app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    environment:
    TZ: "Australia/Brisbane"
    ports:
    - '80:80'
    - '81:81'
    - '443:443'
    volumes:
    - ./data:/data
    - ./letsencrypt:/etc/letsencrypt


    保存并退出:

    按 Ctrl + X 退出编辑器

    按 Ctrl + O 保存文件

    按 Enter 确认文件名

    验证文件是否创建成功

    创建完成后,使用以下命令检查文件内容:

    查看文件内容

    cat docker-compose.yml

    或者查看文件详细信息

    ls -la docker-compose.yml

    启动服务

    docker-compose up -d
    

    后续管理 Nginx Proxy Manager Docker 指令:

    # 启动服务
    docker-compose up -d
    
    # 查看日志
    docker-compose logs
    
    # 停止服务
    docker-compose down
    
    # 重启服务
    docker-compose restart

    常见 YAML 格式错误

    • 缩进问题:必须使用空格,不能使用 Tab 键
    • 冒号后面必须有空格key: value 而不是 key:value
    • 字符串引号:可以使用单引号、双引号或不使用引号,但要保持一致
    • 列表格式:使用 - 开头表示列表项

    文稿参考:https://nginxproxymanager.com/

  • 安装 Immich

    要求

    • 具有至少 4GB RAM 和 2 个 CPU 内核的系统。
    • Docker

    设置服务器

    第 1 步 – 下载所需的文件

    创建一个您选择的目录(例如 )来保存 和 文件。./immich-appdocker-compose.yml.env

    移动到您创建的目录

    mkdir ./immich-app
    cd ./immich-app

    通过运行以下命令下载 docker-compose.yml 和 example.env

    获取docker-compose.yml文件

    wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml

    获取 .env 文件

    wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env

    或者,您可以从浏览器下载这两个文件,并将它们移动到您创建的目录,在这种情况下,请确保将重命名为 。example.env.env

    步骤 2 – 使用自定义值填充 .env 文件

    默认环境变量内容

    # You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables

    # The location where your uploaded files are stored
    UPLOAD_LOCATION=./library

    # The location where your database files are stored. Network shares are not supported for the database
    DB_DATA_LOCATION=./postgres

    # To set a timezone, uncomment the next line and change Etc/UTC to a TZ identifier from this list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
    # TZ=Etc/UTC

    # The Immich version to use. You can pin this to a specific version like "v2.1.0"
    IMMICH_VERSION=v2

    # Connection secret for postgres. You should change it to a random password
    # Please use only the characters `A-Za-z0-9`, without special characters or spaces
    DB_PASSWORD=postgres

    # The values below this line do not need to be changed
    ###################################################################################
    DB_USERNAME=postgres
    DB_DATABASE_NAME=immich
    • 填充您存储备份资产的首选位置。它应该是服务器上具有足够可用空间的新目录。UPLOAD_LOCATION
    • 考虑更改为自定义值。Postgres 不公开,因此此密码仅用于本地身份验证。 为避免 Docker 解析此值时出现问题,最好仅使用字符 。 是一个方便的实用程序。DB_PASSWORDA-Za-z0-9pwgen
    • 通过取消注释该行来设置您的时区。TZ=
    • 如有必要,填充自定义数据库信息。

    第 3 步 – 启动容器

    从您在步骤 1 中创建的目录(现在应该包含您的自定义和文件),运行以下命令以将 Immich 作为后台服务启动:docker-compose.yml.env

    启动容器

    docker compose up -d

    试用 Web 应用

    第一个注册的用户将是管理员用户。管理员用户将能够将其他用户添加到应用程序中。

    要注册管理员用户,请访问 Web 应用程序,然后单击“入门”按钮。http://<machine-ip-address>:2283

    按照提示注册为管理员用户并登录应用程序。

    下载移动应用程序

    手机应用程式可从以下位置下载: