sendcard/README.md
2026-04-14 09:47:00 +08:00

154 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- markdownlint-disable MD047 -->
# 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 包一层):
```text
<外部返回的 data.token例如 JWT 整段>
```
错误响应:
- 未带 `type=xianyu`
```json
{"error":"Invalid request parameter","need_type":"xianyu"}
```
- 外部接口返回异常/缺少数据:
```json
{"error":"External response missing data"}
```
## SQLite 备档
每次成功调用并拿到外部 `data` 后,会把外部返回的字段逐列拆开写入一行记录(并额外保存 `raw_json`)。
- 数据库文件:`./data/sendcard.sqlite`
- 表名:`cursor_login_backups`
表字段包含(部分):
- `external_msg`, `external_code`
- `token`, `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 ProcessorIdwmic 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 位十六进制)
很像 去掉连字符的 UUID128 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**
1. 打开 **计划任务****Shell 脚本**(或同类「执行脚本」)。
2. 执行周期选 **每 1 分钟**(要足够密;未到点脚本会 `Skip` 退出,不会打外部接口)。
3. 脚本内容把路径换成你站点实际路径,例如:
```bash
/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))`,即 **48 分钟** 之间随机。
若你希望轮询成功时也像 `getcard.php` 一样写入 SQLite可以再说一下我可以把那段插入逻辑复用到 `getcard_poll.php` 里。