Skip to content
gokelePublic

About

No description, website, or topics provided.

Resources

Stars

165 stars

Watchers

0 watching

Forks

Repository files navigation

OVH 控制台

🌐 English | 中文

OVH 独立服务器 / VPS / Eco 系列抢购 + 监控 + 管理控制台。

实时检测 OVH 各数据中心库存,发现可购买的服务器时按用户配置(机房、内存、存储、带宽、vRack)自动下单。后台同时管理已购买服务器的全生命周期(重启 / 重装 / IPMI / BIOS / 启动模式 / 维护工单 / 联系人变更 / 带宽 / 防火墙 / FTP 备份 / vRack / Secondary DNS 等)。支持多 OVH 账户同时管理,抢购 / 监控按账户隔离。

Go (Gin) + SQLite 后端、Vite/React + TanStack + shadcn-ui 前端、//go:embed 单二进制部署(自带 SQLite, 跨平台无依赖)、 强制 OvhCredsGate、多账户支持、双 SQLite driver(modernc.org/sqlite 纯 Go / mattn/go-sqlite3 cgo, build tag 自动选)、 中英双语界面(跟随浏览器,可手动固定)、自动检测 GitHub Releases 更新。

性能说明:预装购物车、连接预热与派发延迟测量。

下载

去 Releases 拿对应平台的二进制,解压即用,不需要装 Go、Node 或 SQLite:

平台 文件
Windows x64 ovh-server-windows-amd64.exe
Linux x64 ovh-server-linux-amd64
Linux ARM64(树莓派 / ARM 云主机) ovh-server-linux-arm64

前端已经用 //go:embed 嵌进二进制,跑起来直接开 http://localhost:19998 就是完整界面。 Linux 上记得 chmod +x。想自己编译见部署方式。

也提供 Docker 镜像(linux/amd64 + linux/arm64):

docker pull ghcr.io/gokele/ovh:latest

订单付款与停止抢购

抢购成功后,任务会保留在队列中并标记为“已下单”,可按状态筛选并进入抢购历史查看订单。已下单任务不再执行,也不能恢复为运行状态;需要再买时请新建任务。旧版本已经自动移除的任务仅保留在抢购历史中,不会重新加入活动队列。

