8.3 KiB
sendcard PHP 后端
提供一个接口用来向外部服务拉取 token,并把外部回传的 data 备档写入 SQLite 数据库。
前置要求
- PHP 8.x(已使用
declare(strict_types=1)) curl扩展PDO_SQLITE扩展- SQLite 写入权限(默认写入到
./data/sendcard.sqlite) - 如果你用 Apache:需要启用
.htaccess(本项目已添加重写规则)
接口说明
1) 获取 token
GET /api/getcard?type=xianyu
请求参数:
type:必须等于xianyu,否则会报错不执行外部请求
说明(避免和轮询脚本混淆):
- 不需要配置
poll_cron_key,也不需要在 URL 里传device_code/device_code_md5。浏览器只访问带type=xianyu的地址即可。 device_code、device_code_md5、external_base_url(例如82.157.20.83:9091/.../getCredentials?...)都在服务端config.php里,由getcard.php在服务器上 用 cURL 去调外部接口;调用方看不到也不会传这些参数。此处对应 机器 A(即时 Web 请求)。- 定时脚本
getcard_poll.php仍调同一external_base_url,但使用config['poll']里机器 B 的device_code/device_code_md5,与机器 A 分离,便于在上游对 单设备或请求频次 有限制时分流(轮询与即时各走一套设备参数)。
成功响应(HTTP 200,响应体为纯文本,即 token 字符串本身,不是 JSON 包一层):
<外部返回的 data.token,例如 JWT 整段>
错误响应:
- 未带
type=xianyu:
{"error":"Invalid request parameter","need_type":"xianyu"}
- 外部接口返回异常/缺少数据:
{"error":"External response missing data"}
SQLite 备档
每次成功调用并拿到外部 data 后,会把外部返回的字段逐列拆开写入一行记录(并额外保存 raw_json)。
- 数据库文件:
./data/sendcard.sqlite - 表名:
cursor_login_backups
表字段包含(部分):
external_msg,external_codetoken,email,deviceCode,activationCode,status,createTime等raw_json:保存外部完整 JSON(可用于排查)
配置文件
config.php 里可配置:
external_base_url:getcard与getcard_poll共用的外部getCredentials基础地址device_code、device_code_md5:机器 A,仅getcard.php使用poll.device_code、poll.device_code_md5:机器 B,仅getcard_poll.php使用(与 A 不同,用于上游限流/限设备时分流)sqlite_path:SQLite 路径
部署路由(Apache / Nginx)
本项目包含 .htaccess(仅对 Apache 生效),用于把 /api/getcard 重写到 api/getcard.php。
如果你部署在宝塔(通常是 Nginx),建议直接访问:
/api/getcard.php?type=xianyu
这样不依赖重写;如果你确实想保留 /api/getcard 形式,把你宝塔里“重写/伪静态”的设置截图或说明一下,我再给你对应的 Nginx 规则。
接口说明
本仓库里的 config.php 只负责把现成的字符串传给外部接口,没有生成逻辑;下面按你这条 device_code 的常见写法说明各段含义。
device_code 的三段(用 - 拼起来) 示例:
BFEBFBFF000A06A4 - 5210N57MP0004BXP00R2 - c84b8af03a6c40e888f72576a5f34a1f
段 典型来源(Windows 上常见叫法) 第 1 段 BFEBFBFF000A06A4 CPU ProcessorId(wmic cpu get ProcessorId / WMI Win32_Processor.ProcessorId)。BFEBFBFF 是 Intel 上很常见的 CPUID 特征前缀,后面一般是 family/model/stepping 等拼成的十六进制,不是传统意义上的“CPU 出厂序列号”,但常被叫作 CPU 标识/CPU ID。 第 2 段 5210N57MP0004BXP00R2 多为 主板序列号(Win32_BaseBoard.SerialNumber)或 BIOS/机器厂商序列 一类 OEM 串号;也可能是别的固定硬件标识,取决于采集工具具体取哪一项。 第 3 段 c84b8af…(32 位十六进制) 很像 去掉连字符的 UUID(128 bit)。常见对应是 Windows 机器 GUID(注册表 HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid 去掉 -),也可能是程序自己生成的 GUID,要看上游工具怎么定义。 所以你记的「CPU 序列 + 别的」大致对应:CPU ProcessorId + 主板/机器类序列号 + 机器 UUID(或同类唯一 ID),中间用 - 连成一条 device_code。
device_code_md5 从形态看是 32 位小写十六进制,符合 MD5 的常见输出。 在本项目里没有对 device_code 做校验或计算;我用 PHP 对整条 device_code 做 md5(),结果不等于你配置里的 bc5613415689b537679ba22c2feae68c。 因此它可能是:对别的规范化字符串(大小写、是否含 -、段顺序等)做的 MD5,或 带盐/由别的客户端算法 生成,需要以提供 device_code 的那套工具/文档为准。 若你需要和上游完全一致,建议对照当时生成 device_code 的程序或接口文档;若你手头有那套工具的源码或说明,发一段我可以帮你对齐 device_code_md5 的精确算法。
定时轮询 getcard_poll.php
与 getcard.php 调同一套外部接口、同一种返回数据;差别在于:getcard 是即时(Web),getcard_poll 约每 5 分钟(计划任务 + 脚本内随机间隔)。因上游常对 单设备或请求频次 有限制,即时走机器 A(顶层 device_code / device_code_md5),轮询走机器 B(config['poll'] 内同名键,值与 A 不同)。
getcard.php:带type=xianyu,不要poll_cron_key,用机器 A 调外部并写 SQLite、回 token。getcard_poll.php:命令行跑定时 不要key;仅当用 HTTP 触发该脚本时配置poll_cron_key与?key=(见下文方式二)。用机器 B 调外部,并写 txt 日志(与 getcard 的 SQLite 备档可并存)。
已在项目里提供 api/getcard_poll.php:宝塔 计划任务每分钟 执行一次;脚本内未到点会 Skip;到点后按 约 5 分钟 ±3 分钟、不少于 4 分钟 再请求外部,结果追加到 data/getcard_poll_log.txt。请务必将 poll 里机器 B 的 device_code / device_code_md5 填全,否则轮询脚本会因配置校验失败退出。
宝塔里怎么设「定时」(二选一)
方式一:Shell 里直接跑 PHP(不经过 Web)
- 打开 计划任务 → Shell 脚本(或同类「执行脚本」)。
- 执行周期选 每 1 分钟(要足够密;未到点脚本会
Skip退出,不会打外部接口)。 - 脚本内容把路径换成你站点实际路径,例如:
/usr/bin/php /www/wwwroot/你的域名/sendcard.yunzer.cn/api/getcard_poll.php >>/www/wwwroot/你的域名/sendcard.yunzer.cn/data/getcard_poll_cron.log 2>&1
- 若不确定 PHP 路径:宝塔 软件商店 → 已安装的 PHP → 设置 → 命令行版本,常见为
/www/server/php/82/bin/php(版本号按你的来)。
方式二:调你自己站点上的 URL(走 HTTP,等价于「调接口」)
可以,不必在 Shell 里写 php 路径。先在 config.php 里配置 poll_cron_key(一长串随机密钥,勿泄露),则允许通过 Web 访问:
https://你的域名/api/getcard_poll.php?key=你配置的密钥
然后任选其一:
- 宝塔计划任务选 访问 URL(若有),每分钟访问上述地址;或
- Shell 计划任务里每分钟执行:
curl -fsS 'https://你的域名/api/getcard_poll.php?key=你的密钥' >>/path/to/curl.log 2>&1
注意: key 会出现在 Web 服务器访问日志 里,且可能被中间代理记录;更稳妥仍是 方式一 CLI。若用 Nginx 且未配置重写,请用带 .php 的路径:/api/getcard_poll.php?key=...。
写入的文件
data/getcard_poll_state.json:下次允许执行的时间(含文件锁,避免并发重复跑)data/getcard_poll_log.txt:每次拉取的一行日志(时间 + HTTP 状态 + 整段 JSON;失败则写ERROR ...)
间隔规则(脚本内已实现)
- 本次执行后,下次间隔秒数:
max(240, 300 + random_int(-180, 180)),即 4~8 分钟 之间随机。
若你希望轮询成功时也像 getcard.php 一样写入 SQLite,可以再说一下,我可以把那段插入逻辑复用到 getcard_poll.php 里。