From 722b719688d426cd466946d68be633a5bdc0d35b Mon Sep 17 00:00:00 2001 From: "Logan.Li" Date: Mon, 14 Sep 2026 23:19:46 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=8E=A5=E5=85=A5=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=A1=B5=E2=80=94=E2=80=94=E7=AD=BE=E5=90=8D=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E6=B7=B1=E5=BA=95=E9=85=8D=E8=89=B2=E4=BF=AE=E5=A4=8D=EF=BC=9B?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=A8=A1=E5=9E=8B=E6=94=B9CSS=E6=97=B6?= =?UTF-8?q?=E5=BA=8F=E5=9B=BE=EF=BC=88=E4=B8=8D=E5=86=8D=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=EF=BC=89=EF=BC=9B=E8=A1=A5=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E8=A1=A8/curl=E8=87=AA=E6=B5=8B/=E4=BF=A1?= =?UTF-8?q?=E5=B0=81=E6=A0=BC=E5=BC=8F=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/Integration.vue | 97 +++++++++++++---------- frontend/src/views/integration/docData.js | 47 +++++++++++ 2 files changed, 104 insertions(+), 40 deletions(-) create mode 100644 frontend/src/views/integration/docData.js diff --git a/frontend/src/views/Integration.vue b/frontend/src/views/Integration.vue index 1288fb9..b62bbdf 100644 --- a/frontend/src/views/Integration.vue +++ b/frontend/src/views/Integration.vue @@ -13,7 +13,27 @@ title="接入三步:① 拿到产品序列号(后台或购买页) ② 客户端调 activate 激活并保存 activation_token ③ 每周调 verify 周检(验签 sig + 时钟校验)" />

接入模型

-
{{ flowText }}
+
+
+
+ 客户端 + 桌面 / CLI / Web 前端 +
+
+ ① activate(序列号,设备ID)⟶ + ⟵ activation_token + sig +
+
+ 授权服务器(双节点) + licence.rlwj.cn + l123.91ai.io +
+
+
+
+ {{ st.title }}{{ st.detail }} +
+
+

客户端 API(无需认证,按 IP 限流)

@@ -23,6 +43,18 @@ +

请求体参数

+ + + + + +

快速自测

+
+ {{ copied === 'curl' ? '已复制' : '复制' }} +
+
+

验证/激活响应关键字段

@@ -120,8 +152,10 @@ import hljsJava from 'highlight.js/lib/languages/java' import hljsPython from 'highlight.js/lib/languages/python' import hljsXml from 'highlight.js/lib/languages/xml' import hljsJavascript from 'highlight.js/lib/languages/javascript' +import hljsBash from 'highlight.js/lib/languages/bash' import 'highlight.js/styles/github-dark.css' -import { clientSnippets, cliSnippets, backendSnippets, API_ENDPOINTS, CANONICAL_SPEC } from './integration/snippets/index.js' +import { clientSnippets, cliSnippets, backendSnippets, CANONICAL_SPEC } from './integration/snippets/index.js' +import { apis as API_ROWS, params as PARAM_ROWS, fields as FIELD_ROWS, flowSteps as FLOW_STEPS, CURL_EXAMPLE, API_ENDPOINTS as EP } from './integration/docData.js' import request from '@/api/request' hljs.registerLanguage('go', hljsGo) @@ -130,6 +164,13 @@ hljs.registerLanguage('java', hljsJava) hljs.registerLanguage('python', hljsPython) hljs.registerLanguage('xml', hljsXml) hljs.registerLanguage('javascript', hljsJavascript) +hljs.registerLanguage('bash', hljsBash) + +const apis = API_ROWS +const params = PARAM_ROWS +const fields = FIELD_ROWS +const flowSteps = FLOW_STEPS +const API_ENDPOINTS = EP const tab = ref('overview') const cliKind = ref('go') @@ -166,38 +207,7 @@ const copy = async (text, key) => { setTimeout(() => { copied.value = '' }, 1200) } -const flowText = `┌──────────┐ ① activate(序列号, 设备ID) ┌──────────────────┐ -│ 客户端 │ ────────────────────────────▶ │ 授权服务器(双节点) │ -│ 桌面/CLI │ ◀──────────────────────────── │ 国内 + 香港容灾 │ -│ Web前端 │ activation_token + sig └──────────────────┘ -└────┬─────┘ - │ ② 每周 verify / heartbeat(多端点按序尝试,超时8s切换) - │ 验签 sig → 通过则按 valid / in_grace 放行 - │ ③ 本机解绑 deactivate(须携带 activation_token) - ▼ - 离线证 LICOFF-:Ed25519 自包含验证,绑设备ID,最长365天,不依赖网络` -const apis = [ - { method: 'POST', path: '/api/v1/license/verify', who: '客户端', desc: '周检验证:返回 valid / in_grace / sig / server_time' }, - { method: 'POST', path: '/api/v1/license/activate', who: '客户端', desc: '激活占座:新设备下发一次性 activation_token' }, - { method: 'POST', path: '/api/v1/license/deactivate', who: '客户端', desc: '解绑本机:必须携带 activation_token' }, - { method: 'POST', path: '/api/v1/license/heartbeat', who: '客户端', desc: '活跃上报:刷新 last_seen(后台"在线"依据)' }, - { method: 'POST', path: '/api/v1/license/devices', who: '客户端', desc: '本序列号的已绑定设备列表' }, - { method: 'GET', path: '/api/v1/products/code/:code', who: '购买页', desc: '产品信息与定价(展示用)' }, - { method: 'POST', path: '/api/v1/orders · /checkout · /purchase/claim', who: '购买页', desc: '下单 / 支付 / 支付完成领取序列号' }, -] - -const fields = [ - { name: 'valid', desc: '是否有效。宽限期内为 true(配合 in_grace 判断提示续费)' }, - { name: 'status', desc: 'active / inactive / expired / revoked / suspended;吊销与暂停无宽限' }, - { name: 'expires_at / expires_at_unix', desc: '到期时间(字符串展示用;unix 为签名字段,直接回用免时区换算)' }, - { name: 'days_left / grace_days_left', desc: '剩余天数 / 宽限剩余天数' }, - { name: 'max_devices / plan_type', desc: '最大设备数 / 套餐类型(trial | monthly | yearly | perpetual)' }, - { name: 'in_grace', desc: '宽限期标记:商业过期 15 天内为 true,照常放行' }, - { name: 'server_time', desc: '服务端时间(unix 秒):客户端本地时钟不得早于历史最大值(防回拨)' }, - { name: 'sig', desc: 'Ed25519 签名(hex):验签失败按未授权处理(假服务器/篡改)' }, - { name: 'activation_token', desc: '仅 activate 响应:一次性下发的设备令牌,解绑必须携带,须持久保存' }, -] diff --git a/frontend/src/views/integration/docData.js b/frontend/src/views/integration/docData.js new file mode 100644 index 0000000..95b456b --- /dev/null +++ b/frontend/src/views/integration/docData.js @@ -0,0 +1,47 @@ +/** + * @file docData.js + * @description 接入文档页的结构化数据:API 契约表、字段表、参数表、时序步骤。 // Logan.Li + */ + +export const API_ENDPOINTS = ['https://licence.rlwj.cn', 'https://l123.91ai.io'] + +export const apis = [ + { method: 'POST', path: '/api/v1/license/verify', who: '客户端', desc: '周检验证:返回 valid / in_grace / sig / server_time(裸对象)' }, + { method: 'POST', path: '/api/v1/license/activate', who: '客户端', desc: '激活占座:新设备下发一次性 activation_token(信封,签名在 data)' }, + { method: 'POST', path: '/api/v1/license/deactivate', who: '客户端', desc: '解绑本机:必须携带 activation_token' }, + { method: 'POST', path: '/api/v1/license/heartbeat', who: '客户端', desc: '活跃上报:刷新 last_seen(后台"在线"依据,信封同 activate)' }, + { method: 'POST', path: '/api/v1/license/devices', who: '客户端', desc: '本序列号的已绑定设备列表' }, + { method: 'GET', path: '/api/v1/products/code/:code', who: '购买页', desc: '产品信息与定价(展示用)' }, + { method: 'POST', path: '/api/v1/checkout · /orders · /purchase/claim', who: '购买页', desc: '下单 / 支付 / 支付完成领取序列号' }, +] + +export const params = [ + { api: 'verify', body: '{ "license_key": "LIC-…", "device_id": "设备唯一ID" }' }, + { api: 'activate', body: '{ "license_key": "LIC-…", "device_id": "设备唯一ID", "device_name": "可选展示名" }' }, + { api: 'deactivate', body: '{ "license_key": "LIC-…", "device_id": "设备唯一ID", "activation_token": "激活时下发" }' }, + { api: 'heartbeat', body: '{ "license_key": "LIC-…", "device_id": "设备唯一ID" }' }, +] + +export const fields = [ + { name: 'valid', desc: '是否有效。宽限期内为 true(配合 in_grace 判断提示续费)' }, + { name: 'status', desc: 'active / inactive / expired / revoked / suspended;吊销与暂停无宽限' }, + { name: 'expires_at / expires_at_unix', desc: '到期时间(字符串展示用;unix 为签名字段,直接回用免时区换算)' }, + { name: 'days_left / grace_days_left', desc: '剩余天数 / 宽限剩余天数' }, + { name: 'max_devices / plan_type', desc: '最大设备数 / 套餐类型(trial | monthly | yearly | perpetual)' }, + { name: 'in_grace', desc: '宽限期标记:商业过期 15 天内为 true,照常放行' }, + { name: 'server_time', desc: '服务端时间(unix 秒):客户端本地时钟不得早于历史最大值(防回拨)' }, + { name: 'sig', desc: 'Ed25519 签名(hex):验签失败按未授权处理(假服务器/篡改)' }, + { name: 'activation_token', desc: '仅 activate 响应:一次性下发的设备令牌,解绑必须携带,须持久保存' }, +] + +export const flowSteps = [ + { title: '① 激活', detail: 'activate(序列号, 设备ID) → 服务端占座并下发一次性 activation_token + 签名 sig,客户端持久保存两者' }, + { title: '② 周检', detail: '每周 verify / heartbeat(多端点按序尝试,超时 8s 切换)→ 验签 sig → 按 valid / in_grace 放行,宽限期提示续费' }, + { title: '③ 解绑', detail: '本机解绑 deactivate 必须携带 activation_token;仅凭序列号无法解绑他人设备' }, + { title: '离线证', detail: 'LICOFF- 前缀为 Ed25519 自包含验证:绑设备 ID、最长 365 天、不依赖网络,服务端仅留审计' }, +] + +export const CURL_EXAMPLE = `# 快速验证(替换为真实序列号) +curl -sS -X POST https://licence.rlwj.cn/api/v1/license/verify \\ + -H 'Content-Type: application/json' \\ + -d '{"license_key":"LIC-XXXX-XXXX-XXXX-XXXX","device_id":"demo"}'`