账户页面的订单记录会为 OVH 确认为未付款的订单提供“发起支付”。打开后核对账户、含税金额及可用的绑定支付方式,勾选确认后才扣款。已过期、已付款或没有可用方式的订单会给出提示。付款请求不自动重试;若结果未知,请到 OVH 控制台核对。需要银行验证时也在 OVH 完成。API 凭据须有订单及支付方式读取权限、POST /me/order/*/pay 权限。

付款确认绑定当时的账户、订单、金额、币种和支付方式。刷新后金额或方式改变时,必须重新确认。切换账户后会清除原账户的邮件选择及付款窗口。

监控列表显示该订阅保存的最近一次无货转有货时间,首次检查已有库存不算补货,没有记录时不推测历史。带宽/vRack 选项不再使内存/硬盘库存匹配失效,选定的完整配置仍用于询价与下单。

暂停或删除抢购队列任务后,监控不能重新创建同账户、型号和机房的任务,停止记录跨重启保留。手动恢复或新建同款任务可解除该限制。全局停止监控后重启也会保持停止。

暂停监控会同时暂停对应的抢购任务,并取消尚未结账的本轮尝试。关联范围按型号、绑定账户、机房和配置匹配;未绑定账户的订阅覆盖同范围所有账户。全局停止会暂停所有订阅关联任务。恢复监控后,队列任务仍保持暂停,需手动恢复。已提交 OVH 的订单需要在 OVH 控制台处理。升级前已删除的任务没有新的停止记录,升级时请核对仍启用的自动下单订阅。

独服和 VPS 的续费策略按具体服务的 API 能力判断,不按账户地区统一禁用手动续费。支持时可将自动续费改为手动并保留当前周期;能力读取失败会显示未知,提交结果由 OVH 校验。

官方在线客服

“账户管理 → 在线客服”跟随全站账户选择,提供独立服务器和 VPS 客服入口。点击后固定实际账户,读取客户编号,再申请 POST /auth/token 一次性认证令牌,立即打开 OVH 官方窗口。切换账户或离开该标签页会取消待完成的认证并关闭旧窗口。需要 GET /me、POST /auth/token 权限;令牌不落库、不缓存。

官方聊天页面限制第三方域名内嵌,因此使用独立窗口。界面支持中文和英文,官方聊天当前使用英语;是否能接通人工取决于 OVH 返回结果。请在官方窗口核对客户身份。帮助中心使用浏览器自己的官网登录态,可能与本应用所选账户不同。

实现依据:官方认证 API、官方 Manager 聊天组件。官方窗口接口可能变化。开发验证使用模拟账户和令牌;公开窗口已能加载,但未验证真实账户认证及人工接通。

客服工单

“账户管理 → 客服工单”支持按当前 OVH 账户分页查看工单、读取对话、新建、回复、关闭及重新打开。列表支持状态筛选与标题搜索,对话按客服左侧、我方右侧显示。工单标题、正文、回复及重开理由提供英文填写提示和示例,按原文提交,不会自动翻译。新建时选择工单类别(咨询协助 / 账单问题 / 故障事件)、关联产品和可选的问题子类别;独服/VPS 可选择已有服务,也可手动填写服务名称。

正文支持纯文本段落和换行;附件通过 OVH 帮助中心添加。关闭前会再次确认并校验是否可关闭,重开必须填写理由。创建和回复不会自动重试,结果不明时先刷新列表或到官网核对。API 凭据需具备对应 GET /support/*、POST /support/* 及服务列表读取权限。

技术栈

层 技术
前端 Vite 7 + React 18 + TypeScript + TanStack Router + TanStack Query + shadcn-ui + Tailwind + recharts + react-i18next(中/英)
后端 Go 1.26+ + Gin + 官方 go-ovh SDK
持久化 SQLite(modernc.org/sqlite 纯 Go / mattn/go-sqlite3 cgo 双 driver, build tag 自动选),凭据字段 AES-256-GCM 加密落盘
通知 Telegram Bot(长轮询,无需公网地址)+ 自定义 Webhook(钉钉 / 飞书 / Bark / 自建),多通道冗余
部署 单二进制(前端 //go:embed 进 Go 二进制) 或前后端分开跑

项目结构

.
├── server/   # Go 后端 (Gin, 默认 :19998)
│   ├── main.go
│   ├── webembed_ui.go    # build tag=ui 时把 web/ 整目录 embed 进二进制
│   ├── webembed_noui.go  # 默认 build,无前端
│   └── internal/
│       ├── app/          # State 聚合
│       ├── db/           # SQLite 层 (schema.sql + 各表 CRUD)
│       ├── handlers/     # Gin handler
│       ├── monitor/      # 服务器补货监控
│       ├── vps/          # VPS 补货监控
│       ├── purchase/     # 下单流程
│       ├── price/        # OVH cart 询价
│       ├── ovh/          # 按 account_id 路由的多账户 client 工厂
│       ├── notify/       # 多通道通知(Telegram / 自定义 Webhook)
│       ├── secret/       # 凭据落盘加密(AES-256-GCM)
│       ├── updater/      # 在线更新:下载 / 校验 / 自替换 / 回滚
│       └── ...
└── web/      # 前端 (Vite + TanStack, dev 默认 :19997)
    └── src/
        ├── routes/       # 文件路由
        ├── components/   # 共享组件 + AuthGate / OvhCredsGate
        ├── hooks/        # TanStack Query hooks
        ├── i18n/         # 多语言:语言包(模块化)+ 错误码翻译层 + 本地化格式化
        └── lib/          # 子公司表 / OVH 数据中心常量 / utils

后端详细文档见 server/README.md。

部署方式

方式 A:Docker(推荐)

# 拿一份 docker-compose.yml,改掉里面的 API_SECRET_KEY,然后:
docker compose up -d

# 更新
docker compose pull && docker compose up -d

或者直接 run:

docker run -d --name ovh-console \
  -p 127.0.0.1:20000:20000 \
  -v "$PWD/data:/data" \
  -e API_SECRET_KEY=换成你自己的随机串 \
  --restart unless-stopped \
  ghcr.io/gokele/ovh:latest

镜像由打 tag 时的 GitHub Action 构建并推到 ghcr.io/gokele/ovh,提供 linux/amd64 与 linux/arm64。

数据全在 /data 这一个目录下:SQLite 数据库、数据库加密密钥(.env)、缓存、日志。备份就备份这个目录。

⚠️ 删了这个目录 = 已保存的 OVH 凭据和 Telegram Token 再也解不开。 加密密钥默认自动生成并写进 /data/.env,跟着卷走;也可以用 OVH_DB_KEY 显式提供(迁移、k8s secret 时更可控)。

首次启动时容器会把 /data 的归属改成 PUID:PGID(默认 10001)。 bind mount 进来的宿主目录归属是宿主机那边的 uid,不处理的话容器里的非 root 用户写不进去,程序根本起不来。 想和宿主机当前用户对齐就传 PUID/PGID,或者直接 docker run --user $(id -u):$(id -g)(那样容器不做归属处理,写权限由你保证)。

容器里自更新是停用的。 新二进制只会写进容器的可写层,容器一重建 (compose up -d、重启策略拉起、宿主机重启)就回到镜像里的旧版本 —— 你会看到「更新成功」之后版本号又变回去。界面上会显示「有新版 + 请用 docker pull 更新」而不是更新按钮。

方式 B:单二进制

前端 build → Vite 输出到 server/web/ → Go -tags ui 触发 //go:embed 把整目录嵌入二进制 → 单文件部署、双击即用。

# 1) 前端构建到 server/web/
cd web
npm ci
npm run build

# 2) 编译带前端的单二进制(CGO_ENABLED=0 走纯 Go SQLite,交叉编译不需要 gcc)
cd ../server
CGO_ENABLED=0 go build -tags ui -trimpath \
  -ldflags "-s -w -X github.com/ovh-buy/server/internal/handlers.Version=$(cat ../VERSION)" \
  -o ovh-server .
./ovh-server

Windows 把产物名改成 ovh-server.exe 即可;交叉编译加 GOOS=linux GOARCH=arm64 这类前缀。 不需要外部 SQLite 库,二进制自带。 默认监听 :19998,浏览器打开 http://localhost:19998 即用, 数据库自动建在 ./data/sniper.db。

Release 页提供 Windows amd64 / Linux amd64 / Linux arm64 三个预编译产物,不想自己编译可以直接下。

方式 C:开发(前后端分开跑)

# 后端
cd server
go run .                # 默认 :19998

# 前端(另一个终端)
cd web
npm install
npm run dev             # 默认 :19997, /api/* 自动反代到 19998

浏览器打开 http://localhost:19997。

首次访问

打开浏览器后会依次出现两层全屏遮罩,都过了才能进主界面:

  1. AuthGate:输入 .env 里设置的 API_SECRET_KEY(没设的话用默认值,见下)
  2. OvhCredsGate:无任何 OVH 账户时强制弹出。填账户名称 + OVH 子公司(Zone) + APP KEY / APP SECRET / CONSUMER KEY,Endpoint / IAM 自动派生。后端 POST /api/accounts 落 ovh_accounts 表并真去 OVH 验一次,通过才放行。

凭据通过后,前端立刻在后台 prefetch 三件套(服务器目录 / catalog / 可用性),用户切到服务器列表页直接出数据,不会再走"加载中"。

后续可在"设置 → OVH 账户"加更多账户。每个账户独立的 endpoint / 凭据 / Zone,抢购队列、监控订阅、自动下单全部按账户隔离。账户切换全站只有一个入口(左侧菜单栏),切换后目录 / 价格 / 控制台 / 下单账户全部跟着走。

配置

server/.env.example → 复制成 server/.env 改:

API_SECRET_KEY=...               # 前端访问后端的密钥, 必须改
PORT=19998                       # 后端监听端口
LISTEN_HOST=                     # 空 = 所有网卡(IPv4+IPv6); 127.0.0.1 锁回环; 0.0.0.0 公网
ENABLE_API_KEY_AUTH=true         # 关掉的话所有 /api/* 不验证密钥, 仅本地调试用
GIN_MODE=release                 # debug | release
DEBUG=false                      # true 时启用 debug 日志

# --- 数据库加密(都可不填,首次启动会自动处理)---
OVH_DB_KEY=                      # 加密数据库里 OVH 凭据和 TG token 的密钥
                                 # 没填的话首次启动自动生成一把并写回这个文件
                                 # 备份 .env 时别漏了它: 丢了就再也解不开已存的账户
OVH_ENV_FILE=                    # 配置文件自身的位置, 默认工作目录下的 .env
                                 # systemd / docker 里工作目录未必是程序所在目录,
                                 # 那种情况写绝对路径, 否则密钥可能"这次写进去下次找不到"

# --- Telegram 安全(都可不填,留空即用默认行为)---
TG_WEBHOOK_SECRET_OPTIONAL=false # true 时跳过 secret 校验, 仅本地调试用, 公网部署不要开
TG_ALLOWED_USER_IDS=             # 群聊场景下允许下单的 user id, 逗号分隔; 私聊不需要

OVH 凭据不放 env,通过前端 OvhCredsGate / 设置页"OVH 账户" tab 录入,落 SQLite ovh_accounts 表(每个账户一行,独立 endpoint / AppKey / Secret / ConsumerKey / Zone),加密存储。.gitignore 默认拒绝所有 .env 入库。

通知地址在设置页的「通知通道」里配,不走 env。Telegram 和自定义 Webhook 至少配一个 —— 只要还有一条能用,监控就继续跑。

主要功能

能力总览

能力 状态 说明
深色模式 ✅ 浅色 / 深色 / 跟随系统三态,顶栏右上角一键切换或到「设置 → 外观」选择;选择只存在当前浏览器
中英双语界面 ✅ 默认跟随浏览器语言;顶栏 / 登录页一键切换,手动切换过则以手动选择为准。日期 / 金额格式跟随语言,后端报错消息同样双语(稳定错误码 + 前端按码翻译)
多 OVH 账户 ✅ 独立 endpoint / 凭据 / Zone,抢购队列、历史、监控订阅全按 account_id 隔离
全站单一账户入口 ✅ 只在左侧菜单栏切换,机型列表 / 可用性 / 价格 / 控制台 / 下单账户全部跟着走
三区支持(EU / US / CA) ✅ 子公司归属、目录站点、region 取值、planCode 后缀、机房集合全部按区解析,不写死欧区
实时库存直连 ✅ 目录页红绿点直连 OVH 公开可用性接口(按账户所在站点,60 秒新鲜期),不再依赖最长 2 小时旧的目录缓存;接口失败时回落静态并明示"库存未知",不冒充缺货
抢购队列 ✅ 每机型 × 每机房 × 数量独立任务,可暂停/恢复,fail-fast 不退化到默认配置
服务器补货监控 ✅ 订阅 planCode + 机房,状态变化推 Telegram,检查间隔 5–3600 秒可配
VPS 补货监控 ✅ 型号来自 OVH 实时目录(型号会整代下架,写死会让监控静默失效),区分 Linux / Windows,按子公司连对站点
服务器自动下单 ✅ 监控触发,可指定下单账户;不指定则只通知
VPS 自动下单 ✅ 同上,走 /order/cart/{id}/vps;系统在下单时选定,region 按站点解析
订阅可编辑 ✅ 改配置不重置库存状态和历史 —— 删了重建会让"本来就有货"被误判成补货
Telegram 文本下单 ✅ 5 种消息格式,plancode [机房] [数量] [配置]
Telegram 一键下单按钮 ✅ 上架通知内嵌机房按钮,参数落库、一次性 nonce、防重放
Telegram 安全链 ✅ 发送者授权 → update_id 幂等 → 频率限制 → 一次性按钮
多通道通知 ✅ Telegram + 自定义 Webhook,只要有一条能用监控就继续跑,全挂才停
凭据落盘加密 ✅ AES-256-GCM,密钥首次启动自动生成写进 .env,老库自动迁移
抢购耗时打点 ✅ 查库存 / 建车 / 绑车 / 加购 / 配置 / 选项 / 下单 逐段计时,回答"我慢在哪一步"
后端询价 ✅ POST /api/servers/{planCode}/price,走 OVH cart 真实询价
已购服务器管理 ✅ 电源 / 重装(对齐官方全能力:OS 定制问题·SSH 密钥·安装后脚本·硬件 RAID arrays/热备·软 RAID 盘数·LVM 卷名·ZFS zpool 名·数据保留) / IPMI / BIOS / 启动模式 / 任务 / 维护工单
已购 VPS 管理 ✅ 开关机 / 重装 / 快照 / 控制台 / 改密 / 反解 / 自动备份
网络与防护 ✅ 网卡 / OLA / MRTG 流量图 / DDoS 缓解 / 防火墙 / Backup FTP
合同期(engagement) ✅ 服务器与 VPS 双端,销毁类操作强制二次确认
隐私模式 ✅ 一键打码所有 IP / MAC / 反向 DNS
自动检测更新 ✅ 拉 GitHub Releases 比版本号,有新版显示 ✨
在线更新 ✅ 点一下自替换 + 自动重启,强制校验 SHA256,新版起不来自动回滚
控制台接入方式可选 ✅ HTML5 KVM / Java KVM(.jnlp) / SOL(URL) / SOL(SSH),由用户选
配置绑定狙击 ❌ 已下线

多账户

  • 账户管理:设置页"OVH 账户" tab 增删改查,每条记录有独立名称 + Zone + endpoint + AppKey/Secret/ConsumerKey
  • 账户隔离:抢购队列、抢购历史、监控订阅都标 account_id,后端 goroutine 按 account_id 取对应 OVH client 下单
  • 级联清理:删账户时关联 history / queue 自动删除,监控订阅的"自动下单账户"字段清空(订阅本身保留,只通知不下单)
  • 默认账户:其中一个标 is_default,新建对话框不选时自动用默认账户
  • 凭据校验:新建 / 更新账户都会真去 OVH 调一次 /me,结果放在响应的 valid 字段里(校验失败仍会入库,前端提示后放行,便于先进系统再到设置页修凭据)
  • 子公司与 endpoint 强制同区:zone=US 只能配 ovh-us(同区的 kimsufi-* / soyoustart-* 别名照常可用)。EU / US / CA 三个站点的目录、价格、库存、购物车完全独立,配错的话要一路走到下单才报错,所以在建账户时就拦掉

抢购

  • 服务器列表:卡片网格 + 实时 DC 库存灯(绿可用 / 红缺货),点击直接选配置下单
  • 实时库存:列表页直连 OVH 公开可用性接口拉全量状态(按当前账户所在站点,EU / US / CA 三站库存互不相通),60 秒新鲜期内复用;拉不到时回落目录静态数据并在页面顶部明示 —— 静态状态最长 2 小时旧,把它当"缺货"会错过有货的机器
  • 配置选择器:按 OVH addonFamilies(CPU / 内存 / 系统盘 / 数据盘 / 带宽 / vRack)分组单选,默认值预选
  • 任务编辑与暂停:抢购任务可编辑机房、硬件配置、重试间隔及优惠码,运行中点击编辑会先暂停,保存后手动恢复。监控订阅可编辑监控范围、配置及通知/自动下单参数,同型号按原配置精确定位,保留历史和暂停状态。暂停状态重启后保留;暂停监控会联动暂停关联抢购任务;恢复监控不会自动恢复队列任务,需要在队列中手动恢复。
  • 付款:程序只下单,不会自动付款。抢到后请在订单过期前到 OVH 订单页付款。
  • 抢购队列:每台服务器 × 每个 DC × 数量 独立任务,每个任务绑定到一个 OVH 账户,可暂停 / 恢复 / 删除,按 retry interval 轮询 OVH 库存
  • 优惠码抢购:服务器详情和新建抢购任务支持填写一个可选 OVH 优惠码,随任务及配置持久保存,预上架任务也适用。每次重试都会在结账前重新应用到新购物车;被 OVH 拒绝时取消使用该码继续下单,在购买历史和通知中提示优惠未生效;网络/限流等临时错误继续重试。展示价格未扣除此优惠码,适用条件和实际优惠以 OVH 为准。
  • 预上架预订抢购:队列新建弹窗可搜索库存接口已出现、销售目录尚未收录的 Eco 型号(标记「预上架」)。可提前选择账户、机房、数量及库存已公开的内存/系统盘/存储配置,等待该账户销售目录开放后自动尝试下单;配置缺失或变化时继续等待,不降配购买。等待上架不创建购物车、不消耗失败次数,任务和配置重启后保留。此功能不会提前预留 OVH 库存。
  • fail-fast:用户选的配置匹配不上 OVH 当前可订购的 addon → 整单失败,绝不退化到默认 HDD
  • 有货判定按官方枚举白名单:只有 \d+H(交付时长承诺)算有货,comingSoon / unknown 不算 —— 否则会为永远下不了单的机型反复建单
  • 价格显示:按当前账户所属子公司计价(币种、税率、目录都跟着它走),前端用本地 catalog 算,不走 cart 流程。不提供跨子公司比价 —— 那个下拉曾让人误以为切换了机型目录,照着它下单会被 OVH 拒
  • 耗时打点:每一轮按 查库存 / 建购物车 / 绑定 / 加购 / 必需配置 / 硬件选项 / 下单 分段计时。抢购输了之后唯一有用的信息就是"慢在哪一步" —— 没有这串数字的话,"OVH 就是没货""我这机器网络慢""某一步卡了 8 秒"三种情况长得一模一样,而它们要采取的行动完全不同。抢购历史每行可点开看分解,队列页显示每条链路上一轮的结果和总耗时
  • 后端询价兜底:POST /api/servers/{planCode}/price(body {datacenter, options},账户走 ?account=<id>)。走 OVH cart 真实询价拿含税/不含税/币种,用于本地 catalog 算不出价(缺项 / OVH 改结构 / addon 在目标机房不可订购)时兜底,也给外部脚本一个不必复刻算价公式的入口

监控

  • 服务器补货:订阅 planCode + DC 组合,状态变化推 Telegram。自动下单可选指定账户;不选只通知不下单
  • 检查间隔可配:监控页「检查间隔」点一下就地改,合法区间 5-3600 秒(越界自动夹紧并回传实际生效值),落 kv 表重启保持。下限 5 秒是因为 OVH 可用性接口本身有缓存,更快只会撞限流
  • VPS 补货:同上,针对 OVH VPS 产品线(区分 Linux / Windows 镜像)。型号列表来自 OVH 实时目录而不是写死 —— VPS 型号会整代下架(2025 代已全线退出下单目录),盯着一个停售型号的订阅永远不会响,而症状只是"一直没货",看不出问题在哪。已有订阅指向停售型号的会标「已停售」
  • VPS 自动下单:补货时真的下单(cart → assign → POST /vps → 必需配置 → checkout)。系统(vps_os)是 VPS 下单时就要定的配置项,不是买完再装,所以放在订阅里选。只对"无货→有货"的跳变下单,抢到一台就停
  • 订阅可编辑:服务器和 VPS 订阅都能改配置,不重置库存状态和历史。删了重建会清空 LastStatus,下一轮把"本来就有货"当成补货跳变,发一条根本没发生的通知外加真下单
  • 多通道通知:Telegram + 自定义 Webhook。只要有一条通道可用监控就继续跑,全部挂掉才停 —— 以前 Telegram 一挂丢的不是一条消息,是整个监控
  • 历史时间线:每个订阅完整变化记录

已购服务器管理

  • 账户隔离:所有 /server-control/* 请求由 axios 拦截器自动追加 ?account=<id>,跟随左侧菜单栏选中的当前账户,无需逐 hook 改造
  • 概览:硬件信息 + 服务到期 + IP / 网卡 + MRTG 流量图
  • 电源 / 系统:重启 / 重装(对齐 OVH 官方能力:按模板动态渲染 OS 定制问题——SSH 密钥 / 安装后脚本 / 语言等;文件系统与 RAID 级别按模板兼容性过滤;硬件 RAID 支持 arrays/热备盘;软 RAID 可指定参与盘数;分区支持 LVM 卷名与 ZFS zpool 名;非安装盘组可声明保留数据)/ IPMI 控制台 / 启动模式 / SPLA Windows 解锁 / 任务列表 / BIOS / 安装进度。重装接口加了 per-service TryLock,防双击重复提交
  • 维护:维护记录 + 硬件更换工单(硬盘 / 内存 / 散热)+ 联系人变更(Token 邮件确认)
  • 高级(9 个 sub-tab):Burst / 防火墙 / Backup FTP / Secondary DNS / 虚拟 MAC / vRack / 可订购升级 / 附加选项 / IP 规格
  • 隐私模式:一键打码所有 IP / MAC / 反向 DNS 主机名

其它

  • 账户管理:余额 / 订单记录 / 退款记录 / 邮件历史(按当前账户切换)
  • 抢购历史:订单 + 价格 + 倒计时 + OVH 订单链接直跳,每行带账户标识 chip
  • 详细日志:实时刷新,按级别 / 关键字筛选
  • 自动检测更新:仪表盘 mount 时调一次 GET /api/version/check-update 拉 GitHub releases 比版本号,有新版在版本号旁显示 ✨ chip 跳 release 页;后端纯被动响应,无 goroutine / 无定时

持久化

全部业务数据在 SQLite(data/sniper.db),11 张表:

表 用途
kv 单例配置(TG token / 通知 webhook 地址 / 服务器与 VPS 检查间隔 / 长轮询 offset 等非账户级配置),其中的密钥字段加密存储
ovh_accounts OVH 账户(独立 endpoint / AppKey / Secret / ConsumerKey / Zone / is_default),三个凭据字段加密存储
queue 抢购队列(account_id 关联)
history 抢购历史(account_id 关联)
servers OVH 服务器目录缓存(刷新一次写一次,2h TTL)
catalogs OVH 公共 catalog 每个 subsidiary 一份(2h TTL),浏览页价格走它
monitor_subscriptions 服务器补货订阅(auto_order_account_id 关联)
vps_subscriptions VPS 补货订阅(同上)
server_aliases 服务器本地别名(account_id + service_name 复合主键,不下发 OVH)
telegram_order_buttons TG「一键下单」按钮 UUID → 下单参数,used_at 做一次性 nonce
telegram_updates Telegram update_id 幂等表,防重投重复下单

日志仍走 JSON 文件(data/logs/app.log.json),不进 SQLite。

加密的字段带 enc:v1: 前缀,没有前缀的一律按明文处理 —— 老库升级上来时表里全是明文,不能一律当密文去解。首次启动会就地把已有的明文迁移成密文,幂等,重复启动不会重复加密。

缓存策略

数据 后端 TTL 前端 staleTime 后台轮询 触发刷新
服务器目录 2h(SQLite + 内存 ServerCache) 2h ❌ 完全访问触发 缓存过期时下一次访问 / 手动刷新按钮
OVH catalog(价格) 2h(SQLite catalogs 表) 2h ❌ 同上
实时可用性 —(前端直连 OVH 公开接口) 60 秒 ❌ 访问触发;失败回落目录静态并明示

实时库存由前端直连 OVH 各站点的公开 datacenter/availabilities 接口(不带凭据、不占账户配额),按当前账户所在站点选择 EU / US / CA 数据源 —— 三站库存互不相通,跟错站点等于整页永远"无货"。后端 /api/servers 返回的目录静态可用性仅作回落。

启动时不主动调 OVH,只把 SQLite 现有数据加载到内存。ServerCache 用 SQLite 真实 updated_at 重建时间戳,旧数据不会被当成"刚刷过的"。

安全 / 鉴权

  • 后端所有 /api/*(除少数白名单如 /health / /version / /version/check-update)都要求 X-API-Key 请求头
  • 两层全屏 gate:AuthGate(API 密钥) + OvhCredsGate(至少一个 OVH 账户)
  • API Key 存浏览器 localStorage,失效自动清除并要求重新输入
  • OVH 凭据落 SQLite ovh_accounts 表,前端通过 OvhCredsGate / 设置页"OVH 账户" tab 录入
  • .gitignore 默认拒绝所有 .env 文件入库(只允许 *.env.example),同时挡掉 *.db / data/ / logs/
  • 凭据落盘加密:ovh_accounts 的 AppKey / AppSecret / ConsumerKey、kv 里的 Telegram Token 都是 AES-256-GCM 加密存的。密钥优先取环境变量 OVH_DB_KEY,没有就在首次启动时生成一把写进 .env(权限 0600)
  • ⚠️ 加密防的是"只拿到 db 文件"那一类泄漏 —— 备份被同步到网盘、拷整个目录换机器、把 data/ 打包发给别人排查问题。它防不住 .env 和 db 一起漏出去,那种情况下加密等于没有。而 .env 恰恰是最容易被顺手提交、被贴进 issue 的文件
  • 密钥丢了会拒绝启动:库里有密文却找不到密钥时,程序会停下来并说明怎么办,而不是照常起来。否则表现是"账户都在但每次调 OVH 都报签名错误",没人猜得到是密钥问题,而这时候重新录入凭据会覆盖旧密文,最后一点恢复余地也没了。确实找不回来时用 OVH_DB_KEY_RESET=1 启动,那些账户需要重新录入

多语言实现口径

  • 界面文案全部走 react-i18next,语言包按模块拆分在 web/src/i18n/locales/(中文是事实来源,英文包经 TypeScript 类型强约束与中文同构,漏翻译直接编译报错)
  • 后端消息双语:后端在返回 error / message 的同时带稳定错误码 code(内容寻址,同一文案跨接口复用),前端按码取当前语言译文;没有码的(动态拼接、含 OVH 原文)透传原文
  • 日期 / 相对时间 / 金额走 Intl + date-fns locale,不随语言出现格式混排
  • 加新语言 = 加一个语言包文件,代码不用动

Telegram 消息收取与安全链

收 Telegram 消息只有长轮询(getUpdates)一条路:程序主动去 api.telegram.org 拉。 不需要公网域名和证书,家宽 / NAT 后面 / 没域名的机器都能用一键下单。

早先还支持 webhook(Telegram 推给你),已经删掉。它要求公网 HTTPS 域名 + 受信证书、 端口只能 443/80/88/8443,还必须把回调端点放进鉴权白名单(Telegram 不可能带 X-API-Key), 于是只能靠 secret_token 证明来源,还得为老部署留一个"secret 还没注册"的兼容模式。 长轮询没有入站端点 —— 伪造来源这个问题连同它那一整套机制一起消失了。 升级上来时后端启动会自动 deleteWebhook,不需要手动操作。

剩下的校验链照常生效:

环节 作用 失败响应
发送者授权 只认 tgChatId 配置的那个 chat;群聊还要求 user id 在 TG_ALLOWED_USER_IDS 白名单里 403 unauthorized_actor
update_id 幂等 telegram_updates 表去重。offset 是在下一次 getUpdates 时才确认的,处理完还没推进 offset 就崩了/被自更新重启了,这条会重发 —— 没这层一次版本升级就能重复下单 200 {"duplicate":true}
频率限制 单 chat 每 10 秒最多 8 次 429 rate_limited
一次性按钮 「一键下单」按钮的完整参数落 telegram_order_buttons 表,used_at 原子占用:同一个按钮只能下单一次,超 24h 作废;入队失败自动归还可重试 409 button_already_used / 410 button_expired

按钮参数原来只存在进程内存里,重启后按钮全部失效、且可被无限次重放下单;落库同时解决了这两个问题。

单实例约束:同一个 Bot Token 只能有一个进程在拉 update。两份程序同时跑会互相把对方踢下线, 表现是按钮时灵时不灵、消息随机丢。后端识别到这种冲突会在日志和设置页明确说出来。

多区域(EU / US / CA)注意事项

账户只在左侧菜单栏切换一次,全站跟着走。 机型列表、机房红绿点、价格币种、服务器/VPS 控制台、下单账户,全部按当前账户所在站点显示。 之所以只留一个入口:以前列表页、下单对话框、控制台页各有一个账户选择器且互不同步, 而三个站点的目录互不相通(同一台机器欧区叫 24sk602、美区叫 24sk602-v1-us), "用 A 账户浏览、用 B 账户下单"一键就能做出来 —— 这种任务必然被拒。 同理机房也只列该机型在当前站点真正可选的那些,不再固定显示 16 个。

三个站点是彼此独立的系统,同一个机型在不同子公司下的 planCode、价格、可下单区域都不一样:

项 EU(ovh-eu) US(ovh-us) 说明
planCode 24sk202 24sk202-us / 24sk202-eu 美区目录的机型都带后缀,拿欧区 planCode 查美区可用性会返回空
region 配置项 canada / europe united_states 下单时 POST /order/cart/{id}/item/{id}/configuration 要发的值
亚太机型(sgp/syd/ynm) canada — OVH 把亚太机房归在 canada 这个 region 桶里,没有 apac 这个取值
目录站点 eu.api.ovh.com api.us.ovhcloud.com 由 ovh.CatalogBaseURLForSubsidiary 统一映射

VPS 也是三套独立系统,差异和独服不同(实测公开目录):

项 EU / CA 站点 US 站点
region 取值 canada / europe 只有 united_states
机房集合 11 个(含 BHS / SGP / SYD / YNM) vps-xxx 只有 US-EAST-VA / US-WEST-OR
买欧洲 / 加拿大机房 同一个商品 要买 -eu / -ca 后缀的另一个商品

所以 VPS 下单时 region 不硬猜:先问购物车的 requiredConfiguration,它给一个取值就用那个,给多个才按机房挑(BHS/SGP/SYD/YNM→canada,其余→europe;这张表是从 OVH 自己的 -ca / -eu 变体目录里读出来的)。认不出的机房宁可不提交 region,让 OVH 用默认值 —— 提交一个错的会把整单打掉。

独服这边,region 的合法取值由 (子公司, planCode) 决定而不是机房:美区账户即使下单欧洲机房(gra/fra),region 也必须是 united_states。所以代码不做静态"机房→区域"映射,而是由 catalog.ResolveRegion 从官方目录的 configurations[].values 里取,内存缓存 2 小时;拉不到目录时才退回 ovh.RegionForDCInSubsidiary 的静态兜底。region_test.go 里有联网用例,会把两区目录里每个 (plan × 机房) 组合穷举验一遍。

OVH API 对接

下单流程严格对齐 OVH 官方 order-cart-examples:

POST /order/cart                         → cartId
POST /order/cart/{id}/assign
POST /order/cart/{id}/eco                → itemId
POST /order/cart/{id}/item/{itemId}/configuration × 3  (datacenter / os / region)
POST /order/cart/{id}/eco/options × N
GET  /order/cart/{id}/summary
POST /order/cart/{id}/checkout

VPS 是另一条链路(注意不是 /eco,必需配置项也不同):

POST /order/cart                         → cartId
POST /order/cart/{id}/assign
POST /order/cart/{id}/vps                → itemId   (duration / pricingMode 取自 GET /order/cart/{id}/vps)
GET  /order/cart/{id}/item/{itemId}/requiredConfiguration
POST /order/cart/{id}/item/{itemId}/configuration   (vps_datacenter 必填 / region / vps_os)
POST /order/cart/{id}/checkout

在售型号取自公开目录 GET /order/catalog/public/vps?ovhSubsidiary=XX,用 OVH 自己的 order-funnel:show 标记筛选 —— 不拿 planCode 正则猜代次,猜的话每次换代都得发版,而且分不出"下架了"和"正则没覆盖到"。

价格计算 = 基础 plan 月费 + 各 addon family 选中 addon 月费累加(ovhjk/parser/price.go 1:1 移植到前端 web/src/hooks/use-availability.ts)。

端口

服务 端口
Go 后端(生产单二进制 / 开发) 19998
Vite dev server(仅开发) 19997

常见问题与排查

下面这些都是实际踩过的,按"看到什么现象"查。

界面变成英文了 / 想换语言

界面语言默认跟随浏览器语言:浏览器是英文(或系统语言首位是英文)就显示英文,中文浏览器显示中文。顶栏右上角(登录页也有)的语言按钮可以手动切换;手动切换过之后就固定为手动选择,不再跟随浏览器变化。想恢复"跟随浏览器",把浏览器语言调回去后清一下该站点的 localStorage(删 ovh-lang 键)即可。

一直提示权限不足 / This call has not been granted

申请 token 时 **Rights(权限)**没给全。只给 GET 的话能看不能买 —— 下单、改配置、重装全部会失败。

去对应站点的 /createToken/ 重新申请,Rights 填四条:

GET     /*
POST    /*
PUT     /*
DELETE  /*

「有效期 / Validity」建议选不限(Unlimited),选了期限到期后要重来一遍。 出处:OVHcloud API first steps —— "In order to allow all OVHcloud APIs for an HTTP method, put an asterisk (*) into the field"。

凭据填了但验证不通过 / 一直 401

两种可能:

  1. 在错误的站点申请的 token。EU / US / CA 三站互不通用,拿欧区 token 配美区账户永远登不进去。 先在界面上选好子公司,再点那个链接去申请 —— 链接会跟着子公司变。
  2. Consumer Key 没激活或已过期。申请完 OVH 会给一个授权链接,必须点开确认;有效期到了也要重新申请。

机型搜不到 / 提示"三个站点都查不到"

  • 先确认 planCode 的大小写。它是区分大小写的,而手机键盘会自动把首字母变大写。 程序会在目录里做一次大小写无关查找并把正确写法告诉你。
  • 再确认账户所在大区。美区机型带 -us / -eu / -ca 后缀,欧区和加区不带。

监控订阅建好了但从来不触发

多半是机房代码填错。填之前先看输入框下面那行"该机型在 XX 可选:"的列表,照着填。 大小写不敏感、半角全角逗号都认,但拼错了不会有提示 —— 这种情况日志里会有一条 "指定的机房 … 一个都不在 OVH 返回的列表里"的告警,去日志页搜"机房"能看到。

Telegram 上下单选不了配置

早期版本的解析器要求配置项之间必须是半角逗号,中文输入法打出的全角 , 会让整串配置 变成一个词而被丢掉。现在半角/全角逗号、顿号、空格分隔都认。格式:

24ska01 gra 2 ram-64g,softraid-2x960ssd
型号     机房 数量 配置(可多个)

机房、数量、配置的顺序随意,@账户名 也可以放在任意位置。

自己被 Telegram 机器人挡在外面

设置页的管理员白名单(chat ID)如果用全角逗号分隔,整条白名单会失效。现在两种逗号都认, 但老配置建议去设置页重新保存一次。

建任务时提示超过上限

单个机房最多 20 台、单次最多 60 个任务、队列里最多 500 条。 每条任务都是一次真实的下单尝试,撞上这个数基本可以断定是数量填错了。 确实需要更多的话,分批建,或先去队列页清理掉跑完的任务。

系统进不去了怎么办

服务器控制 → 电源与系统 → 一键救援系统。它把 OVH 后台那四步合成一个按钮: 改 netboot 为救援 → 填收密码的邮箱 → 重启。

手动做最常漏的是最后的重启 —— netboot 改了但没重启,机器还在原系统里跑, 你会对着 SSH 连不上发懵。这里重启是自动发的。

进去之后:约 3~5 分钟,root 密码发到你填的邮箱(留空则发到 OVH 账户联系邮箱), SSH 登录后挂载硬盘修东西。救援系统是网络启动的独立临时 Linux,不动硬盘数据。 修完回到同一个入口点「退出救援模式」切回正常系统 —— 不切回去的话每次重启都会再进救援。

提示「密钥连续错误次数过多」进不去了

从 v0.1.32 起,同一来源连续 10 次密钥错误会被暂时拒绝 5 分钟 —— 这是为了挡住爆破(这个服务能用你的 OVH 账户下单,不设阻力的话端口可达就能一秒试几千次)。

密钥就是后端 .env 里的 API_SECRET_KEY;从没设置过的话默认是 123456 (强烈建议改掉,启动日志里也会红字提醒)。 等冷却结束再输一次正确的即可,输对之后计数立刻清零。 限流按来源 IP 分开算,不会因为别人试错把你挡在外面。

停止 / 重启容器时日志好像少了一段

v0.1.31 及以前:程序不处理 SIGTERM,而 docker compose down / up -d 发的就是它。 Go 收到 SIGTERM 默认当场终止,内存里还没落盘的日志会整段丢掉(实测:日志文件 连创建都没有)。v0.1.32 起会先等在途请求收尾、把日志刷盘、正常关库再退出。

数据库 / 配置放在哪、怎么备份

  • SQLite:工作目录下的 sniper.db(Docker 里是挂载卷)
  • 加密密钥:.env 里的 OVH_DB_KEY

两个必须一起备份。只备份 sniper.db 而丢了 OVH_DB_KEY,已存的 OVH 凭据和 Telegram token 就再也解不开了,只能去 OVH 控制台吊销后重新录入。

About

No description, website, or topics provided.

Resources

Stars

165 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages