fix: 接入文档页——签名规范深底配色修复;接入模型改CSS时序图(不再字符对齐);补请求参数表/curl自测/信封格式说明
This commit is contained in:
1 parent
53678b3ca9
commit
722b719688
2 files changed
+104
-40
No files matched your search
@@ -13,7 +13,27 @@
|
|||||||
title="接入三步:① 拿到产品序列号(后台或购买页) ② 客户端调 activate 激活并保存 activation_token ③ 每周调 verify 周检(验签 sig + 时钟校验)" />
|
title="接入三步:① 拿到产品序列号(后台或购买页) ② 客户端调 activate 激活并保存 activation_token ③ 每周调 verify 周检(验签 sig + 时钟校验)" />
|
||||||
|
|
||||||
<h3 class="h3">接入模型</h3>
|
<h3 class="h3">接入模型</h3>
|
||||||
<pre class="flow">{{ flowText }}</pre>
|
<div class="flow-diagram">
|
||||||
|
<div class="flow-row">
|
||||||
|
<div class="flow-box">
|
||||||
|
<b>客户端</b>
|
||||||
|
<span>桌面 / CLI / Web 前端</span>
|
||||||
|
</div>
|
||||||
|
<div class="flow-arrows">
|
||||||
|
<span class="fa-line">① activate(序列号,设备ID)⟶</span>
|
||||||
|
<span class="fa-line back">⟵ activation_token + sig</span>
|
||||||
|
</div>
|
||||||
|
<div class="flow-box">
|
||||||
|
<b>授权服务器(双节点)</b>
|
||||||
|
<span>licence.rlwj.cn + l123.91ai.io</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flow-steps">
|
||||||
|
<div v-for="st in flowSteps" :key="st.title" class="flow-step">
|
||||||
|
<b>{{ st.title }}</b><span>{{ st.detail }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 class="h3">客户端 API(无需认证,按 IP 限流)</h3>
|
<h3 class="h3">客户端 API(无需认证,按 IP 限流)</h3>
|
||||||
<el-table :data="apis" stripe size="small">
|
<el-table :data="apis" stripe size="small">
|
||||||
@@ -23,6 +43,18 @@
|
|||||||
<el-table-column prop="desc" label="说明" min-width="300" />
|
<el-table-column prop="desc" label="说明" min-width="300" />
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
|
<h3 class="h3">请求体参数</h3>
|
||||||
|
<el-table :data="params" stripe size="small">
|
||||||
|
<el-table-column prop="api" label="接口" width="110" />
|
||||||
|
<el-table-column prop="body" label="JSON 请求体" min-width="480" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<h3 class="h3">快速自测</h3>
|
||||||
|
<div class="code-wrap">
|
||||||
|
<el-button class="copy-btn" size="small" @click="copy(CURL_EXAMPLE, 'curl')">{{ copied === 'curl' ? '已复制' : '复制' }}</el-button>
|
||||||
|
<pre class="code"><code class="hljs" v-html="hl(CURL_EXAMPLE, 'bash')"></code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 class="h3">验证/激活响应关键字段</h3>
|
<h3 class="h3">验证/激活响应关键字段</h3>
|
||||||
<el-table :data="fields" stripe size="small">
|
<el-table :data="fields" stripe size="small">
|
||||||
<el-table-column prop="name" label="字段" width="150" />
|
<el-table-column prop="name" label="字段" width="150" />
|
||||||
@@ -120,8 +152,10 @@ import hljsJava from 'highlight.js/lib/languages/java'
|
|||||||
import hljsPython from 'highlight.js/lib/languages/python'
|
import hljsPython from 'highlight.js/lib/languages/python'
|
||||||
import hljsXml from 'highlight.js/lib/languages/xml'
|
import hljsXml from 'highlight.js/lib/languages/xml'
|
||||||
import hljsJavascript from 'highlight.js/lib/languages/javascript'
|
import hljsJavascript from 'highlight.js/lib/languages/javascript'
|
||||||
|
import hljsBash from 'highlight.js/lib/languages/bash'
|
||||||
import 'highlight.js/styles/github-dark.css'
|
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'
|
import request from '@/api/request'
|
||||||
|
|
||||||
hljs.registerLanguage('go', hljsGo)
|
hljs.registerLanguage('go', hljsGo)
|
||||||
@@ -130,6 +164,13 @@ hljs.registerLanguage('java', hljsJava)
|
|||||||
hljs.registerLanguage('python', hljsPython)
|
hljs.registerLanguage('python', hljsPython)
|
||||||
hljs.registerLanguage('xml', hljsXml)
|
hljs.registerLanguage('xml', hljsXml)
|
||||||
hljs.registerLanguage('javascript', hljsJavascript)
|
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 tab = ref('overview')
|
||||||
const cliKind = ref('go')
|
const cliKind = ref('go')
|
||||||
@@ -166,38 +207,7 @@ const copy = async (text, key) => {
|
|||||||
setTimeout(() => { copied.value = '' }, 1200)
|
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 响应:一次性下发的设备令牌,解绑必须携带,须持久保存' },
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -206,18 +216,25 @@ const fields = [
|
|||||||
.h3 { margin: 20px 0 8px; font-size: 15px; font-weight: 600; }
|
.h3 { margin: 20px 0 8px; font-size: 15px; font-weight: 600; }
|
||||||
.steps { line-height: 1.9; padding-left: 20px; font-size: 13px; }
|
.steps { line-height: 1.9; padding-left: 20px; font-size: 13px; }
|
||||||
.steps code, .tip code { background: var(--el-fill-color); padding: 1px 6px; border-radius: 3px; }
|
.steps code, .tip code { background: var(--el-fill-color); padding: 1px 6px; border-radius: 3px; }
|
||||||
.flow {
|
.flow-diagram {
|
||||||
padding: 12px;
|
padding: 14px;
|
||||||
background: #2f3530;
|
background: #2f3530;
|
||||||
color: #cfe0d4;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
||||||
white-space: pre;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
}
|
||||||
.tip { color: var(--el-text-color-secondary); font-size: 13px; line-height: 1.7; }
|
.tip { color: var(--el-text-color-secondary); font-size: 13px; line-height: 1.7; }
|
||||||
.code-wrap { position: relative; margin: 12px 0 16px; }
|
.code-wrap { position: relative; margin: 12px 0 16px; }
|
||||||
.copy-btn { position: absolute; right: 8px; top: 8px; z-index: 1; }
|
.copy-btn { position: absolute; right: 8px; top: 8px; z-index: 1; }
|
||||||
.code { margin: 0; padding: 16px; background: #1e2227; border-radius: 8px; white-space: pre; overflow: auto; max-height: 620px; }
|
.code { margin: 0; padding: 16px; background: #1e2227; color: #d6dbe1; border-radius: 8px; white-space: pre; overflow: auto; max-height: 620px; }
|
||||||
|
/* 接入模型时序:CSS 盒模型,避免字符画跨字体错位 // Logan.Li */
|
||||||
|
.flow-diagram .flow-row { display: flex; align-items: stretch; gap: 12px; flex-wrap: wrap; }
|
||||||
|
.flow-diagram .flow-box { flex: 1 1 180px; background: #3a4436; border: 1px solid #5b6b52; color: #cfe0d4; border-radius: 8px; padding: 12px 14px; display: flex; flex-direction: column; justify-content: center; gap: 4px; }
|
||||||
|
.flow-diagram .flow-box b { color: #fff; font-size: 14px; }
|
||||||
|
.flow-diagram .flow-box span { font-size: 12px; color: #b9c7b6; word-break: break-all; }
|
||||||
|
.flow-diagram .flow-arrows { flex: 1 1 200px; display: flex; flex-direction: column; justify-content: center; gap: 10px; }
|
||||||
|
.flow-diagram .fa-line { font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: #ffd666; background: #2f3530; border-radius: 6px; padding: 6px 10px; white-space: nowrap; overflow-x: auto; }
|
||||||
|
.flow-diagram .fa-line.back { color: #95d475; }
|
||||||
|
.flow-diagram .flow-steps { margin-top: 12px; display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; }
|
||||||
|
.flow-diagram .flow-step { background: #2f3530; border-radius: 8px; padding: 10px 12px; font-size: 12px; line-height: 1.6; color: #cfe0d4; }
|
||||||
|
.flow-diagram .flow-step b { display: block; color: #95d475; margin-bottom: 2px; }
|
||||||
.code code.hljs { display: block; background: transparent; padding: 0; font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
.code code.hljs { display: block; background: transparent; padding: 0; font: 12px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||||
</style>
|
</style>
|
||||||
@@ -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"}'`
|
||||||
Reference in new issue
Block a user