dsh-mobile-plugin 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -269,8 +269,21 @@ function touch(record, info = {}) {
|
|
|
269
269
|
record.lastSeenAt = now;
|
|
270
270
|
const channel = typeof info.channel === 'string' ? info.channel : '';
|
|
271
271
|
if (channel) record.lastChannel = channel.slice(0, FIELD_MAX);
|
|
272
|
+
// 身份那几格(2026-10-26):名字 / 平台 / 系统版本 / App 版本由**每一次请求**带上来,
|
|
273
|
+
// 顺手刷新。它们以前只在配对那一刻写一次,于是「手机升到新版了、面板还报配对时的旧版本」,
|
|
274
|
+
// 而旧会话升级写进来的记录干脆什么身份都没有(面板只能画「未知设备」)。
|
|
275
|
+
// **只写这次真带了值的格子**:老版本 App 不带头,绝不能把已经有的名字冲成空。
|
|
276
|
+
const meta = deviceMeta(info.meta);
|
|
277
|
+
let metaChanged = false;
|
|
278
|
+
for (const key of Object.keys(meta)) {
|
|
279
|
+
if (record[key] !== meta[key]) {
|
|
280
|
+
record[key] = meta[key];
|
|
281
|
+
metaChanged = true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
272
284
|
if (readOnly) return true;
|
|
273
|
-
|
|
285
|
+
// 身份变了必须落盘(那是面板唯一能看到的来源);只刷新「刚连上过」才走老的一分钟节流。
|
|
286
|
+
if (!metaChanged && now - lastTouchFlushAt < TOUCH_FLUSH_MS) return true;
|
|
274
287
|
lastTouchFlushAt = now;
|
|
275
288
|
save();
|
|
276
289
|
return true;
|
package/lib/impl.js
CHANGED
|
@@ -24,6 +24,7 @@ const { authorized, authOutcome, presentedCredential } = require('./mobile/auth.
|
|
|
24
24
|
const { readRawBody } = require('./shared/e2ee-request')
|
|
25
25
|
const streamRegistry = require('./mobile/stream-registry.js')
|
|
26
26
|
const { viaChannel } = require('./shared/via-header')
|
|
27
|
+
const { deviceMetaFromHeaders } = require('./shared/device-headers.js')
|
|
27
28
|
const { createSidecarLifecycle } = require('./sidecar-lifecycle.cjs')
|
|
28
29
|
const { createMirrorPusher } = require('./mirror/pusher.cjs')
|
|
29
30
|
const { randomBytes } = require('node:crypto')
|
|
@@ -424,12 +425,11 @@ function apply(ctx) {
|
|
|
424
425
|
readBody: (req) => readRawBody(req),
|
|
425
426
|
}
|
|
426
427
|
const guarded = createGuarded(ctx, authOutcome, activity.note, {
|
|
427
|
-
// 鉴权通过后立刻记「最近连接 + 走的是哪条路」:两条代理都注入了 `x-dsh-via`。
|
|
428
428
|
onAuthorized: (req) => {
|
|
429
429
|
const presented = presentedCredential(req)
|
|
430
430
|
if (!presented) return
|
|
431
431
|
const record = credentials.lookup(presented, state.instanceId)
|
|
432
|
-
if (record) credentials.touch(record, { channel: viaChannel(req) })
|
|
432
|
+
if (record) credentials.touch(record, { channel: viaChannel(req), meta: deviceMetaFromHeaders(req) })
|
|
433
433
|
},
|
|
434
434
|
envelope,
|
|
435
435
|
})
|
|
@@ -113,6 +113,30 @@ async function handleCredentialUpgrade(req, res, deps) {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// 同一个 keyId 已经登记过一枚(上一次的响应可能在路上丢了,手机又发了一遍):
|
|
117
|
+
// **不再签发第二条** —— 手机留着手上这枚凭据、按 v2 签名,宿主按 keyId 验签,链路照样通。
|
|
118
|
+
// 没有这一条时每次重试都往库里堆一条记录(实测同一台设备 54 分钟里堆了 39 条,
|
|
119
|
+
// 面板上于是出现一堆同名的行)。
|
|
120
|
+
const known = presented && typeof store.findByKeyId === 'function'
|
|
121
|
+
? store.findByKeyId(instanceId, secret.keyId)
|
|
122
|
+
: null;
|
|
123
|
+
if (known) {
|
|
124
|
+
store.touch(known, { channel: 'upgrade', meta: { ...meta, deviceId: known.deviceId } });
|
|
125
|
+
return sendJson(res, 200, {
|
|
126
|
+
ok: true,
|
|
127
|
+
upgraded: true,
|
|
128
|
+
proto: 2,
|
|
129
|
+
instanceId,
|
|
130
|
+
token: presented,
|
|
131
|
+
cookie: `dsh_lan=${presented}`,
|
|
132
|
+
deviceCredential: presented,
|
|
133
|
+
keyId: known.keyId,
|
|
134
|
+
deviceId: known.deviceId,
|
|
135
|
+
credentialExpiresAt: known.expiresAt || null,
|
|
136
|
+
serverTime: Date.now(),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
116
140
|
const issued = store.issue(instanceId, { ...meta, deviceId }, '', undefined, {
|
|
117
141
|
deviceKey: secret.deviceKey,
|
|
118
142
|
keyId: secret.keyId,
|