mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
here goes nothin'
This commit is contained in:
Generated
+7
@@ -8,6 +8,7 @@
|
||||
"name": "webui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@thumbmarkjs/thumbmarkjs": "^1.10.0",
|
||||
"midi-file": "^1.2.4",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
@@ -1417,6 +1418,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@thumbmarkjs/thumbmarkjs": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@thumbmarkjs/thumbmarkjs/-/thumbmarkjs-1.10.0.tgz",
|
||||
"integrity": "sha512-mmIjivwI76Jm1to/VsEzxX+FNRD38HBHV37LmlF6Gg6psclifkVO01KV7UZr+i2d9IboqsTcW33vD1vr3meCeA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@thumbmarkjs/thumbmarkjs": "^1.10.0",
|
||||
"midi-file": "^1.2.4",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
||||
@@ -337,8 +337,8 @@ export function SessionProvider({ children }) {
|
||||
const actions = useMemo(
|
||||
() => ({
|
||||
login: (username, password) => emitWithAck('auth:login', { username, password }),
|
||||
identifySession: ({ cookieUserId, nickname, overseerEnabled, identitySurface } = {}) =>
|
||||
emitWithAck('session:identify', { cookieUserId, nickname, overseerEnabled, identitySurface }),
|
||||
identifySession: ({ cookieUserId, fingerprintId, nickname, overseerEnabled, identitySurface } = {}) =>
|
||||
emitWithAck('session:identify', { cookieUserId, fingerprintId, nickname, overseerEnabled, identitySurface }),
|
||||
setRole: (role) => emitWithAck('session:setRole', { role }),
|
||||
requestControl: (roverId, options = {}) =>
|
||||
emitWithAck('session:requestControl', { roverId, ...options }),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useSessionActions, useSessionSelector } from '../context/SessionContext.jsx';
|
||||
import { useSocket } from '../context/SocketContext.jsx';
|
||||
import { getBrowserFingerprintId } from '../lib/browserFingerprint.js';
|
||||
import { useSettingsNamespace } from '../settings/index.js';
|
||||
|
||||
export default function useUserIdentitySync({ identitySurface = 'passive' } = {}) {
|
||||
@@ -21,6 +22,7 @@ export default function useUserIdentitySync({ identitySurface = 'passive' } = {}
|
||||
const inFlightRef = useRef(false);
|
||||
const lastAckSocketRef = useRef(null);
|
||||
const retryTimerRef = useRef(null);
|
||||
const fingerprintRef = useRef('');
|
||||
|
||||
const ready =
|
||||
identityStatus === 'ready' && profileStatus === 'ready' && overseerPreferenceStatus === 'ready';
|
||||
@@ -40,6 +42,14 @@ export default function useUserIdentitySync({ identitySurface = 'passive' } = {}
|
||||
if (!ready || !connected || !socket?.id || inFlightRef.current) return;
|
||||
inFlightRef.current = true;
|
||||
try {
|
||||
if (!fingerprintRef.current) {
|
||||
/*
|
||||
The portable cookie key is still the cross-device identity signal.
|
||||
Thumbmark adds a same-device signal that survives cookie clearing, so
|
||||
both are sent together whenever the heartbeat identifies this socket.
|
||||
*/
|
||||
fingerprintRef.current = await getBrowserFingerprintId();
|
||||
}
|
||||
/*
|
||||
Every route shares the same persisted identity key, but only the main
|
||||
driver page should trigger duplicate-tab prevention. Sending the surface
|
||||
@@ -48,6 +58,7 @@ export default function useUserIdentitySync({ identitySurface = 'passive' } = {}
|
||||
*/
|
||||
const resp = await identifySession({
|
||||
cookieUserId,
|
||||
fingerprintId: fingerprintRef.current,
|
||||
nickname,
|
||||
overseerEnabled,
|
||||
identitySurface: normalizedIdentitySurface,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Browser Fingerprint
|
||||
// Purpose: Wraps Thumbmark behind one app-owned helper so identity sync does not
|
||||
// depend on the third-party package shape throughout the UI.
|
||||
// Scope: Produces a normalized device fingerprint signal for the server identity service.
|
||||
import { getFingerprint } from '@thumbmarkjs/thumbmarkjs';
|
||||
|
||||
let fingerprintPromise = null;
|
||||
|
||||
function normalizeThumbmark(value) {
|
||||
const raw = String(value || '').trim().toLowerCase();
|
||||
if (!raw) return '';
|
||||
|
||||
/*
|
||||
The server treats the prefix as part of the signal format so different
|
||||
fingerprint providers can coexist later without hash-space ambiguity.
|
||||
*/
|
||||
const body = raw.startsWith('tm_') ? raw.slice(3) : raw;
|
||||
const safeBody = body.replace(/[^a-z0-9_-]/g, '');
|
||||
return safeBody ? `tm_${safeBody}` : '';
|
||||
}
|
||||
|
||||
export async function getBrowserFingerprintId() {
|
||||
if (typeof window === 'undefined') return '';
|
||||
if (!fingerprintPromise) {
|
||||
fingerprintPromise = Promise.resolve()
|
||||
.then(() => getFingerprint())
|
||||
.then(normalizeThumbmark)
|
||||
.catch((error) => {
|
||||
console.warn('Failed to calculate browser fingerprint', error); // eslint-disable-line no-console
|
||||
return '';
|
||||
});
|
||||
}
|
||||
return fingerprintPromise;
|
||||
}
|
||||
Reference in New Issue
Block a user