Wavelog 3.2.4 常用 CLI 命令与工具详解
目标:把 Wavelog 3.2.4 在服务器上的命令行运维面一次讲全——mariadb 客户端直查数据库(含 6 条列名纠错实录)、mariadb-dump 备份与恢复演练(含 ERROR 1044 权限实录)、migrate 迁移端点、cron/run 手动触发与 429 限流对照实验、API v1 十二例与 API v2 十例 curl 全回显、PHP 运行时与日志自检。全部命令与输出均为 2026-11-01 在已部署实例上的实测回显。
Wavelog 3.2.4 常用 CLI 命令与工具详解
目标:把 Wavelog 3.2.4 在服务器上的命令行运维面一次讲全——mariadb 客户端直查数据库(含 6 条列名纠错实录)、mariadb-dump 备份与恢复演练(含 ERROR 1044 权限实录)、migrate 迁移端点、cron/run 手动触发与 429 限流对照实验、API v1 十二例与 API v2 十例 curl 全回显、PHP 运行时与日志自检。全部命令与输出均为 2026-11-01 在已部署实例上的实测回显。
一、环境信息
本文所有命令实测于以下环境(版本复核日期 2026-11-01):
- 应用版本:Wavelog 3.2.4(官方最新稳定版;实例部署过程见站内 《Wavelog 3.2.4 Docker Compose 部署指南》)
- 技术栈:PHP 8.5.12 / Apache 2.4.69 (Debian) / MariaDB 11.8.9 / 数据库迁移版本 307 / ADIF 3.1.8
- 栈内网络:
10.89.59.0/24,数据库服务主机名wavelog-324-db(10.89.59.10),应用服务wavelog-324-main(10.89.59.20),Web 入口http://127.0.0.1:8137/ - 数据库坐标:库
wavelog,业务账号wavelog/q1w2e3r4;数据库 root 初始口令为镜像随机生成(见部署篇一、) - Web 管理员:
root/q1w2e3r4(呼号 BH1ABC,网格 OM89KK,user_type=99) - 本文使用的凭据(均为演示环境生成值,生成过程见三、):v1 API key
wl1b5b5e4f61ee5;v2 API tokenwl2_03892f48d7e67050abbb5edcf64d7d9e624a0cf0(名称 ops-token);cron 访问令牌3104a9e2d58faefc23760c9f7484786bb32b0933371f01ac729da4c27b3248a6
💡 视角约定:本文按「部署完成后的服务器」视角撰写——数据库命令用
mariadb客户端连栈内主机名wavelog-324-db(备份恢复演练在数据库机上以 localhost 执行),HTTP 命令在应用机上用 curl 打127.0.0.1:8137或容器内localhost。四篇分工一句话:选型看 A1、部署看 B2、参数看 C1、命令看本文(A1:《Wavelog 介绍与选型指南》,C1:《Wavelog 3.2.4 配置参数详解》)。
二、工具概览
Wavelog 没有自带专门的管理 CLI 二进制,服务器上的命令行运维面由两类入口构成:数据库客户端直查 + curl 打 HTTP 端点。日常七类操作与对应工具:
| 工具/入口 | 角色 | 本文章节 |
|---|---|---|
mariadb 客户端 | 直查 users/options/cron/QSO 等 54 张表,排障与核对配置 | 4.1 |
mariadb-dump | 逻辑备份与恢复演练 | 4.2 |
curl → /index.php/migrate | 升级镜像后的数据库 schema 迁移 | 4.3 |
curl → /index.php/cron/run(+ X-Wavelog-Auth 头) | 后台任务手动触发与限流观察 | 4.4 |
curl → /index.php/api/*(v1,key 认证) | 旧版程序化日志接口:ADIF 导入导出、台站管理 | 4.5 |
curl → /index.php/api/v2/*(v2,Bearer token + scopes) | 新版接口:结构化 CRUD 与统计 | 4.6 |
php -m / php -i / 日志目录 | PHP 运行时自检与应用/Apache 日志检索 | 4.7 |
💡 技术拓展:v1 与 v2 是 Wavelog 的两代 API,共存而非替代——
维度 v1( /index.php/api/)v2( /index.php/api/v2/)认证 key(JSON body 或 URI 路径) Authorization: Bearer <token>权限粒度 账号级 rw 一把梭 scopes 逐项授权(qso:write、radio:write…) 写入格式 ADIF 标签串 结构化 JSON 字段 响应形态 业务 JSON 裸返回 {"data":…,"meta":…}信封操作面 导入/导出/查询 GET/POST/PATCH/DELETE 全套 官方文档:v1 见 https://docs.wavelog.org/developer/api/ ,v2 见 https://docs.wavelog.org/developer/api-v2/ 。
三、前置条件
跟读本文实测前需要具备:
- 已完成部署并通过初始化向导的 Wavelog 3.2.4 实例(主线见 《Wavelog 3.2.4 Docker Compose 部署指南》);
- 服务器上有
mariadb-client(Ubuntu/Debian:apt install mariadb-client,自带mariadb与mariadb-dump)与curl; - 管理员登录态——实测以 root 账号表单登录(凭据 q1w2e3r4):
login: 303 -> http://127.0.0.1:8137/dashboard
dashboard: 200 url=http://127.0.0.1:8137/index.php/dashboard
<title>Dashboard - Wavelog</title>
- 两枚 API 凭据在 Web UI 生成,实测生成即落表:
v1 key(Account → API,提交后 303 回跳):
api/generate: 303 -> http://127.0.0.1:8137/api
id key rights status creation_date
1 wl1b5b5e4f61ee5 rw active 2026-11-01 09:41:31
v2 token(Account → API Tokens,勾选 scopes 后生成):
api_token/generate: 303 -> http://127.0.0.1:8137/api
id token_name scopes status expires_at created_at
1 ops-token qso:read,qso:write,station:read,statistic:read,lookup:read active 2027-11-01 09:41:31 2026-11-01 09:41:31
解读:v1 key 是 wl1 前缀短串、权限只有 rw 一档;v2 token 是 wl2_ 前缀长串,scopes 逐项列出、默认一年过期(2027-11-01)。两枚凭据的完整值只在生成时展示一次,数据库里 v1 只存前缀(见 4.1.3),遗失只能重生成。
四、各工具详解
4.1 mariadb 客户端直查数据库
4.1.1 连接方式与全库 54 表速览
业务账号直连栈内数据库(-h 用栈内主机名或 IP 10.89.59.10 均可):
mariadb -h wavelog-324-db -uwavelog -pq1w2e3r4 wavelog -e "SHOW TABLES;"
实测全库 54 张表(输出节选照抄,空格分隔为原样):
TABLE_HRD_CONTACTS_V01 adif_modes api api_token bandedges bands bandxuser cat club_permissions config contest contest_qsos contest_session cron cwmacros dxcc_entities dxcc_exceptions dxcc_master dxcc_prefixes dxcc_temp dxpedition eQSL_images hams_of_note iota label_templates label_types lotw_certs lotw_users migrations notes oqrs paper_types pota_boundaries pota_directory primary_subdivisions qsl_images qsl_postcard_templates queries satellite satellitemode sota_directory station_logbooks station_logbooks_relationship station_profile themes thirdparty_logins timezones tle user_options users vuccgrids webadif wwff_directory
运维最常打交道的表:
| 表 | 内容 | 对应 UI/章节 |
|---|---|---|
TABLE_HRD_CONTACTS_V01 | QSO 主日志表(ADIF 列名体系) | 4.1.4 |
users / user_options | 账号与每用户偏好 | 4.1.2 |
options | 全局动态配置 | C1 3.2 |
cron | 21 个后台任务定义 | C1 5.8 |
api / api_token | v1 key / v2 token | 三、 |
station_profile / station_logbooks / station_logbooks_relationship | 台站档案、日志簿、二者关联 | 4.5.5 |
migrations | schema 版本戳(当前 307) | 4.3 |
config | LotW/eQSL 服务地址常量 | 4.1.5 |
dxcc_* / *_directory / timezones | 参考数据(备份体积大头,见 4.2.1) | 4.2 |
4.1.2 users 表:照 Cloudlog 习惯写列名会报错(实录)
第一反应按常见命名查询,实测报错:
--------------
SELECT user_id, username, email, callsign, user_level FROM users
--------------
ERROR 1054 (42S22) at line 1: Unknown column 'username' in 'SELECT'
先看真实列名(SHOW COLUMNS FROM users,输出节选实测首尾):
user_id,user_name,user_password,user_email,user_type,clubstation,external_account,user_callsign,user_locator,user_firstname,user_lastname,user_timezone,user_lotw_name,user_lotw_password,user_eqsl_name,user_eqsl_password,user_eqsl_qth_nickname,active_station_logbook,user_language,user_clublog_name,user_clublog_password,user_clublog_callsign,user_measurement_base,user_date_format,user_stylesheet,...,login_attempts,slug,creation_date,last_modified
正确查询实测:
user_id user_name user_email user_callsign user_type active_station_logbook login_attempts
1 root root@quzuo.net BH1ABC 99 1 0
解读:user_type=99 即所有者级(对应 config.php auth_level[99]='Administrator',见 C1 5.2);login_attempts=0 可作爆破排查指标;active_station_logbook=1 指向当前活动日志簿——它直接决定 API 统计口径(4.5.5 有对照实验)。
4.1.3 api / api_token 表:v1 key 只落前缀,last_used_at 不存在(实录)
id key_prefix rights status
1 wl1b5b5e rw active
v1 key 在库里只存 key_prefix(wl1b5b5e),完整值不可从库反查——这是凭据「展示一次」设计的存储侧证据。想查 token 最近使用时间时踩了第二个坑:
--------------
SELECT id, token_name, LEFT(scopes,60) AS scopes, status, last_used_at FROM api_token
--------------
ERROR 1054 (42S22) at line 1: Unknown column 'last_used_at' in 'SELECT'
api_token 实测可用列为 id/token_name/scopes/status/expires_at/created_at(三、生成时表头),3.2.4 没有记录 last_used_at。
4.1.4 cron / options / QSO 主表:还有三处列名坑(实录)
坑一:cron 表没有 name 列。 按任务名语义查询:
--------------
SELECT * FROM cron WHERE name IN ('master_run','access_token') OR name LIKE '%token%'
--------------
ERROR 1054 (42S22) at line 1: Unknown column 'name' in 'WHERE'
DESCRIBE cron 实测主键是字符串 id(varchar(191),任务名即主键),全结构见 C1 5.8。正确查法与 21 任务开关实测(SELECT id, enabled FROM cron):
clublog_download 0
clublog_upload 0
eqsl_sync 0
hrdlog_upload 0
lotw_lotw_upload 0
qrz_download 0
qrz_upload 0
sync_dcl 0
update_dxcc 1
update_lotw_users 1
update_pota_boundaries 1
update_update_clublog_scp 1
update_update_dok 1
update_update_hamqsl 1
update_update_hamsofnote 0
update_update_pota 1
update_update_sota 1
update_update_tle 0
update_update_wwff 1
update_version_check 0
vucc_grid_file 0
顺带验证了「cron 令牌存在哪」的错误猜想——令牌不落库(全盘搜索 options/user_options/api/api_token 均无命中,派生机制见 C1 5.9)。
坑二:没有 wavelog_options 表。 想当然加了库名前缀式表名:
--------------
SELECT * FROM wavelog_options
--------------
ERROR 1146 (42S02) at line 1: Table 'wavelog.wavelog_options' doesn't exist
真实表名就是 options(11 行全表见 C1 3.2)。
坑三:QSO 表主键不是 PRIMARY_KEY 也不是 id。 两次实测报错:
--------------
SELECT PRIMARY_KEY, COL_CALL, COL_QSO_DATE, COL_TIME_ON, COL_BAND, COL_MODE FROM TABLE_HRD_CONTACTS_V01 ORDER BY PRIMARY_KEY LIMIT 5
--------------
ERROR 1054 (42S22) at line 1: Unknown column 'PRIMARY_KEY' in 'SELECT'
--------------
SELECT id, COL_CALL, COL_BAND, COL_MODE, COL_QSO_DATE FROM TABLE_HRD_CONTACTS_V01 ORDER BY id
--------------
ERROR 1054 (42S22) at line 1: Unknown column 'id' in 'SELECT'
正确列名是 COL_PRIMARY_KEY,修正后实测:
COL_PRIMARY_KEY COL_CALL COL_QSO_DATE COL_TIME_ON COL_BAND COL_MODE
1 JA1ABC 2026-11-01 2026-11-01 10:30:00 15m CW
2 DL1ABC 2026-11-01 2026-11-01 09:15:00 20m SSB
3 JA1ABC 2026-11-01 2026-11-01 08:00:00 40m FM
QSO 表实测列名全清单(150+ 列,输出节选实测首尾):
COL_PRIMARY_KEY,COL_ADDRESS,COL_AGE,COL_A_INDEX,COL_ANT_AZ,COL_ANT_EL,COL_ANT_PATH,COL_ARRL_SECT,COL_BAND,COL_BAND_RX,COL_BIOGRAPHY,COL_CALL,COL_CHECK,COL_CLASS,COL_CNTY,...,COL_QSO_DATE,COL_QSO_DATE_OFF,...,COL_SUBMODE,COL_UKSMG,COL_USACA_COUNTIES,COL_VUCC_GRIDS,station_id,COL_WWFF_REF,COL_MY_WWFF_REF,COL_POTA_REF,COL_MY_POTA_REF,COL_MY_DARC_DOK,COL_QRZCOM_QSO_DOWNLOAD_DATE,COL_QRZCOM_QSO_DOWNLOAD_STATUS,COL_CLUBLOG_QSO_DOWNLOAD_DATE,COL_CLUBLOG_QSO_DOWNLOAD_STATUS,COL_DCL_QSLRDATE,COL_DCL_QSL_RCVD,COL_DCL_QSLSDATE,COL_DCL_QSL_SENT,creation_date,last_modified
💡 技术拓展:QSO 表列名 = ADIF 字段名加
COL_前缀(COL_CALL↔<CALL>、COL_QSO_DATE↔<QSO_DATE>),表名TABLE_HRD_CONTACTS_V01也是 ADIF v01 时代遗产(C1 5.6)。对着 ADIF 规范就能猜出列名,反之猜「通用命名」必踩坑。
纠错实录汇总(六条全部真实触发):
| 错误写法 | 实测报错 | 正确写法 |
|---|---|---|
users.username/email/callsign/user_level | 1054 Unknown column 'username' | user_name/user_email/user_callsign/user_type |
api_token.last_used_at | 1054 Unknown column 'last_used_at' | 3.2.4 无此列,用 expires_at/created_at |
cron.name | 1054 Unknown column 'name' | cron.id(varchar 主键) |
表 wavelog_options | 1146 Table doesn't exist | options |
TABLE_HRD_CONTACTS_V01.PRIMARY_KEY | 1054 Unknown column 'PRIMARY_KEY' | COL_PRIMARY_KEY |
TABLE_HRD_CONTACTS_V01.id | 1054 Unknown column 'id' | COL_PRIMARY_KEY |
⚠️ 注意:Wavelog 各表命名体系互不相同(users 带
user_前缀、QSO 表带COL_前缀、cron/options 用通用名)。写任何统计/排障 SQL 前先DESCRIBE,别继承 Cloudlog 时代或网上教程的旧列名。
4.1.5 config 与 user_options:两张容易和 options 混淆的表
config 表不是用户配置,是外部服务地址常量(实测全表 1 行,节选):
id lotw_download_url lotw_upload_url lotw_rcvd_mark lotw_login_url eqsl_download_url eqsl_rcvd_mark
1 https://lotw.arrl.org/lotwuser/lotwreport.adi https://lotw.arrl.org/lotwuser/upload Y https://lotw.arrl.org/lotwuser/default https://www.eqsl.cc/qslcard/DownloadInBox.cfm Y
user_options 是每用户的界面/字段显示偏好(实测节选):
option_name LEFT(option_value,40)
shown 1
utc_headermenu true
icon {"icon":"fas fa-dot-circle","color":"#ff
icon {"icon":"fas fa-check-circle","color":"#
gridsquare 0
icon {"icon":"fas fa-home","color":"#0000ff"}
oqrs_grouped_search_show_station_name on
confirmed false
三张「配置」表分工:config.php(文件层,C1 五)→ options(全局动态层)→ user_options(每用户偏好层);config 表则基本不动。
4.2 mariadb-dump 备份与恢复演练
4.2.1 全量逻辑备份
在数据库机上执行(业务账号即可,只读导出):
mariadb-dump --single-transaction -h wavelog-324-db -uwavelog -pq1w2e3r4 wavelog > backup-verify.sql; echo "exit=$?"
实测回显:
exit=0
-rw-r--r-- 1 root root 174102551 Nov 1 18:25 backup-verify.sql
备份文件头部(输出节选,实测首尾):
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.20-11.8.9-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: wavelog
-- ------------------------------------------------------
-- Server version 11.8.9-MariaDB-ubu2404
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
解读:日志库只有 3 条 QSO,dump 却有 174MB——体积大头是参考数据表(pota_boundaries 的 GeoJSON、sota_directory/wwff_directory/dxcc_*/timezones 等,见 4.1.1 表清单)。这决定了 Wavelog 备份的两个实操结论:备份文件按百 MB 级预留空间与传输窗口;恢复演练必须做,因为「小业务数据 + 大参考数据」的库最容易在迁移时只拷了前者。
💡 技术拓展:MariaDB 10.20+ 起官方工具改名
mariadb-dump(dump 头MariaDB dump 10.20-11.8.9即该工具版本),mysqldump为兼容别名,两者在本实例等价。备份方式选型:
方式 工具 特点 适用 逻辑备份(本文) mariadb-dump --single-transaction一致性快照不锁 InnoDB、可跨版本、恢复慢 个人/俱乐部库,日备 物理备份 mariadb-backup快、需同版本环境 大库增量 文件级 直接拷数据目录 必须停库否则损坏 不建议
4.2.2 恢复演练第一回合:业务账号建库被拒(ERROR 1044 实录)
把 dump 导入新库 wavelog_restore 做还原验证,用业务账号执行:
--------------
CREATE DATABASE IF NOT EXISTS wavelog_restore CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
--------------
ERROR 1044 (42000) at line 1: Access denied for user 'wavelog'@'%' to database 'wavelog_restore'
ERROR 1044 (42000): Access denied for user 'wavelog'@'%' to database 'wavelog_restore'
restore exit=1
后续核对语句连锁失败(表根本没建起来):
src_tables 54
restore_tables 0
src_qso 3
--------------
SELECT 'restore_qso', COUNT(*) FROM wavelog_restore.TABLE_HRD_CONTACTS_V01
--------------
ERROR 1142 (42000) at line 1: SELECT command denied to user 'wavelog'@'localhost' for table `wavelog_restore`.`TABLE_HRD_CONTACTS_V01`
连清理也被拒:
--------------
DROP DATABASE wavelog_restore
--------------
ERROR 1044 (42000) at line 1: Access denied for user 'wavelog'@'%' to database 'wavelog_restore'
drop exit=1
定位:wavelog 业务账号是镜像初始化时按「仅 wavelog 库」授权创建的(部署篇),对新库无 CREATE/DROP/SELECT 权限——这是最小权限的正常表现,不是缺陷。
4.2.3 恢复演练第二回合:root 重跑成功 + 行数对照
改用数据库 root 账号(初始随机口令见部署篇一、)重跑建库与导入:
create exit=0
restore exit=0
核对脚本实测(源库 vs 恢复库):
src_tables 54
restore_tables 54
src_qso 3
restore_qso 3
src_options 11
restore_options 11
恢复库内抽查 QSO 明细时又踩一次 4.1.4 的 id 列坑(实录见彼处),换 COL_PRIMARY_KEY 后与 4.1.4 回显一致。随后给 wavelog 账号 GRANT 恢复库可见权限,用业务账号复核表数量:
54
演练收尾,root 删除恢复库与授权:
cleanup exit=0
恢复验证四步清单(本文实测顺序):表数量对照 → 关键表行数对照(业务表 + 配置表各选一个)→ 明细抽查(记得用对列名)→ 以生产账号复核权限可见性。
4.3 migrate:升级后的数据库迁移
Wavelog 的 schema 迁移是一个 HTTP 端点,命令行用 curl 触发:
curl -s -w '\nHTTP_CODE=%{http_code}\n' http://127.0.0.1:8137/index.php/migrate
实测回显:
{"status":"success","version":"307"}
HTTP_CODE=200
解读:version=307 与 migrations 表版本戳一致(安装向导阶段同端点回显见部署篇 4.5)。该操作幂等——schema 已在目标版本时重放只返回 success、不改动数据,实测安装后重跑即此结果。运维时机:换新版镜像(如 3.2.4 → 后续版本)拉起后跑一次本命令,即完成「代码随镜像、结构随 migrate」的升级闭环。
⚠️ 注意:迁移前先做 4.2 备份;虽属可重放操作,生产仍建议在反代层限制
/index.php/migrate的外部访问,只允许本机或运维网段触发。
4.4 cron/run:后台任务触发与 429 限流
4.4.1 调度入口是 cron.d,不是用户 crontab(实录)
按直觉先查 wavelog 用户的 crontab:
crontab -l -u wavelog
no crontab for wavelog
真正的调度片段在系统级 cron.d(实测 cat /etc/cron.d/wavelog):
* * * * * wavelog /usr/bin/curl --silent http://localhost/index.php/cron/run >/dev/null 2>&1
每分钟以 wavelog 身份 curl 一次 cron/run,「哪个任务到期该跑」由应用内 cron 表(4.1.4 的 21 任务)决定——系统 cron 只管敲门,不管敲哪间房。Apache 访问日志实测尾部(每分钟一条 200):
127.0.0.1 - - [01/Nov/2026:10:20:01 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
127.0.0.1 - - [01/Nov/2026:10:21:01 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
127.0.0.1 - - [01/Nov/2026:10:22:02 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
127.0.0.1 - - [01/Nov/2026:10:23:01 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
127.0.0.1 - - [01/Nov/2026:10:24:01 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
127.0.0.1 - - [01/Nov/2026:10:25:01 +0000] "GET /index.php/cron/run HTTP/1.1" 200 1766 "-" "curl/8.14.1"
排查「同步任务没跑」的顺序:cron.d 在不在 → 访问日志有没有每分钟的 200 → 应用日志与 cron 表 last_run/next_run。
4.4.2 手动触发:21 任务 due/skipped 全回显
运维排障时常需要立刻催一次任务,带 cron 令牌手动触发(令牌派生机制见 C1 5.9——由 encryption_key HMAC-SHA256 派生、不落库):
curl -s -w '\nHTTP_CODE=%{http_code}\n' \
-H 'X-Wavelog-Auth: 3104a9e2d58faefc23760c9f7484786bb32b0933371f01ac729da4c27b3248a6' \
http://127.0.0.1:8137/index.php/cron/run
实测回显(21 任务全清单):
CRON: clublog_download is disabled. skipped..
CRON: clublog_upload is disabled. skipped..
CRON: eqsl_sync is disabled. skipped..
CRON: hrdlog_upload is disabled. skipped..
CRON: lotw_lotw_upload is disabled. skipped..
CRON: qrz_download is disabled. skipped..
CRON: qrz_upload is disabled. skipped..
CRON: sync_dcl is disabled. skipped..
CRON: update_dxcc -> is due: false -> Next Run: 2027-01-01 00:20:00
CRON: update_lotw_users -> is due: false -> Next Run: 2026-11-02 01:10:00
CRON: update_pota_boundaries -> is due: false -> Next Run: 2026-12-01 00:30:00
CRON: update_update_clublog_scp -> is due: false -> Next Run: 2026-11-08 00:00:00
CRON: update_update_dok -> is due: false -> Next Run: 2026-12-01 00:00:00
CRON: update_update_hamqsl -> is due: false -> Next Run: 2026-11-01 10:00:00
CRON: update_update_hamsofnote is disabled. skipped..
CRON: update_update_pota -> is due: false -> Next Run: 2026-12-01 00:15:00
CRON: update_update_sota -> is due: false -> Next Run: 2026-12-01 00:05:00
CRON: update_update_tle is disabled. skipped..
CRON: update_update_wwff -> is due: false -> Next Run: 2026-12-01 00:10:00
CRON: update_version_check is disabled. skipped..
CRON: vucc_grid_file is disabled. skipped..
HTTP_CODE=200
解读:输出只有两种形态——disabled. skipped..(cron 表 enabled=0,与 4.1.4 查询的 9 个 0 值任务一一对应)和 is due: false -> Next Run: <时间>(启用但未到 expression 周期)。Next Run 由表达式推算,例如 update_dxcc 表达式 20 0 1 */2 *(每 2 个月的 1 日 00:20,C1 5.8)对应下次执行 2027-01-01 00:20:00。
4.4.3 429 限流:与令牌无关的 30 秒锁(对照实验实录)
紧接着再触发一次(不带任何头):
no-token HTTP_CODE=429
响应体实测:
CRON: rate limit active, skipping. Try again in a few seconds.
HTTP_CODE=429
第一反应是「429 因为没带令牌」——对照实验证伪了这个猜想。同一 30 秒窗口内:
| 场景 | 实测结果 |
|---|---|
| 窗口外、无头触发 | no-header HTTP_CODE=200(4.4.2 即此情形,全量执行) |
| 窗口内、带正确令牌 | with-token HTTP_CODE=429 |
| 窗口内、带错误令牌 | bad-token HTTP_CODE=429 |
窗口内、X-Wavelog-Auth 空值 | X-Wavelog-Auth empty HTTP_CODE=429 |
结论:cron/run 主入口不校验令牌,只受 30 秒缓存限流锁约束(源码实测与机制解读见 C1 5.9:锁键 cron_run_rate_limit 跑在 cache_adapter 上)。手动触发返回 429 不是故障,等窗口过期即可;每分钟 cron.d 的节奏恰好贴着安全线,这也是官方不建议把系统 cron 加密到亚分钟的原因。
4.4.4 子端点验令牌:X-Wavelog-Auth 的真实用场
令牌防的是直接打各同步子端点。实测 cronauth_allowed 在源码中的调用点:
/var/www/html/application/controllers/Hrdlog.php:26: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Clublog.php:31: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Lotw.php:178: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Eqsl.php:628: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Qrz.php:74: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Qrz.php:398: if (!cronauth_allowed(3)) {
/var/www/html/application/controllers/Update.php:20: if (!cronauth_allowed(99)) {
即业务同步类端点要求 3 级、数据更新类(Update)要求 99 级,凭 X-Wavelog-Auth 头或管理员会话放行。安装向导最后一步就是带头手动拉了一次 DXCC 数据(部署篇 4.5):
curl -s -H "X-Wavelog-Auth: 3104a9e2d58faefc23760c9f7484786bb32b0933371f01ac729da4c27b3248a6" \
http://127.0.0.1:8137/index.php/update/dxcc
⚠️ 注意:令牌 = HMAC(
encryption_key),改 key 等于换令牌——如果外部监控系统(非本实例 cron.d)直接调子端点,换 key 后必须同步更新其配置(C1 7.2)。
4.5 API v1:key 认证与 12 例接口实测
v1 端点族挂 /index.php/api/ 下,key 有两种携带形态:POST 时放 JSON body 的 "key" 字段,GET 类放 URI 路径段(/api/statistics/<key>)。以下 12 例按调用顺序照抄实测回显(时间戳 2026-11-01 09:44–09:45 UTC)。
4.5.1 version:正反例
curl -s -w '\nHTTP_CODE=%{http_code}\n' -X POST http://127.0.0.1:8137/index.php/api/version \
-H 'Content-Type: application/json' -d '{"key":"wl1b5b5e4f61ee5"}'
--- v1 version (ok) ---
{"status":"ok","version":"3.2.4"}
HTTP_CODE=200
--- v1 version (bad) ---
{"status":"failed","reason":"missing or invalid api key"}
HTTP_CODE=401
4.5.2 statistics 与 station_info(key 在 URI)
--- v1 statistics (key in URI) ---
{"Today":0,"total_qsos":0,"month_qsos":0,"year_qsos":0}
HTTP_CODE=201
--- v1 station_info (key in URI) ---
[{"station_id":"1","station_profile_name":"Beijing","station_gridsquare":"OM89KK","station_callsign":"BH1ABC","station_active":"1","station_uuid":"49780306-d5d8-11f1-a360-569a53790263","station_city":"Beijing","station_dxcc":"27","station_cq":"1","station_itu":"1","station_country":"BELARUS", "...":"..."}]
HTTP_CODE=200
解读:此刻库内还没有 QSO,statistics 全 0;station_info 返回安装向导创建的唯一台站(空字段已折叠,输出节选)。注意 statistics 返回 201 而非 200——v1 的部分 GET 端点也回 201,写监控脚本按 2xx 判定,别硬编码 200。
4.5.3 create_station:JSON 数组建台站
请求体(station.json 原文):
[
{
"station_profile_name": "Beijing Home QTH",
"station_callsign": "BH1ABC",
"station_gridsquare": "OM89KK",
"station_city": "Beijing",
"station_dxcc": "27",
"station_cnty": "China",
"station_cq": "24",
"station_itu": "44"
}
]
--- v1 create_station ---
{"status":"success","message":"1 locations imported."}
HTTP_CODE=201
--- v1 station_info after create ---
[{"station_id":"1","station_profile_name":"Beijing","station_active":"1","...":"..."},{"station_id":"2","station_profile_name":"Beijing Home QTH","station_gridsquare":"OM89KK","station_callsign":"BH1ABC","station_active":"0","station_city":"Beijing","station_dxcc":"27","station_cnty":"China","station_cq":"24","station_itu":"44","...":"..."}]
HTTP_CODE=200
解读:新台站 station_id=2 入库但 station_active=0——API 建的台站默认不激活,这个细节在 4.5.5 会引出统计口径的坑。
4.5.4 qso:ADIF 导入(400 实录 → 修正 → 201 → 重复 400)
首试构造了一段缺日期字段的 ADIF 串(<CALL:6>DL1ABC<EOM><BAND:3>20m<EOM><MODE:3>SSB<EOM>...,无 <QSO_DATE>):
--- v1 qso (first) ---
{"status":"abort","type":"adif","string":"","adif_count":1,"adif_errors":1,"messages":["","You tried to import a QSO without valid date. This QSO wasn't imported. It's invalid. Call: DL1ABC, Mode: Band: <br>"]}
HTTP_CODE=400
服务端应用日志同步留痕(application/logs/log-2026-11-01.php 实测内容):
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
ERROR - 2026-11-01 09:44:18 --> Trying to import QSO with invalid date: for station_id 2. Call: DL1ABC Mode: Band:
ERROR - 2026-11-01 09:44:18 --> Trying to import QSO with invalid date: for station_id 2. Call: DL1ABC Mode: Band:
补齐日期后重跑(完整 ADIF 串:<CALL:6>DL1ABC<EOM><BAND:3>20m<EOM><MODE:3>SSB<EOM><FREQ:7>14.2360<EOM><QSO_DATE:8>20261101<EOM><TIME_ON:6>091500<EOM><RST_SENT:3>599<EOM><RST_RCVD:3>599<EOM><COMMENT:9>api test<EOM>,POST 到 /index.php/api/qso,station_profile_id=2):
--- v1 qso first import ---
{"status":"created","type":"adif","string":"","adif_count":1,"adif_errors":0,"messages":[""]}
HTTP_CODE=201
--- v1 qso duplicate import ---
{"status":"abort","type":"adif","string":"","adif_count":1,"adif_errors":1,"messages":["","Date\/Time: 2026-11-01 09:15:00 Callsign: DL1ABC Band: 20m Duplicate for BH1ABC<br>"]}
HTTP_CODE=400
错误 key 的对照:
--- v1 qso (badkey) ---
{"status":"failed","reason":"missing or wrong api key"}
HTTP_CODE=401
解读:qso 端点三连——格式错(缺必填字段)400 + adif_errors 计数、重复导入 400 + Duplicate 明细、key 错 401。去重键是「时间+呼号+波段」组合;400 与 401 的响应体结构完全不同(status:abort/messages vs status:failed/reason),脚本按此分流。
💡 技术拓展:ADIF 标签语法
<字段名:长度>值,如<CALL:6>DL1ABC;记录头以<EOH>结束、记录体以<EOR>结束。长度前缀必须准确(<QSO_DATE:8>20261101),长度错了后续字段全部错位——这是手工拼 ADIF 最常见的翻车点,能用库生成就别手拼。
4.5.5 export、check、radio 与统计口径实验
导出(get_contacts_adif,fetchfromid 增量语义):
--- v1 get_contacts_adif (export) ---
{"status":"successful","message":"No new QSOs available.","lastfetchedid":0,"exported_qsos":0,"adif":null}
HTTP_CODE=200
导入两条后重跑(输出节选,ADIF 正文只留头尾):
{"status":"successful","message":"Export successful","lastfetchedid":"2","exported_qsos":2,"adif":"Wavelog ADIF export\n<ADIF_VER:5>3.1.8\n<PROGRAMID:7>Wavelog\r\n<PROGRAMVERSION:5>3.2.4\r\n<EOH>\n\n<BAND:3>15m\r\n<CALL:6>JA1ABC\r\n...<EOR>\r\n\r\n<BAND:3>20m\r\n<CALL:6>DL1ABC\r\n...<EOR>\r\n\r\n"}
HTTP_CODE=200
呼号查重(logbook_check_callsign,按公共日志簿 slug 查询;slug 由 station_logbooks.public_slug 提供,实测 UPDATE station_logbooks SET public_slug='beijing-demo', public_search=1 WHERE logbook_id=1; 等价 Web UI 的 Logbooks 编辑):
--- v1 logbook_check_callsign (check) ---
{"callsign":"DL1ABC","result":"Not Found"}
HTTP_CODE=201
(导入 JA1ABC 后查 JA1ABC:{"callsign":"JA1ABC","result":"Found"};DL1ABC 因导入落在 station 2 仍 Not Found——查询作用域是公共日志簿关联的 station 1。)
电台上报(radio):
--- v1 radio (radio) ---
{"status":"success"}
HTTP_CODE=200
统计口径实验:导入 station 2 两条后 statistics 纹丝不动:
--- v1 statistics after import ---
{"Today":0,"total_qsos":0,"month_qsos":0,"year_qsos":0}
HTTP_CODE=201
查关联表实测:
logbook_relation_id station_logbook_id station_location_id modified creation_date last_modified
1 1 1 2026-11-01 09:40:33 2026-11-01 09:40:34 2026-11-01 09:40:34
station_id station_active
1 1
2 0
把 QSO 导入 station 1(active)后:
--- import to station 1 (active) ---
{"status":"created","type":"adif","string":"","adif_count":1,"adif_errors":0,"messages":[""]}
HTTP_CODE=201
--- statistics ---
{"Today":1,"total_qsos":1,"month_qsos":1,"year_qsos":1}
HTTP_CODE=201
结论:v1 statistics 只统计活动台站/活动日志簿(users.active_station_logbook=1,4.1.2)口径;create_station 建的新台站默认 inactive,往它导数据会出现「导入成功但统计为 0」的假丢失。数据在库里(4.1.4 的 QSO 抽查 3 行可证),切回活动台站即可见。
v1 十二例小结:
| 端点 | 方法 | 实测结果 | HTTP |
|---|---|---|---|
/api/version | POST body key | ok / bad key 401 | 200 / 401 |
/api/statistics/<key> | GET URI key | 全 0 → 口径实验见 4.5.5 | 201 |
/api/station_info/<key> | GET URI key | 1 台站 → 2 台站 | 200 |
/api/create_station/<key> | POST JSON 数组 | 1 locations imported. | 201 |
/api/qso | POST ADIF 串 | 缺日期 400 / 成功 201 / 重复 400 / 坏 key 401 | 400/201/401 |
/api/get_contacts_adif | POST | 空导出 → 导出 2 条 | 200 |
/api/logbook_check_callsign | POST | Not Found / Found | 201 |
/api/radio | POST | {"status":"success"} | 200 |
4.6 API v2:Bearer token、scopes 与 CRUD 实测
v2 端点族挂 /index.php/api/v2/,统一 Authorization: Bearer <token>,响应是 {"data":…,"meta":…} 信封。以下 10 例 + 4 例修正实测于 09:44–09:45 UTC。
4.6.1 服务状态与未认证探测
--- v2 root (no auth) ---
{"data":{"name":"Wavelog API","status":"ok"},"meta":{"timestamp":"2026-11-01T09:44:18+00:00","resource":"status","method":"GET"}}
HTTP_CODE=200
--- v2 token/whoami no-auth ---
{"error":{"code":"unauthorized","message":"Missing API token"}}
HTTP_CODE=401
解读:/api/v2/ 状态端点免认证,可作监控探活;业务端点缺 token 直接 401 unauthorized。带 token 查自身:
curl -s -H "Authorization: Bearer wl2_03892f48d7e67050abbb5edcf64d7d9e624a0cf0" \
http://127.0.0.1:8137/index.php/api/v2/token
--- v2 GET token ---
{"data":{"id":2,"name":"ops-token","owner":"BH1ABC","user_id":1,"scopes":["qso:read","qso:write","station:read","statistic:read","lookup:read"],"expires_at":"2027-11-01 09:41:42"},"meta":{"timestamp":"2026-11-01T09:44:18+00:00","resource":"token","method":"GET"}}
HTTP_CODE=200
(token 行 id 与三、生成时不同,系演练中重建;scopes 与有效期与生成时一致。)
4.6.2 station 与 qso 读取
--- v2 GET station ---
{"data":[{"id":1,"uuid":"49780306-d5d8-11f1-a360-569a53790263","name":"Beijing","callsign":"BH1ABC","gridsquare":"OM89KK","city":"Beijing","dxcc":27,"cq":1,"itu":1,"active":true,"clublog_upload":"on","...":"..."},{"id":2,"uuid":"cf0c1033-d5d8-11f1-a360-569a53790263","name":"Beijing Home QTH","callsign":"BH1ABC","gridsquare":"OM89KK","city":"Beijing","dxcc":27,"cq":24,"itu":44,"cnty":"China","active":false,"clublog_upload":"disabled","...":"..."}],"meta":{"timestamp":"2026-11-01T09:44:18+00:00","resource":"station","method":"GET"}}
HTTP_CODE=200
--- v2 GET qso?per_page=3 ---
{"data":[],"meta":{"timestamp":"2026-11-01T09:44:19+00:00","resource":"qso","method":"GET","page":1,"per_page":3,"count":0,"total":0,"total_pages":0,"has_more":false}}
HTTP_CODE=200
解读:v2 的 station 视图直接给出 active 布尔与 uuid;qso 列表带分页 meta(page/per_page/count/total/has_more)。此刻 v1 已导入的 2 条 QSO 在 v2 列表仍为空——与 4.5.5 同一口径(活动日志簿)。
4.6.3 写入:结构化 JSON 建 QSO
--- v2 POST qso ---
{"data":{"id":1,"station_id":2,"qso_date":"2026-11-01 10:30:00","mode":"CW","freq":"21020000","call":"JA1ABC","band":"15m","rst_sent":"599","rst_rcvd":"579","cqz":25,"...":"..."},"meta":{"timestamp":"2026-11-01T09:44:19+00:00","resource":"qso","method":"POST"}}
HTTP_CODE=201
请求体是普通 JSON({"station_profile_id":2,"call":"JA1ABC","band":"15m","mode":"CW","qso_date":"20261101","time_on":"103000","rst_sent":"599","rst_rcvd":"579"}),不用拼 ADIF 长度前缀——v2 写 QSO 比 v1 省心之处。随后统计与查询:
--- v2 GET statistic ---
{"data":{"qso":{"total":1,"activity":{"today":1,"month":1,"year":1},"breakdown":{"by_band":[{"band":"15m","count":1}],"by_mode":[{"mode":"CW","count":1}]},"dxcc":{"worked":1,"confirmed":0,"available":340,"deleted":{"worked":0,"confirmed":0,"confirmed_paper":0,"confirmed_lotw":0}}}},"meta":{"timestamp":"2026-11-01T09:44:19+00:00","resource":"statistic","method":"GET","profile":"qso","admin":true}}
HTTP_CODE=200
--- v2 GET lookup?callsign=DL1ABC ---
{"data":{"callsign":"DL1ABC","dxcc":"FEDERAL REPUBLIC OF GERMANY","dxcc_id":"230","dxcc_cqz":"14","cont":"EU","workedBefore":false,"lotw_member":false,"...":"..."},"meta":{"timestamp":"2026-11-01T09:44:19+00:00","resource":"lookup","method":"GET","detail":"basic"}}
HTTP_CODE=200
--- v2 GET qso ---
{"data":[{"id":1,"station_id":2,"qso_date":"2026-11-01 10:30:00","mode":"CW","call":"JA1ABC","band":"15m","rst_sent":"599","rst_rcvd":"579","cqz":25,"...":"..."}],"meta":{"timestamp":"2026-11-01T09:44:19+00:00","resource":"qso","method":"GET","page":1,"per_page":50,"count":1,"total":1,"total_pages":1,"has_more":false}}
HTTP_CODE=200
(超长字段以 "...":"..." 折叠,输出节选;statistic 的 total:1 同样只算活动口径。)
4.6.4 scope 边界:radio 403、PATCH 200、DELETE 403
POST radio 被拒(ops-token 没申请 radio scope):
--- v2 POST radio ---
{"error":{"code":"insufficient_scope","message":"Token is missing the required scope: radio:write","details":{"required_scope":"radio:write"}}}
HTTP_CODE=403
修改与删除(09:45:21 UTC 实测):
--- v2 PATCH qso/1 ---
{"data":{"id":1,"station_id":2,"qso_date":"2026-11-01 10:30:00","mode":"CW","call":"JA1ABC","band":"15m","comment":"updated via v2 API","...":"..."},"meta":{"timestamp":"2026-11-01T09:45:21+00:00","resource":"qso","method":"PATCH"}}
HTTP_CODE=200
--- v2 DELETE qso/1 (expect insufficient_scope) ---
{"error":{"code":"insufficient_scope","message":"Token is missing the required scope: qso:delete","details":{"required_scope":"qso:delete"}}}
HTTP_CODE=403
--- v2 GET qso/1 ---
{"data":{"id":1,"station_id":2,"qso_date":"2026-11-01 10:30:00","mode":"CW","call":"JA1ABC","band":"15m","comment":"updated via v2 API","...":"..."},"meta":{"timestamp":"2026-11-01T09:45:21+00:00","resource":"qso","method":"GET"}}
HTTP_CODE=200
--- v2 bad token ---
{"error":{"code":"invalid_token","message":"Invalid or revoked API token"}}
HTTP_CODE=401
解读:v2 的权限模型在实测里完整现形——qso:write 覆盖 PATCH 但不覆盖 DELETE(删除要 qso:delete);错误分类清晰:缺 scope 是 403 insufficient_scope(带 details.required_scope,脚本可自动提示补哪个 scope),token 被吊销/无效是 401 invalid_token,没带是 401 unauthorized。PATCH 后立即 GET 回读,comment 已更新,写读一致。
4.7 PHP 运行时与日志自检
4.7.1 php -m 与 php -i
php -m | grep -E 'apcu|curl|gd|mbstring|mysqli|openssl|session'
实测(关键模块全部就位):
apcu
curl
gd
json
mbstring
mysqli
openssl
session
php -i | grep -E 'max_execution_time|memory_limit|post_max_size|upload_max_filesize'
实测生效值:
max_execution_time => 0 => 0
memory_limit => 256M => 256M
post_max_size => 64M => 64M
upload_max_filesize => 64M => 64M
解读:max_execution_time=0 保证大 ADIF 导入不被 PHP 时限掐断;memory_limit=256M 与上传 64M 是镜像默认,调法见 C1 3.4/六。mysqli/apcu 缺失会分别导致数据库连不上与缓存层报错(cron 限流锁依赖缓存,C1 5.4)。
4.7.2 应用日志与 Apache 日志
ls -l /var/www/html/application/logs/
实测(运行一天后):
total 24
drwxrwxr-x 1 wavelog www-data 4096 Nov 1 09:44 .
-rw-rw-r-- 1 wavelog www-data 114 Oct 21 19:05 index.html
-rw-r--r-- 1 www-data www-data 1323 Nov 1 09:40 installer_debug.log
-rw-r--r-- 1 www-data www-data 310 Nov 1 09:44 log-2026-11-01.php
log-2026-11-01.php 内容见 4.5.4(两条 ERROR 与 API 400 一一对应);installer_debug.log 是向导留档。Apache 侧实测:
lrwxrwxrwx 1 www-data www-data 11 Oct 21 15:47 /var/log/apache2/access.log -> /dev/stdout
解读:access log 符号链接到 /dev/stdout,容器化形态下日志从标准输出走(docker logs 即入口,部署篇五);排障三层各司其职——接口错误看响应体、应用错误看 application/logs/、请求流水看 access 日志。配置文件规模自检(wc -l,呼应 C1 一、):
948 /var/www/html/application/config/docker/config.php
98 /var/www/html/application/config/docker/database.php
1046 total
五、注意事项
- ⚠️ 先 DESCRIBE 再写 SQL:本文 4.1 实测踩中 6 处列名/表名坑(username、name、PRIMARY_KEY、id、last_used_at、wavelog_options),网上旧教程的 Cloudlog 式列名在 Wavelog 3.2.4 会直接报错。
- ⚠️ 业务账号没有 DDL:
wavelog账号仅对wavelog库有权限,建库/恢复/授权必须数据库 root(ERROR 1044 实录见 4.2.2);恢复库验证完及时 DROP 并回收 GRANT。 - ⚠️ 备份体积与业务数据量不成正比:3 条 QSO 的库 dump 出 174MB(参考数据表占绝对大头,4.2.1),容量规划与恢复演练都按这个量级来。
- ⚠️ 429 是限流不是鉴权:
cron/run30 秒锁窗口内带对令牌也 429(4.4.3 对照实验);别为此重启服务,等窗口过期即可。外部系统直接调同步子端点才需要X-Wavelog-Auth令牌。 - ⚠️ v1 key 走 URI 形态会进访问日志:
/api/statistics/<key>的 key 明文出现在 access log(4.4.1 日志同款位置);生产优先 JSON body 形态并上 HTTPS。 - ⚠️ v2 token 按最小 scope 签发并设过期:ops-token 实测无
qso:delete/radio:write,误删/误控被 403 兜住(4.6.4);吊销即时生效(401invalid_token)。 - ⚠️ 统计口径绑定活动日志簿:往 inactive 台站导数据会出现「导入 201 但 statistics 为 0」的假丢失(4.5.5),排障先查
station_active与station_logbooks_relationship。 - ⚠️ 改
encryption_key的连带影响:cron 令牌随 key 变化(不落库、HMAC 派生,C1 5.9),外部 crontab/监控若直连子端点需同步换令牌。
六、常见问题
Q1:手动触发 cron/run 返回 429,任务是不是挂了?
不是。响应体 CRON: rate limit active, skipping. 是 30 秒限流锁(4.4.3 对照实验:窗口内带正确令牌同样 429,窗口外无头 200)。等 30 秒再触发;cron.d 每分钟一敲已贴安全线。
Q2:crontab -l -u wavelog 查不到任务?
Wavelog 用系统级片段 /etc/cron.d/wavelog(4.4.1 实测 no crontab for wavelog + cron.d 行原文),不在任何用户 crontab 里。
Q3:恢复备份时报 ERROR 1044?
业务账号无建库权限(4.2.2 实录)。用数据库 root 建库导入,或先 GRANT 再导;恢复验证按 4.2.3 四步清单走。
Q4:SQL 查询各种 Unknown column / Table doesn't exist?
对照 4.1 纠错汇总表:users 列带 user_ 前缀、cron 主键是 id、QSO 主键是 COL_PRIMARY_KEY、配置表叫 options。统一办法:SHOW TABLES; + DESCRIBE <表>; 先行。
Q5:v1 导入 QSO 返回 400 You tried to import a QSO without valid date?
ADIF 串缺 <QSO_DATE>/<TIME_ON>(4.5.4 实录)。补齐后 201 created;服务端 application/logs/ 有同时间戳 ERROR 留痕可交叉核对。
Q6:v2 返回 403 insufficient_scope?
token 缺对应 scope,响应体 details.required_scope 直接点名要哪个(radio:write、qso:delete 实测两例,4.6.4)。到 Web UI 重建 token 时勾上所需 scope。
Q7:v1/v2 的 401 怎么区分原因?
v1:missing or invalid api key(version 探测)/missing or wrong api key(qso 写入)——key 错或已删。v2:unauthorized(没带 Bearer)vs invalid_token(带了但被吊销/无效,4.6.4 实测)。
Q8:导入明明返回 201,statistics 却是 0?
导到了 inactive 台站。4.5.5 实验:station 2 两条 → 统计 0;station 1 一条 → 统计 1。数据没丢(4.1.4 QSO 抽查 3 行),切活动台站或改 station_profile_id。
Q9:v1 导出返回 No new QSOs available.?
fetchfromid 增量语义:该 id 之后无新 QSO 时即此响应(4.5.5 首测)。全量导出把 fetchfromid 置 0。
Q10:升级新版镜像后要做点什么?
curl http://127.0.0.1:8137/index.php/migrate 跑一次 schema 迁移(4.3,幂等、回显目标版本号);迁移前备份(4.2),迁移后核对 migrations 版本戳与界面正常。
七、参考链接
- Wavelog API v1 官方文档:https://docs.wavelog.org/developer/api/
- Wavelog API v2 官方文档:https://docs.wavelog.org/developer/api-v2/
- Wavelog 官方文档总入口:https://docs.wavelog.org
- GitHub 仓库:https://github.com/wavelog/wavelog
- ADIF 规范:https://adif.org
- 站内相关:《Wavelog 介绍与选型指南》、《Wavelog 3.2.4 Docker Compose 部署指南》、《Wavelog 3.2.4 配置参数详解》
- 母项目对照:《Cloudlog 2.8.21 常用命令与运维排障详解》