@xrkseek/harness 0.7.0-rc.2 → 0.7.0
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.
- package/node_modules/@xrkseek/exec-fs/dist/present.d.ts +1 -1
- package/node_modules/@xrkseek/exec-fs/dist/present.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-fs/dist/present.js +51 -3
- package/node_modules/@xrkseek/exec-fs/dist/present.js.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/index.d.ts +1 -0
- package/node_modules/@xrkseek/exec-memory/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/index.js +1 -0
- package/node_modules/@xrkseek/exec-memory/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/layered.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/layered.js +10 -0
- package/node_modules/@xrkseek/exec-memory/dist/layered.js.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/live-fragment.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/live-fragment.js +3 -1
- package/node_modules/@xrkseek/exec-memory/dist/live-fragment.js.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/sqlite.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/sqlite.js +16 -2
- package/node_modules/@xrkseek/exec-memory/dist/sqlite.js.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/store.d.ts +7 -0
- package/node_modules/@xrkseek/exec-memory/dist/store.d.ts.map +1 -1
- package/node_modules/@xrkseek/exec-memory/dist/store.js +11 -0
- package/node_modules/@xrkseek/exec-memory/dist/store.js.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/preset.d.ts.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/preset.js +2 -4
- package/node_modules/@xrkseek/preset-harness/dist/preset.js.map +1 -1
- package/node_modules/@xrkseek/preset-harness/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/preset-minimal/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/preset-server/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-home.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-home.js +2 -5
- package/node_modules/@xrkseek/server-face/dist/agent-home.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts +8 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js +10 -2
- package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts +2 -2
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js +2 -1
- package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js +6 -4
- package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/catalog-seed-labels.d.ts +31 -0
- package/node_modules/@xrkseek/server-face/dist/catalog-seed-labels.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/catalog-seed-labels.js +106 -0
- package/node_modules/@xrkseek/server-face/dist/catalog-seed-labels.js.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/context.d.ts +14 -3
- package/node_modules/@xrkseek/server-face/dist/context.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/catalog.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/catalog.js +11 -0
- package/node_modules/@xrkseek/server-face/dist/handlers/catalog.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/session.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/session.js +27 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/session.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/subagent.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/subagent.js +14 -4
- package/node_modules/@xrkseek/server-face/dist/handlers/subagent.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/team.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/handlers/team.js +76 -12
- package/node_modules/@xrkseek/server-face/dist/handlers/team.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/index.d.ts +5 -3
- package/node_modules/@xrkseek/server-face/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/index.js +5 -3
- package/node_modules/@xrkseek/server-face/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/projections/title-controller.d.ts +6 -0
- package/node_modules/@xrkseek/server-face/dist/projections/title-controller.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/projections/title-controller.js +26 -0
- package/node_modules/@xrkseek/server-face/dist/projections/title-controller.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/runtime.js +72 -21
- package/node_modules/@xrkseek/server-face/dist/runtime.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/seat-channel.d.ts +45 -0
- package/node_modules/@xrkseek/server-face/dist/seat-channel.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/seat-channel.js +93 -0
- package/node_modules/@xrkseek/server-face/dist/seat-channel.js.map +1 -0
- package/node_modules/@xrkseek/server-face/dist/seat-identity.d.ts +13 -0
- package/node_modules/@xrkseek/server-face/dist/seat-identity.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/seat-identity.js +90 -36
- package/node_modules/@xrkseek/server-face/dist/seat-identity.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-capability.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-capability.js +4 -1
- package/node_modules/@xrkseek/server-face/dist/session-capability.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-status.d.ts +4 -0
- package/node_modules/@xrkseek/server-face/dist/session-status.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/session-status.js +93 -51
- package/node_modules/@xrkseek/server-face/dist/session-status.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-credentials.d.ts +5 -0
- package/node_modules/@xrkseek/server-face/dist/settings-credentials.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-credentials.js +20 -0
- package/node_modules/@xrkseek/server-face/dist/settings-credentials.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-document.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-document.js +34 -0
- package/node_modules/@xrkseek/server-face/dist/settings-document.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.js +18 -11
- package/node_modules/@xrkseek/server-face/dist/settings-schemas.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-face.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-face.js +3 -10
- package/node_modules/@xrkseek/server-face/dist/workspace-face.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-member-look.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-member-look.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-member-look.js +4 -1
- package/node_modules/@xrkseek/server-face/dist/workspace-member-look.js.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.d.ts +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.js +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/memory-polish.d.ts +2 -2
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/memory-polish.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/memory-polish.js +27 -13
- package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/memory-polish.js.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/community-live-bridge.d.ts +14 -0
- package/node_modules/@xrkseek/server-host/dist/community-live-bridge.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-host/dist/community-live-bridge.js +149 -0
- package/node_modules/@xrkseek/server-host/dist/community-live-bridge.js.map +1 -0
- package/node_modules/@xrkseek/server-host/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-host/dist/index.js +13 -1
- package/node_modules/@xrkseek/server-host/dist/index.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/adapter-providers.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/adapter-providers.js +15 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/adapter-providers.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/adapter-types.d.ts +11 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/adapter-types.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-live-state.d.ts +38 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-live-state.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-live-state.js +182 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-live-state.js.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-root-http.d.ts +2 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-root-http.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-root-http.js +26 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/community-root-http.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/cordis-connection-bridge.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/cordis-connection-bridge.js +29 -4
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/cordis-connection-bridge.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-compat-matrix.js +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-compat-matrix.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-path-capabilities.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-path-capabilities.js +5 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-path-capabilities.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-skins.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-skins.js +2 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/dsh-skins.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/honest-http-catchall.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/honest-http-catchall.js +13 -3
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/honest-http-catchall.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/incomplete-api-shapes.d.ts +21 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/incomplete-api-shapes.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/incomplete-api-shapes.js +62 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/incomplete-api-shapes.js.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/index.d.ts +5 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/index.js +6 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/index.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-auth.d.ts +19 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-auth.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-auth.js +425 -385
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-auth.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-gate.d.ts +20 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-gate.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-gate.js +22 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access-gate.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access.d.ts +13 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access.js +37 -3
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/mobile-access.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.js +2 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/our-free-model-host.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/plugin-package-id.d.ts +22 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/plugin-package-id.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/plugin-package-id.js +42 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/plugin-package-id.js.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/skin-center.d.ts +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/skin-discover.d.ts +2 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/skin-discover.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/skin-discover.js +5 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/skin-discover.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/underlying/mobile-gate-kit.d.ts +4 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/underlying/mobile-gate-kit.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/underlying/mobile-gate-kit.js +20 -8
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/underlying/mobile-gate-kit.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallet.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallet.js +2 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallet.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper-bg.d.ts +7 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper-bg.d.ts.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper-bg.js +153 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper-bg.js.map +1 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper.d.ts +10 -2
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper.js +361 -29
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wallpaper.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wire-normalize.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wire-normalize.js +3 -0
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/wire-normalize.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/workbuddy-host.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/workbuddy-host.js +2 -1
- package/node_modules/@xrkseek/server-http/dist/dsh-compat/workbuddy-host.js.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/index.d.ts +1 -1
- package/node_modules/@xrkseek/server-http/dist/index.d.ts.map +1 -1
- package/node_modules/@xrkseek/server-http/dist/index.js +1 -1
- package/node_modules/@xrkseek/server-http/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,13 @@ export const CSRF_COOKIE = "dsh_ma_csrf";
|
|
|
8
8
|
export const CSRF_HEADER = "x-dsh-mobile-csrf";
|
|
9
9
|
export const LAN_PIN_COOKIE = "dsh_ma_lan";
|
|
10
10
|
export const WAN_PIN_COOKIE = "dsh_ma_wan";
|
|
11
|
+
/**
|
|
12
|
+
* Non-HttpOnly twin of the LAN/WAN PIN cookies. The SPA cannot read HttpOnly
|
|
13
|
+
* PIN digests; this marker lets `sessionStorage.xrk.operatorAccess` elevate on
|
|
14
|
+
* cookie-return visits so Settings → Plugins cards stay Host-backed.
|
|
15
|
+
* Keep in sync with client `OPERATOR_UI_COOKIE` (`@xrkseek/client-connection`).
|
|
16
|
+
*/
|
|
17
|
+
export const OPERATOR_UI_COOKIE = "xrk_operator_ui";
|
|
11
18
|
const DEVICE_TTL_MS = 90 * 24 * 60 * 60 * 1000;
|
|
12
19
|
const SESSION_TTL_MS = 12 * 60 * 60 * 1000;
|
|
13
20
|
const MAX_DEVICES = 32;
|
|
@@ -135,6 +142,7 @@ export function renewSession(state, deviceToken, now = Date.now()) {
|
|
|
135
142
|
}
|
|
136
143
|
export function setMobileSessionCookies(res, tokens, tls = false) {
|
|
137
144
|
const now = Date.now();
|
|
145
|
+
const sessionMaxAgeSeconds = Math.floor((tokens.sessionExpiresAt - now) / 1000);
|
|
138
146
|
res.setHeader("set-cookie", [
|
|
139
147
|
cookie(DEVICE_COOKIE, tokens.deviceToken, {
|
|
140
148
|
path: "/mobile-access/auth/renew",
|
|
@@ -144,16 +152,23 @@ export function setMobileSessionCookies(res, tokens, tls = false) {
|
|
|
144
152
|
}),
|
|
145
153
|
cookie(SESSION_COOKIE, tokens.sessionToken, {
|
|
146
154
|
path: "/",
|
|
147
|
-
maxAgeSeconds:
|
|
155
|
+
maxAgeSeconds: sessionMaxAgeSeconds,
|
|
148
156
|
httpOnly: true,
|
|
149
157
|
tls,
|
|
150
158
|
}),
|
|
151
159
|
cookie(CSRF_COOKIE, tokens.csrfToken, {
|
|
152
160
|
path: "/",
|
|
153
|
-
maxAgeSeconds:
|
|
161
|
+
maxAgeSeconds: sessionMaxAgeSeconds,
|
|
154
162
|
httpOnly: true,
|
|
155
163
|
tls,
|
|
156
164
|
}),
|
|
165
|
+
// Same SPA elevation marker as PIN (cookie-return / pair without re-run).
|
|
166
|
+
cookie(OPERATOR_UI_COOKIE, "1", {
|
|
167
|
+
path: "/",
|
|
168
|
+
maxAgeSeconds: sessionMaxAgeSeconds,
|
|
169
|
+
httpOnly: false,
|
|
170
|
+
tls,
|
|
171
|
+
}),
|
|
157
172
|
]);
|
|
158
173
|
}
|
|
159
174
|
export function parseCookieHeader(header, name) {
|
|
@@ -197,13 +212,22 @@ export function verifyMobileSession(state, sessionToken, now = Date.now()) {
|
|
|
197
212
|
}
|
|
198
213
|
export function setAccessPinCookie(res, cookieName, pin, instanceId, tls = false) {
|
|
199
214
|
const value = accessPinCookieDigest(pin, instanceId);
|
|
215
|
+
const maxAgeSeconds = Math.floor(ACCESS_PIN_TTL_MS / 1000);
|
|
200
216
|
res.setHeader("set-cookie", [
|
|
201
217
|
cookie(cookieName, value, {
|
|
202
218
|
path: "/",
|
|
203
|
-
maxAgeSeconds
|
|
219
|
+
maxAgeSeconds,
|
|
204
220
|
httpOnly: true,
|
|
205
221
|
tls,
|
|
206
222
|
}),
|
|
223
|
+
// Readable by the SPA so WAN/LAN cookie-return visits elevate without
|
|
224
|
+
// re-running the PIN page script (which alone used to set sessionStorage).
|
|
225
|
+
cookie(OPERATOR_UI_COOKIE, "1", {
|
|
226
|
+
path: "/",
|
|
227
|
+
maxAgeSeconds,
|
|
228
|
+
httpOnly: false,
|
|
229
|
+
tls,
|
|
230
|
+
}),
|
|
207
231
|
]);
|
|
208
232
|
}
|
|
209
233
|
export function publicDevice(device) {
|
|
@@ -217,401 +241,417 @@ export function publicDevice(device) {
|
|
|
217
241
|
lastSeenAt: device.lastSeenAt,
|
|
218
242
|
};
|
|
219
243
|
}
|
|
220
|
-
export const PAIR_PAGE_HTML = `<!doctype html>
|
|
221
|
-
<html lang="zh-CN">
|
|
222
|
-
<meta charset="utf-8">
|
|
223
|
-
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
224
|
-
<title>配对 XRK 移动访问</title>
|
|
225
|
-
<main style="font-family:system-ui,sans-serif;max-width:28rem;margin:2rem auto;padding:0 1rem">
|
|
226
|
-
<h1>配对此设备</h1>
|
|
227
|
-
<p>输入桌面端显示的配对码,或从二维码链接自动填充。</p>
|
|
228
|
-
<form id="pair-form">
|
|
229
|
-
<label style="display:block;margin:.75rem 0">配对码
|
|
230
|
-
<input id="pair-token" autocomplete="one-time-code" required style="width:100%;padding:.5rem">
|
|
231
|
-
</label>
|
|
232
|
-
<label style="display:block;margin:.75rem 0">设备名称
|
|
233
|
-
<input id="device-label" maxlength="64" autocomplete="off" style="width:100%;padding:.5rem">
|
|
234
|
-
</label>
|
|
235
|
-
<button type="submit" style="padding:.5rem 1rem">配对</button>
|
|
236
|
-
<output id="pair-status" style="display:block;margin-top:.75rem"></output>
|
|
237
|
-
</form>
|
|
238
|
-
</main>
|
|
239
|
-
<script src="/mobile-access/pair.js" defer></script>
|
|
244
|
+
export const PAIR_PAGE_HTML = `<!doctype html>
|
|
245
|
+
<html lang="zh-CN">
|
|
246
|
+
<meta charset="utf-8">
|
|
247
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
248
|
+
<title>配对 XRK 移动访问</title>
|
|
249
|
+
<main style="font-family:system-ui,sans-serif;max-width:28rem;margin:2rem auto;padding:0 1rem">
|
|
250
|
+
<h1>配对此设备</h1>
|
|
251
|
+
<p>输入桌面端显示的配对码,或从二维码链接自动填充。</p>
|
|
252
|
+
<form id="pair-form">
|
|
253
|
+
<label style="display:block;margin:.75rem 0">配对码
|
|
254
|
+
<input id="pair-token" autocomplete="one-time-code" required style="width:100%;padding:.5rem">
|
|
255
|
+
</label>
|
|
256
|
+
<label style="display:block;margin:.75rem 0">设备名称
|
|
257
|
+
<input id="device-label" maxlength="64" autocomplete="off" style="width:100%;padding:.5rem">
|
|
258
|
+
</label>
|
|
259
|
+
<button type="submit" style="padding:.5rem 1rem">配对</button>
|
|
260
|
+
<output id="pair-status" style="display:block;margin-top:.75rem"></output>
|
|
261
|
+
</form>
|
|
262
|
+
</main>
|
|
263
|
+
<script src="/mobile-access/pair.js" defer></script>
|
|
240
264
|
</html>`;
|
|
241
|
-
export const PAIR_PAGE_JS = `(() => {
|
|
242
|
-
const params = new URLSearchParams(location.hash.replace(/^#/, ''))
|
|
243
|
-
const tokenInput = document.getElementById('pair-token')
|
|
244
|
-
const labelInput = document.getElementById('device-label')
|
|
245
|
-
const status = document.getElementById('pair-status')
|
|
246
|
-
const form = document.getElementById('pair-form')
|
|
247
|
-
const hashToken = params.get('token')
|
|
248
|
-
if (hashToken && tokenInput) tokenInput.value = hashToken
|
|
249
|
-
form?.addEventListener('submit', async (event) => {
|
|
250
|
-
event.preventDefault()
|
|
251
|
-
status.textContent = '配对中…'
|
|
252
|
-
const response = await fetch('/mobile-access/auth/pair', {
|
|
253
|
-
method: 'POST',
|
|
254
|
-
credentials: 'same-origin',
|
|
255
|
-
headers: { 'content-type': 'application/json' },
|
|
256
|
-
body: JSON.stringify({
|
|
257
|
-
token: tokenInput?.value ?? '',
|
|
258
|
-
label: labelInput?.value || undefined,
|
|
259
|
-
}),
|
|
260
|
-
})
|
|
261
|
-
if (!response.ok) {
|
|
262
|
-
status.textContent = '配对失败(链接可能已过期)'
|
|
263
|
-
return
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
})
|
|
265
|
+
export const PAIR_PAGE_JS = `(() => {
|
|
266
|
+
const params = new URLSearchParams(location.hash.replace(/^#/, ''))
|
|
267
|
+
const tokenInput = document.getElementById('pair-token')
|
|
268
|
+
const labelInput = document.getElementById('device-label')
|
|
269
|
+
const status = document.getElementById('pair-status')
|
|
270
|
+
const form = document.getElementById('pair-form')
|
|
271
|
+
const hashToken = params.get('token')
|
|
272
|
+
if (hashToken && tokenInput) tokenInput.value = hashToken
|
|
273
|
+
form?.addEventListener('submit', async (event) => {
|
|
274
|
+
event.preventDefault()
|
|
275
|
+
status.textContent = '配对中…'
|
|
276
|
+
const response = await fetch('/mobile-access/auth/pair', {
|
|
277
|
+
method: 'POST',
|
|
278
|
+
credentials: 'same-origin',
|
|
279
|
+
headers: { 'content-type': 'application/json' },
|
|
280
|
+
body: JSON.stringify({
|
|
281
|
+
token: tokenInput?.value ?? '',
|
|
282
|
+
label: labelInput?.value || undefined,
|
|
283
|
+
}),
|
|
284
|
+
})
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
status.textContent = '配对失败(链接可能已过期)'
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
try { sessionStorage.setItem('xrk.operatorAccess', '1') } catch {}
|
|
290
|
+
location.replace('/')
|
|
291
|
+
})
|
|
292
|
+
})()
|
|
268
293
|
`;
|
|
269
|
-
export const LOGIN_PAGE_HTML = `<!doctype html>
|
|
270
|
-
<html lang="zh-CN">
|
|
271
|
-
<meta charset="utf-8">
|
|
272
|
-
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
273
|
-
<title>XRK 移动访问</title>
|
|
274
|
-
<main style="font-family:system-ui,sans-serif;max-width:28rem;margin:2rem auto;padding:0 1rem">
|
|
275
|
-
<h1>正在恢复会话…</h1>
|
|
276
|
-
<p id="login-progress">正在验证设备凭据。</p>
|
|
277
|
-
<p id="login-failed" hidden>无法恢复会话,请重新配对。</p>
|
|
278
|
-
<a href="/mobile-access/pair">打开配对页</a>
|
|
279
|
-
</main>
|
|
280
|
-
<script src="/mobile-access/login.js" defer></script>
|
|
294
|
+
export const LOGIN_PAGE_HTML = `<!doctype html>
|
|
295
|
+
<html lang="zh-CN">
|
|
296
|
+
<meta charset="utf-8">
|
|
297
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
298
|
+
<title>XRK 移动访问</title>
|
|
299
|
+
<main style="font-family:system-ui,sans-serif;max-width:28rem;margin:2rem auto;padding:0 1rem">
|
|
300
|
+
<h1>正在恢复会话…</h1>
|
|
301
|
+
<p id="login-progress">正在验证设备凭据。</p>
|
|
302
|
+
<p id="login-failed" hidden>无法恢复会话,请重新配对。</p>
|
|
303
|
+
<a href="/mobile-access/pair">打开配对页</a>
|
|
304
|
+
</main>
|
|
305
|
+
<script src="/mobile-access/login.js" defer></script>
|
|
281
306
|
</html>`;
|
|
282
|
-
export const LOGIN_PAGE_JS = `(() => {
|
|
283
|
-
fetch('/mobile-access/auth/renew', {
|
|
284
|
-
method: 'POST',
|
|
285
|
-
credentials: 'same-origin',
|
|
286
|
-
headers: { 'content-type': 'application/json' },
|
|
287
|
-
body: '{}',
|
|
288
|
-
}).then((response) => {
|
|
289
|
-
if (response.ok) {
|
|
290
|
-
|
|
291
|
-
location.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
document.getElementById('login-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
})
|
|
307
|
+
export const LOGIN_PAGE_JS = `(() => {
|
|
308
|
+
fetch('/mobile-access/auth/renew', {
|
|
309
|
+
method: 'POST',
|
|
310
|
+
credentials: 'same-origin',
|
|
311
|
+
headers: { 'content-type': 'application/json' },
|
|
312
|
+
body: '{}',
|
|
313
|
+
}).then((response) => {
|
|
314
|
+
if (response.ok) {
|
|
315
|
+
try { sessionStorage.setItem('xrk.operatorAccess', '1') } catch {}
|
|
316
|
+
const next = new URLSearchParams(location.search).get('return') || '/'
|
|
317
|
+
location.replace(next)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
document.getElementById('login-progress').hidden = true
|
|
321
|
+
document.getElementById('login-failed').hidden = false
|
|
322
|
+
}).catch(() => {
|
|
323
|
+
const el = document.getElementById('login-progress')
|
|
324
|
+
if (el) el.textContent = '无法连接到主机。'
|
|
325
|
+
})
|
|
326
|
+
})()
|
|
301
327
|
`;
|
|
302
328
|
/** Gate pages share product-shell first-paint (index.html) light/dark floors. */
|
|
303
|
-
const GATE_SHELL_CSS = `
|
|
304
|
-
:root {
|
|
305
|
-
color-scheme: light dark;
|
|
306
|
-
--gate-bg: #f4f4f5;
|
|
307
|
-
--gate-fg: #14161a;
|
|
308
|
-
--gate-muted: #5c6168;
|
|
309
|
-
--gate-border: rgb(0 0 0 / 12%);
|
|
310
|
-
--gate-fill: #14161a;
|
|
311
|
-
--gate-fill-fg: #f4f4f5;
|
|
312
|
-
--gate-fill-hover: #2a2d33;
|
|
313
|
-
--gate-error: #c41e1e;
|
|
314
|
-
--gate-field: #ffffff;
|
|
315
|
-
--gate-ring: #5c6168;
|
|
316
|
-
--gate-ring-dim: rgb(0 0 0 / 14%);
|
|
317
|
-
}
|
|
318
|
-
html[data-theme="dark"] {
|
|
319
|
-
color-scheme: dark;
|
|
320
|
-
--gate-bg: #151517;
|
|
321
|
-
--gate-fg: #e8e8ea;
|
|
322
|
-
--gate-muted: #9a9aa3;
|
|
323
|
-
--gate-border: rgb(255 255 255 / 12%);
|
|
324
|
-
--gate-fill: #e8e8ea;
|
|
325
|
-
--gate-fill-fg: #151517;
|
|
326
|
-
--gate-fill-hover: #c4c4cc;
|
|
327
|
-
--gate-error: #f25a5a;
|
|
328
|
-
--gate-field: #1c1c1f;
|
|
329
|
-
--gate-ring: #c4c4cc;
|
|
330
|
-
--gate-ring-dim: rgb(255 255 255 / 12%);
|
|
331
|
-
}
|
|
332
|
-
*, *::before, *::after { box-sizing: border-box; }
|
|
333
|
-
html, body { height: 100%; margin: 0; }
|
|
334
|
-
body {
|
|
335
|
-
min-height: 100dvh;
|
|
336
|
-
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
337
|
-
color: var(--gate-fg);
|
|
338
|
-
background:
|
|
339
|
-
radial-gradient(120% 80% at 50% -10%, rgb(255 255 255 / 70%), transparent 55%),
|
|
340
|
-
radial-gradient(90% 70% at 50% 120%, rgb(20 22 26 / 4%), transparent 50%),
|
|
341
|
-
var(--gate-bg);
|
|
342
|
-
-webkit-font-smoothing: antialiased;
|
|
343
|
-
-webkit-tap-highlight-color: transparent;
|
|
344
|
-
}
|
|
345
|
-
html[data-theme="dark"] body {
|
|
346
|
-
background:
|
|
347
|
-
radial-gradient(120% 80% at 50% -10%, rgb(255 255 255 / 6%), transparent 55%),
|
|
348
|
-
radial-gradient(90% 70% at 50% 120%, rgb(0 0 0 / 35%), transparent 50%),
|
|
349
|
-
var(--gate-bg);
|
|
350
|
-
}
|
|
351
|
-
.gate {
|
|
352
|
-
min-height: 100dvh;
|
|
353
|
-
display: flex;
|
|
354
|
-
flex-direction: column;
|
|
355
|
-
align-items: center;
|
|
356
|
-
justify-content: center;
|
|
357
|
-
padding:
|
|
358
|
-
max(1.5rem, env(safe-area-inset-top))
|
|
359
|
-
max(1.25rem, env(safe-area-inset-right))
|
|
360
|
-
max(1.75rem, env(safe-area-inset-bottom))
|
|
361
|
-
max(1.25rem, env(safe-area-inset-left));
|
|
362
|
-
}
|
|
363
|
-
.panel {
|
|
364
|
-
width: min(100%, 22.5rem);
|
|
365
|
-
display: flex;
|
|
366
|
-
flex-direction: column;
|
|
367
|
-
align-items: stretch;
|
|
368
|
-
gap: 0;
|
|
369
|
-
}
|
|
370
|
-
.mark {
|
|
371
|
-
font-size: 13px;
|
|
372
|
-
font-weight: 650;
|
|
373
|
-
letter-spacing: 0.14em;
|
|
374
|
-
line-height: 1;
|
|
375
|
-
color: var(--gate-fg);
|
|
376
|
-
margin: 0 0 1.75rem;
|
|
377
|
-
text-align: center;
|
|
378
|
-
}
|
|
379
|
-
h1 {
|
|
380
|
-
margin: 0 0 0.5rem;
|
|
381
|
-
font-size: clamp(1.375rem, 4.2vw, 1.625rem);
|
|
382
|
-
font-weight: 600;
|
|
383
|
-
letter-spacing: -0.02em;
|
|
384
|
-
line-height: 1.25;
|
|
385
|
-
text-align: center;
|
|
386
|
-
}
|
|
387
|
-
.hint {
|
|
388
|
-
margin: 0 0 1.5rem;
|
|
389
|
-
font-size: 0.9375rem;
|
|
390
|
-
line-height: 1.45;
|
|
391
|
-
color: var(--gate-muted);
|
|
392
|
-
text-align: center;
|
|
393
|
-
text-wrap: pretty;
|
|
394
|
-
}
|
|
395
|
-
form {
|
|
396
|
-
display: flex;
|
|
397
|
-
flex-direction: column;
|
|
398
|
-
gap: 0.875rem;
|
|
399
|
-
}
|
|
400
|
-
.field-label {
|
|
401
|
-
position: absolute;
|
|
402
|
-
width: 1px;
|
|
403
|
-
height: 1px;
|
|
404
|
-
padding: 0;
|
|
405
|
-
margin: -1px;
|
|
406
|
-
overflow: hidden;
|
|
407
|
-
clip: rect(0, 0, 0, 0);
|
|
408
|
-
white-space: nowrap;
|
|
409
|
-
border: 0;
|
|
410
|
-
}
|
|
411
|
-
#access-pin {
|
|
412
|
-
width: 100%;
|
|
413
|
-
height: 3.25rem;
|
|
414
|
-
padding: 0 1rem;
|
|
415
|
-
border: 1px solid var(--gate-border);
|
|
416
|
-
border-radius: 14px;
|
|
417
|
-
background: var(--gate-field);
|
|
418
|
-
color: var(--gate-fg);
|
|
419
|
-
font-size: 1.375rem;
|
|
420
|
-
font-variant-numeric: tabular-nums;
|
|
421
|
-
letter-spacing: 0.28em;
|
|
422
|
-
text-align: center;
|
|
423
|
-
caret-color: var(--gate-fg);
|
|
424
|
-
outline: none;
|
|
425
|
-
transition: border-color 120ms ease, box-shadow 120ms ease;
|
|
426
|
-
}
|
|
427
|
-
#access-pin::placeholder {
|
|
428
|
-
color: var(--gate-muted);
|
|
429
|
-
letter-spacing: 0.12em;
|
|
430
|
-
opacity: 0.7;
|
|
431
|
-
}
|
|
432
|
-
#access-pin:focus-visible {
|
|
433
|
-
border-color: var(--gate-fg);
|
|
434
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--gate-fg) 18%, transparent);
|
|
435
|
-
}
|
|
436
|
-
#access-pin[aria-invalid="true"] {
|
|
437
|
-
border-color: var(--gate-error);
|
|
438
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--gate-error) 22%, transparent);
|
|
439
|
-
}
|
|
440
|
-
#pin-submit {
|
|
441
|
-
display: inline-flex;
|
|
442
|
-
align-items: center;
|
|
443
|
-
justify-content: center;
|
|
444
|
-
width: 100%;
|
|
445
|
-
height: 2.75rem;
|
|
446
|
-
margin: 0;
|
|
447
|
-
padding: 0 1rem;
|
|
448
|
-
border: none;
|
|
449
|
-
border-radius: 18px;
|
|
450
|
-
background: var(--gate-fill);
|
|
451
|
-
color: var(--gate-fill-fg);
|
|
452
|
-
font: inherit;
|
|
453
|
-
font-size: 0.9375rem;
|
|
454
|
-
font-weight: 560;
|
|
455
|
-
line-height: 1;
|
|
456
|
-
cursor: pointer;
|
|
457
|
-
transition: background 120ms ease, opacity 120ms ease;
|
|
458
|
-
}
|
|
459
|
-
#pin-submit:hover:not(:disabled) { background: var(--gate-fill-hover); }
|
|
460
|
-
#pin-submit:disabled {
|
|
461
|
-
cursor: not-allowed;
|
|
462
|
-
opacity: 0.45;
|
|
463
|
-
}
|
|
464
|
-
#pin-submit:focus-visible {
|
|
465
|
-
outline: 2px solid var(--gate-fg);
|
|
466
|
-
outline-offset: 2px;
|
|
467
|
-
}
|
|
468
|
-
#pin-status {
|
|
469
|
-
min-height: 1.25rem;
|
|
470
|
-
margin: 0;
|
|
471
|
-
font-size: 0.875rem;
|
|
472
|
-
line-height: 1.35;
|
|
473
|
-
text-align: center;
|
|
474
|
-
color: var(--gate-muted);
|
|
475
|
-
}
|
|
476
|
-
#pin-status[data-tone="error"] { color: var(--gate-error); }
|
|
477
|
-
#pin-status[data-tone="busy"] { color: var(--gate-muted); }
|
|
478
|
-
@media (max-width: 480px) {
|
|
479
|
-
#access-pin {
|
|
480
|
-
height: 3.5rem;
|
|
481
|
-
font-size: 1.5rem;
|
|
482
|
-
letter-spacing: 0.32em;
|
|
483
|
-
border-radius: 16px;
|
|
484
|
-
}
|
|
485
|
-
#pin-submit { height: 3rem; font-size: 1rem; }
|
|
486
|
-
}
|
|
487
|
-
@media (prefers-reduced-motion: reduce) {
|
|
488
|
-
#access-pin, #pin-submit { transition: none; }
|
|
489
|
-
}
|
|
329
|
+
const GATE_SHELL_CSS = `
|
|
330
|
+
:root {
|
|
331
|
+
color-scheme: light dark;
|
|
332
|
+
--gate-bg: #f4f4f5;
|
|
333
|
+
--gate-fg: #14161a;
|
|
334
|
+
--gate-muted: #5c6168;
|
|
335
|
+
--gate-border: rgb(0 0 0 / 12%);
|
|
336
|
+
--gate-fill: #14161a;
|
|
337
|
+
--gate-fill-fg: #f4f4f5;
|
|
338
|
+
--gate-fill-hover: #2a2d33;
|
|
339
|
+
--gate-error: #c41e1e;
|
|
340
|
+
--gate-field: #ffffff;
|
|
341
|
+
--gate-ring: #5c6168;
|
|
342
|
+
--gate-ring-dim: rgb(0 0 0 / 14%);
|
|
343
|
+
}
|
|
344
|
+
html[data-theme="dark"] {
|
|
345
|
+
color-scheme: dark;
|
|
346
|
+
--gate-bg: #151517;
|
|
347
|
+
--gate-fg: #e8e8ea;
|
|
348
|
+
--gate-muted: #9a9aa3;
|
|
349
|
+
--gate-border: rgb(255 255 255 / 12%);
|
|
350
|
+
--gate-fill: #e8e8ea;
|
|
351
|
+
--gate-fill-fg: #151517;
|
|
352
|
+
--gate-fill-hover: #c4c4cc;
|
|
353
|
+
--gate-error: #f25a5a;
|
|
354
|
+
--gate-field: #1c1c1f;
|
|
355
|
+
--gate-ring: #c4c4cc;
|
|
356
|
+
--gate-ring-dim: rgb(255 255 255 / 12%);
|
|
357
|
+
}
|
|
358
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
359
|
+
html, body { height: 100%; margin: 0; }
|
|
360
|
+
body {
|
|
361
|
+
min-height: 100dvh;
|
|
362
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
363
|
+
color: var(--gate-fg);
|
|
364
|
+
background:
|
|
365
|
+
radial-gradient(120% 80% at 50% -10%, rgb(255 255 255 / 70%), transparent 55%),
|
|
366
|
+
radial-gradient(90% 70% at 50% 120%, rgb(20 22 26 / 4%), transparent 50%),
|
|
367
|
+
var(--gate-bg);
|
|
368
|
+
-webkit-font-smoothing: antialiased;
|
|
369
|
+
-webkit-tap-highlight-color: transparent;
|
|
370
|
+
}
|
|
371
|
+
html[data-theme="dark"] body {
|
|
372
|
+
background:
|
|
373
|
+
radial-gradient(120% 80% at 50% -10%, rgb(255 255 255 / 6%), transparent 55%),
|
|
374
|
+
radial-gradient(90% 70% at 50% 120%, rgb(0 0 0 / 35%), transparent 50%),
|
|
375
|
+
var(--gate-bg);
|
|
376
|
+
}
|
|
377
|
+
.gate {
|
|
378
|
+
min-height: 100dvh;
|
|
379
|
+
display: flex;
|
|
380
|
+
flex-direction: column;
|
|
381
|
+
align-items: center;
|
|
382
|
+
justify-content: center;
|
|
383
|
+
padding:
|
|
384
|
+
max(1.5rem, env(safe-area-inset-top))
|
|
385
|
+
max(1.25rem, env(safe-area-inset-right))
|
|
386
|
+
max(1.75rem, env(safe-area-inset-bottom))
|
|
387
|
+
max(1.25rem, env(safe-area-inset-left));
|
|
388
|
+
}
|
|
389
|
+
.panel {
|
|
390
|
+
width: min(100%, 22.5rem);
|
|
391
|
+
display: flex;
|
|
392
|
+
flex-direction: column;
|
|
393
|
+
align-items: stretch;
|
|
394
|
+
gap: 0;
|
|
395
|
+
}
|
|
396
|
+
.mark {
|
|
397
|
+
font-size: 13px;
|
|
398
|
+
font-weight: 650;
|
|
399
|
+
letter-spacing: 0.14em;
|
|
400
|
+
line-height: 1;
|
|
401
|
+
color: var(--gate-fg);
|
|
402
|
+
margin: 0 0 1.75rem;
|
|
403
|
+
text-align: center;
|
|
404
|
+
}
|
|
405
|
+
h1 {
|
|
406
|
+
margin: 0 0 0.5rem;
|
|
407
|
+
font-size: clamp(1.375rem, 4.2vw, 1.625rem);
|
|
408
|
+
font-weight: 600;
|
|
409
|
+
letter-spacing: -0.02em;
|
|
410
|
+
line-height: 1.25;
|
|
411
|
+
text-align: center;
|
|
412
|
+
}
|
|
413
|
+
.hint {
|
|
414
|
+
margin: 0 0 1.5rem;
|
|
415
|
+
font-size: 0.9375rem;
|
|
416
|
+
line-height: 1.45;
|
|
417
|
+
color: var(--gate-muted);
|
|
418
|
+
text-align: center;
|
|
419
|
+
text-wrap: pretty;
|
|
420
|
+
}
|
|
421
|
+
form {
|
|
422
|
+
display: flex;
|
|
423
|
+
flex-direction: column;
|
|
424
|
+
gap: 0.875rem;
|
|
425
|
+
}
|
|
426
|
+
.field-label {
|
|
427
|
+
position: absolute;
|
|
428
|
+
width: 1px;
|
|
429
|
+
height: 1px;
|
|
430
|
+
padding: 0;
|
|
431
|
+
margin: -1px;
|
|
432
|
+
overflow: hidden;
|
|
433
|
+
clip: rect(0, 0, 0, 0);
|
|
434
|
+
white-space: nowrap;
|
|
435
|
+
border: 0;
|
|
436
|
+
}
|
|
437
|
+
#access-pin {
|
|
438
|
+
width: 100%;
|
|
439
|
+
height: 3.25rem;
|
|
440
|
+
padding: 0 1rem;
|
|
441
|
+
border: 1px solid var(--gate-border);
|
|
442
|
+
border-radius: 14px;
|
|
443
|
+
background: var(--gate-field);
|
|
444
|
+
color: var(--gate-fg);
|
|
445
|
+
font-size: 1.375rem;
|
|
446
|
+
font-variant-numeric: tabular-nums;
|
|
447
|
+
letter-spacing: 0.28em;
|
|
448
|
+
text-align: center;
|
|
449
|
+
caret-color: var(--gate-fg);
|
|
450
|
+
outline: none;
|
|
451
|
+
transition: border-color 120ms ease, box-shadow 120ms ease;
|
|
452
|
+
}
|
|
453
|
+
#access-pin::placeholder {
|
|
454
|
+
color: var(--gate-muted);
|
|
455
|
+
letter-spacing: 0.12em;
|
|
456
|
+
opacity: 0.7;
|
|
457
|
+
}
|
|
458
|
+
#access-pin:focus-visible {
|
|
459
|
+
border-color: var(--gate-fg);
|
|
460
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--gate-fg) 18%, transparent);
|
|
461
|
+
}
|
|
462
|
+
#access-pin[aria-invalid="true"] {
|
|
463
|
+
border-color: var(--gate-error);
|
|
464
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--gate-error) 22%, transparent);
|
|
465
|
+
}
|
|
466
|
+
#pin-submit {
|
|
467
|
+
display: inline-flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: center;
|
|
470
|
+
width: 100%;
|
|
471
|
+
height: 2.75rem;
|
|
472
|
+
margin: 0;
|
|
473
|
+
padding: 0 1rem;
|
|
474
|
+
border: none;
|
|
475
|
+
border-radius: 18px;
|
|
476
|
+
background: var(--gate-fill);
|
|
477
|
+
color: var(--gate-fill-fg);
|
|
478
|
+
font: inherit;
|
|
479
|
+
font-size: 0.9375rem;
|
|
480
|
+
font-weight: 560;
|
|
481
|
+
line-height: 1;
|
|
482
|
+
cursor: pointer;
|
|
483
|
+
transition: background 120ms ease, opacity 120ms ease;
|
|
484
|
+
}
|
|
485
|
+
#pin-submit:hover:not(:disabled) { background: var(--gate-fill-hover); }
|
|
486
|
+
#pin-submit:disabled {
|
|
487
|
+
cursor: not-allowed;
|
|
488
|
+
opacity: 0.45;
|
|
489
|
+
}
|
|
490
|
+
#pin-submit:focus-visible {
|
|
491
|
+
outline: 2px solid var(--gate-fg);
|
|
492
|
+
outline-offset: 2px;
|
|
493
|
+
}
|
|
494
|
+
#pin-status {
|
|
495
|
+
min-height: 1.25rem;
|
|
496
|
+
margin: 0;
|
|
497
|
+
font-size: 0.875rem;
|
|
498
|
+
line-height: 1.35;
|
|
499
|
+
text-align: center;
|
|
500
|
+
color: var(--gate-muted);
|
|
501
|
+
}
|
|
502
|
+
#pin-status[data-tone="error"] { color: var(--gate-error); }
|
|
503
|
+
#pin-status[data-tone="busy"] { color: var(--gate-muted); }
|
|
504
|
+
@media (max-width: 480px) {
|
|
505
|
+
#access-pin {
|
|
506
|
+
height: 3.5rem;
|
|
507
|
+
font-size: 1.5rem;
|
|
508
|
+
letter-spacing: 0.32em;
|
|
509
|
+
border-radius: 16px;
|
|
510
|
+
}
|
|
511
|
+
#pin-submit { height: 3rem; font-size: 1rem; }
|
|
512
|
+
}
|
|
513
|
+
@media (prefers-reduced-motion: reduce) {
|
|
514
|
+
#access-pin, #pin-submit { transition: none; }
|
|
515
|
+
}
|
|
490
516
|
`.replace(/\n/g, "");
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Stamp `data-theme` from durable Settings (same resolve as product
|
|
519
|
+
* `injectBootTheme`), not OS-only `prefers-color-scheme`.
|
|
520
|
+
*/
|
|
521
|
+
export function gateThemeBootScript(preference = "system") {
|
|
522
|
+
return `(function(){
|
|
523
|
+
var preference = ${JSON.stringify(preference)};
|
|
524
|
+
var systemDark = preference === "system"
|
|
525
|
+
&& typeof matchMedia !== "undefined"
|
|
526
|
+
&& matchMedia("(prefers-color-scheme: dark)").matches;
|
|
527
|
+
var dark = preference === "dark" || systemDark;
|
|
528
|
+
document.documentElement.dataset.theme = dark ? "dark" : "light";
|
|
529
|
+
document.documentElement.style.colorScheme = dark ? "dark" : "light";
|
|
496
530
|
})();`;
|
|
497
|
-
|
|
531
|
+
}
|
|
532
|
+
function pinPageHtml(mode, title, preference = "system") {
|
|
498
533
|
const kind = mode === "wan" ? "远程" : "局域网";
|
|
499
|
-
return `<!doctype html>
|
|
500
|
-
<html lang="zh-CN">
|
|
501
|
-
<head>
|
|
502
|
-
<meta charset="utf-8">
|
|
503
|
-
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,interactive-widget=resizes-content">
|
|
504
|
-
<meta name="color-scheme" content="light dark">
|
|
505
|
-
<meta name="theme-color" content="#151517" media="(prefers-color-scheme: dark)">
|
|
506
|
-
<meta name="theme-color" content="#f4f4f5" media="(prefers-color-scheme: light)">
|
|
507
|
-
<title>${title} · XRK Harness</title>
|
|
508
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
509
|
-
<style>${GATE_SHELL_CSS}</style>
|
|
510
|
-
<script>${
|
|
511
|
-
</head>
|
|
512
|
-
<body>
|
|
513
|
-
<main class="gate">
|
|
514
|
-
<div class="panel">
|
|
515
|
-
<div class="mark" aria-hidden="true">HARNESS</div>
|
|
516
|
-
<h1>${title}</h1>
|
|
517
|
-
<p class="hint">输入 Host 启动日志中的 8 位${kind}访问 PIN。</p>
|
|
518
|
-
<form id="pin-form" novalidate>
|
|
519
|
-
<label class="field-label" for="access-pin">访问 PIN</label>
|
|
520
|
-
<input
|
|
521
|
-
id="access-pin"
|
|
522
|
-
name="pin"
|
|
523
|
-
type="text"
|
|
524
|
-
inputmode="numeric"
|
|
525
|
-
autocomplete="one-time-code"
|
|
526
|
-
enterkeyhint="go"
|
|
527
|
-
maxlength="8"
|
|
528
|
-
pattern="[0-9]{8}"
|
|
529
|
-
placeholder="········"
|
|
530
|
-
required
|
|
531
|
-
autofocus
|
|
532
|
-
spellcheck="false"
|
|
533
|
-
aria-describedby="pin-status"
|
|
534
|
-
>
|
|
535
|
-
<button id="pin-submit" type="submit">进入</button>
|
|
536
|
-
<output id="pin-status" role="status" aria-live="polite"></output>
|
|
537
|
-
</form>
|
|
538
|
-
</div>
|
|
539
|
-
</main>
|
|
540
|
-
<script src="/mobile-access/${mode}-pin.js" defer></script>
|
|
541
|
-
</body>
|
|
534
|
+
return `<!doctype html>
|
|
535
|
+
<html lang="zh-CN">
|
|
536
|
+
<head>
|
|
537
|
+
<meta charset="utf-8">
|
|
538
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,interactive-widget=resizes-content">
|
|
539
|
+
<meta name="color-scheme" content="light dark">
|
|
540
|
+
<meta name="theme-color" content="#151517" media="(prefers-color-scheme: dark)">
|
|
541
|
+
<meta name="theme-color" content="#f4f4f5" media="(prefers-color-scheme: light)">
|
|
542
|
+
<title>${title} · XRK Harness</title>
|
|
543
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
544
|
+
<style>${GATE_SHELL_CSS}</style>
|
|
545
|
+
<script>${gateThemeBootScript(preference)}</script>
|
|
546
|
+
</head>
|
|
547
|
+
<body>
|
|
548
|
+
<main class="gate">
|
|
549
|
+
<div class="panel">
|
|
550
|
+
<div class="mark" aria-hidden="true">HARNESS</div>
|
|
551
|
+
<h1>${title}</h1>
|
|
552
|
+
<p class="hint">输入 Host 启动日志中的 8 位${kind}访问 PIN。</p>
|
|
553
|
+
<form id="pin-form" novalidate>
|
|
554
|
+
<label class="field-label" for="access-pin">访问 PIN</label>
|
|
555
|
+
<input
|
|
556
|
+
id="access-pin"
|
|
557
|
+
name="pin"
|
|
558
|
+
type="text"
|
|
559
|
+
inputmode="numeric"
|
|
560
|
+
autocomplete="one-time-code"
|
|
561
|
+
enterkeyhint="go"
|
|
562
|
+
maxlength="8"
|
|
563
|
+
pattern="[0-9]{8}"
|
|
564
|
+
placeholder="········"
|
|
565
|
+
required
|
|
566
|
+
autofocus
|
|
567
|
+
spellcheck="false"
|
|
568
|
+
aria-describedby="pin-status"
|
|
569
|
+
>
|
|
570
|
+
<button id="pin-submit" type="submit">进入</button>
|
|
571
|
+
<output id="pin-status" role="status" aria-live="polite"></output>
|
|
572
|
+
</form>
|
|
573
|
+
</div>
|
|
574
|
+
</main>
|
|
575
|
+
<script src="/mobile-access/${mode}-pin.js" defer></script>
|
|
576
|
+
</body>
|
|
542
577
|
</html>`;
|
|
543
578
|
}
|
|
579
|
+
/** Build a LAN/WAN PIN gate document with the active appearance preference. */
|
|
580
|
+
export function buildPinPageHtml(mode, preference = "system") {
|
|
581
|
+
return pinPageHtml(mode, mode === "wan" ? "远程访问" : "局域网访问", preference);
|
|
582
|
+
}
|
|
544
583
|
function pinPageJs(mode) {
|
|
545
|
-
return `(() => {
|
|
546
|
-
const status = document.getElementById('pin-status')
|
|
547
|
-
const input = document.getElementById('access-pin')
|
|
548
|
-
const submit = document.getElementById('pin-submit')
|
|
549
|
-
const form = document.getElementById('pin-form')
|
|
550
|
-
let busy = false
|
|
551
|
-
|
|
552
|
-
const setStatus = (text, tone) => {
|
|
553
|
-
if (!status) return
|
|
554
|
-
status.textContent = text
|
|
555
|
-
if (tone) status.dataset.tone = tone
|
|
556
|
-
else delete status.dataset.tone
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
const setBusy = (next) => {
|
|
560
|
-
busy = next
|
|
561
|
-
if (submit) submit.disabled = next
|
|
562
|
-
if (input) input.readOnly = next
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
input?.addEventListener('input', () => {
|
|
566
|
-
const digits = (input.value || '').replace(/\\D/g, '').slice(0, 8)
|
|
567
|
-
if (input.value !== digits) input.value = digits
|
|
568
|
-
input.removeAttribute('aria-invalid')
|
|
569
|
-
if (status?.dataset.tone === 'error') setStatus('')
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
form?.addEventListener('submit', async (event) => {
|
|
573
|
-
event.preventDefault()
|
|
574
|
-
if (busy) return
|
|
575
|
-
const pin = (input?.value || '').trim()
|
|
576
|
-
if (!/^\\d{8}$/.test(pin)) {
|
|
577
|
-
input?.setAttribute('aria-invalid', 'true')
|
|
578
|
-
setStatus('请输入 8 位数字 PIN', 'error')
|
|
579
|
-
input?.focus()
|
|
580
|
-
input?.select()
|
|
581
|
-
return
|
|
582
|
-
}
|
|
583
|
-
setBusy(true)
|
|
584
|
-
setStatus('验证中…', 'busy')
|
|
585
|
-
try {
|
|
586
|
-
const response = await fetch('/mobile-access/auth/${mode}-pin', {
|
|
587
|
-
method: 'POST',
|
|
588
|
-
credentials: 'same-origin',
|
|
589
|
-
headers: { 'content-type': 'application/json' },
|
|
590
|
-
body: JSON.stringify({ pin }),
|
|
591
|
-
})
|
|
592
|
-
if (!response.ok) {
|
|
593
|
-
input?.setAttribute('aria-invalid', 'true')
|
|
594
|
-
setStatus('PIN 不正确,请核对启动日志后重试', 'error')
|
|
595
|
-
setBusy(false)
|
|
596
|
-
input?.focus()
|
|
597
|
-
input?.select()
|
|
598
|
-
return
|
|
599
|
-
}
|
|
600
|
-
try { sessionStorage.setItem('xrk.operatorAccess', '1') } catch {}
|
|
601
|
-
const next = new URLSearchParams(location.search).get('return') || '/'
|
|
602
|
-
location.replace(next)
|
|
603
|
-
} catch {
|
|
604
|
-
setStatus('无法连接 Host,请检查网络后重试', 'error')
|
|
605
|
-
setBusy(false)
|
|
606
|
-
}
|
|
607
|
-
})
|
|
608
|
-
|
|
609
|
-
queue?.focus()
|
|
610
|
-
})()
|
|
584
|
+
return `(() => {
|
|
585
|
+
const status = document.getElementById('pin-status')
|
|
586
|
+
const input = document.getElementById('access-pin')
|
|
587
|
+
const submit = document.getElementById('pin-submit')
|
|
588
|
+
const form = document.getElementById('pin-form')
|
|
589
|
+
let busy = false
|
|
590
|
+
|
|
591
|
+
const setStatus = (text, tone) => {
|
|
592
|
+
if (!status) return
|
|
593
|
+
status.textContent = text
|
|
594
|
+
if (tone) status.dataset.tone = tone
|
|
595
|
+
else delete status.dataset.tone
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const setBusy = (next) => {
|
|
599
|
+
busy = next
|
|
600
|
+
if (submit) submit.disabled = next
|
|
601
|
+
if (input) input.readOnly = next
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
input?.addEventListener('input', () => {
|
|
605
|
+
const digits = (input.value || '').replace(/\\D/g, '').slice(0, 8)
|
|
606
|
+
if (input.value !== digits) input.value = digits
|
|
607
|
+
input.removeAttribute('aria-invalid')
|
|
608
|
+
if (status?.dataset.tone === 'error') setStatus('')
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
form?.addEventListener('submit', async (event) => {
|
|
612
|
+
event.preventDefault()
|
|
613
|
+
if (busy) return
|
|
614
|
+
const pin = (input?.value || '').trim()
|
|
615
|
+
if (!/^\\d{8}$/.test(pin)) {
|
|
616
|
+
input?.setAttribute('aria-invalid', 'true')
|
|
617
|
+
setStatus('请输入 8 位数字 PIN', 'error')
|
|
618
|
+
input?.focus()
|
|
619
|
+
input?.select()
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
setBusy(true)
|
|
623
|
+
setStatus('验证中…', 'busy')
|
|
624
|
+
try {
|
|
625
|
+
const response = await fetch('/mobile-access/auth/${mode}-pin', {
|
|
626
|
+
method: 'POST',
|
|
627
|
+
credentials: 'same-origin',
|
|
628
|
+
headers: { 'content-type': 'application/json' },
|
|
629
|
+
body: JSON.stringify({ pin }),
|
|
630
|
+
})
|
|
631
|
+
if (!response.ok) {
|
|
632
|
+
input?.setAttribute('aria-invalid', 'true')
|
|
633
|
+
setStatus('PIN 不正确,请核对启动日志后重试', 'error')
|
|
634
|
+
setBusy(false)
|
|
635
|
+
input?.focus()
|
|
636
|
+
input?.select()
|
|
637
|
+
return
|
|
638
|
+
}
|
|
639
|
+
try { sessionStorage.setItem('xrk.operatorAccess', '1') } catch {}
|
|
640
|
+
const next = new URLSearchParams(location.search).get('return') || '/'
|
|
641
|
+
location.replace(next)
|
|
642
|
+
} catch {
|
|
643
|
+
setStatus('无法连接 Host,请检查网络后重试', 'error')
|
|
644
|
+
setBusy(false)
|
|
645
|
+
}
|
|
646
|
+
})
|
|
647
|
+
|
|
648
|
+
queue?.focus()
|
|
649
|
+
})()
|
|
611
650
|
`;
|
|
612
651
|
}
|
|
613
|
-
|
|
614
|
-
export const
|
|
652
|
+
/** Default PIN HTML (`system` → OS scheme). Prefer {@link buildPinPageHtml}. */
|
|
653
|
+
export const LAN_PIN_PAGE_HTML = buildPinPageHtml("lan", "system");
|
|
654
|
+
export const WAN_PIN_PAGE_HTML = buildPinPageHtml("wan", "system");
|
|
615
655
|
export const LAN_PIN_PAGE_JS = pinPageJs("lan");
|
|
616
656
|
export const WAN_PIN_PAGE_JS = pinPageJs("wan");
|
|
617
657
|
//# sourceMappingURL=mobile-access-auth.js.map
|