mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
899f1004a3
- Added NotificationsHub durable object to handle WebSocket connections for vault sync notifications. - Integrated SignalR protocol for message framing and communication. - Updated storage service methods to return revision date and user ID for vault sync notifications. - Enhanced existing handlers (attachments, ciphers, folders, sends, and import) to notify users of vault sync events. - Created new notifications handler for WebSocket negotiation and binding user IDs. - Updated frontend to establish WebSocket connection for receiving vault sync notifications. - Improved CORS headers to support new notification endpoints. - Bumped wrangler version in package.json to 4.71.0.
79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
const DEFAULT_DEVICE_NAME = 'Unknown device';
|
|
const DEFAULT_DEVICE_TYPE = 14;
|
|
|
|
function decodeBase64UrlUtf8(value: string): string | null {
|
|
try {
|
|
const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
const padding = normalized.length % 4;
|
|
const padded = padding === 0 ? normalized : normalized + '='.repeat(4 - padding);
|
|
const binary = atob(padded);
|
|
const bytes = new Uint8Array(binary.length);
|
|
for (let i = 0; i < binary.length; i++) {
|
|
bytes[i] = binary.charCodeAt(i);
|
|
}
|
|
return new TextDecoder().decode(bytes);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function normalizeDeviceIdentifier(value: string | undefined | null): string | null {
|
|
if (!value) return null;
|
|
const normalized = String(value).trim();
|
|
if (!normalized) return null;
|
|
return normalized.slice(0, 128);
|
|
}
|
|
|
|
function normalizeDeviceName(value: string | undefined | null): string {
|
|
const normalized = String(value || '').trim();
|
|
if (!normalized) return DEFAULT_DEVICE_NAME;
|
|
return normalized.slice(0, 128);
|
|
}
|
|
|
|
function parseDeviceType(value: string | number | undefined | null): number {
|
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
return Math.max(0, Math.floor(value));
|
|
}
|
|
const parsed = Number.parseInt(String(value || ''), 10);
|
|
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
|
|
return DEFAULT_DEVICE_TYPE;
|
|
}
|
|
|
|
export interface AuthRequestDeviceInfo {
|
|
deviceIdentifier: string | null;
|
|
deviceName: string;
|
|
deviceType: number;
|
|
}
|
|
|
|
export function readAuthRequestDeviceInfo(
|
|
body: Record<string, string | undefined>,
|
|
request: Request
|
|
): AuthRequestDeviceInfo {
|
|
const bodyIdentifier = body.deviceIdentifier || body.device_identifier;
|
|
const headerIdentifier = request.headers.get('X-Device-Identifier') || undefined;
|
|
const bodyName = body.deviceName || body.device_name;
|
|
const headerName = request.headers.get('X-Device-Name') || undefined;
|
|
const bodyType = body.deviceType || body.device_type;
|
|
const headerType = request.headers.get('Device-Type') || undefined;
|
|
|
|
return {
|
|
deviceIdentifier: normalizeDeviceIdentifier(bodyIdentifier || headerIdentifier),
|
|
deviceName: normalizeDeviceName(bodyName || headerName),
|
|
deviceType: parseDeviceType(bodyType || headerType),
|
|
};
|
|
}
|
|
|
|
export function readKnownDeviceProbe(request: Request): { email: string | null; deviceIdentifier: string | null } {
|
|
const encodedEmail = request.headers.get('X-Request-Email') || '';
|
|
const decodedEmail = decodeBase64UrlUtf8(encodedEmail);
|
|
const fallbackRawEmail = request.headers.get('X-Request-Email');
|
|
const email = (decodedEmail || fallbackRawEmail || '').trim().toLowerCase() || null;
|
|
const deviceIdentifier = normalizeDeviceIdentifier(request.headers.get('X-Device-Identifier'));
|
|
return { email, deviceIdentifier };
|
|
}
|
|
|
|
export function readActingDeviceIdentifier(request: Request): string | null {
|
|
return normalizeDeviceIdentifier(request.headers.get('X-NodeWarden-Acting-Device-Id'));
|
|
}
|
|
|