186 lines
4.4 KiB
TypeScript
186 lines
4.4 KiB
TypeScript
import type { AppPageDescriptor } from '../../store/appStore/types';
|
|
import { isPreviewRuntime } from './previewRuntime';
|
|
|
|
export const CLIENT_ID_STORAGE_KEY = 'work-app.visitor.client-id';
|
|
|
|
function readStorageValue(storage: Storage | null | undefined, key: string) {
|
|
try {
|
|
return storage?.getItem(key)?.trim() ?? '';
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function writeStorageValue(storage: Storage | null | undefined, key: string, value: string) {
|
|
try {
|
|
if (value) {
|
|
storage?.setItem(key, value);
|
|
} else {
|
|
storage?.removeItem(key);
|
|
}
|
|
} catch {
|
|
// Ignore storage failures in restricted preview runtimes.
|
|
}
|
|
}
|
|
|
|
function removeStorageValue(storage: Storage | null | undefined, key: string) {
|
|
try {
|
|
storage?.removeItem(key);
|
|
} catch {
|
|
// Ignore storage failures in restricted preview runtimes.
|
|
}
|
|
}
|
|
|
|
function getClientStorage() {
|
|
if (typeof window === 'undefined') {
|
|
return null;
|
|
}
|
|
|
|
if (isPreviewRuntime()) {
|
|
return {
|
|
key: CLIENT_ID_STORAGE_KEY,
|
|
primaryStorage: window.localStorage,
|
|
legacyStorage: window.sessionStorage,
|
|
};
|
|
}
|
|
|
|
return {
|
|
key: CLIENT_ID_STORAGE_KEY,
|
|
primaryStorage: window.localStorage,
|
|
legacyStorage: null,
|
|
};
|
|
}
|
|
|
|
function generateFallbackClientId() {
|
|
return `client-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
}
|
|
|
|
function generateClientId() {
|
|
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
return crypto.randomUUID();
|
|
}
|
|
|
|
return generateFallbackClientId();
|
|
}
|
|
|
|
export function getClientId() {
|
|
const storageConfig = getClientStorage();
|
|
|
|
if (!storageConfig) {
|
|
return '';
|
|
}
|
|
|
|
const savedClientId = readStorageValue(storageConfig.primaryStorage, storageConfig.key);
|
|
|
|
if (savedClientId) {
|
|
return savedClientId;
|
|
}
|
|
|
|
const legacyClientId = readStorageValue(storageConfig.legacyStorage, storageConfig.key);
|
|
|
|
if (legacyClientId) {
|
|
writeStorageValue(storageConfig.primaryStorage, storageConfig.key, legacyClientId);
|
|
removeStorageValue(storageConfig.legacyStorage, storageConfig.key);
|
|
return legacyClientId;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
export function clearClientId() {
|
|
const storageConfig = getClientStorage();
|
|
|
|
if (!storageConfig) {
|
|
return;
|
|
}
|
|
|
|
removeStorageValue(storageConfig.primaryStorage, storageConfig.key);
|
|
removeStorageValue(storageConfig.legacyStorage, storageConfig.key);
|
|
}
|
|
|
|
export function getOrCreateClientId() {
|
|
const existingClientId = getClientId();
|
|
|
|
if (existingClientId) {
|
|
return existingClientId;
|
|
}
|
|
|
|
const storageConfig = getClientStorage();
|
|
|
|
if (!storageConfig) {
|
|
return '';
|
|
}
|
|
|
|
const nextClientId = generateClientId();
|
|
writeStorageValue(storageConfig.primaryStorage, storageConfig.key, nextClientId);
|
|
removeStorageValue(storageConfig.legacyStorage, storageConfig.key);
|
|
return nextClientId;
|
|
}
|
|
|
|
export function appendClientIdHeader(headersInit?: HeadersInit) {
|
|
const headers = new Headers(headersInit);
|
|
const clientId = getOrCreateClientId();
|
|
|
|
if (clientId && !headers.has('X-Client-Id')) {
|
|
headers.set('X-Client-Id', clientId);
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
const { origin, hostname } = window.location;
|
|
|
|
if (origin && !headers.has('X-App-Origin')) {
|
|
headers.set('X-App-Origin', origin);
|
|
}
|
|
|
|
if (hostname && !headers.has('X-App-Domain')) {
|
|
headers.set('X-App-Domain', hostname);
|
|
}
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
export function buildTrackedPageUrl(page: AppPageDescriptor) {
|
|
if (typeof window === 'undefined') {
|
|
return '';
|
|
}
|
|
|
|
const url = new URL(window.location.href);
|
|
|
|
if (page.topMenu === 'plans') {
|
|
url.searchParams.set('topMenu', 'plans');
|
|
url.searchParams.set('planFilter', page.section);
|
|
url.searchParams.delete('planSection');
|
|
} else {
|
|
url.searchParams.set('topMenu', page.topMenu);
|
|
url.searchParams.delete('planSection');
|
|
url.searchParams.delete('planFilter');
|
|
}
|
|
|
|
if (page.topMenu === 'docs') {
|
|
url.searchParams.set('docsSection', page.section);
|
|
} else {
|
|
url.searchParams.delete('docsSection');
|
|
}
|
|
|
|
if (page.topMenu === 'apis') {
|
|
url.searchParams.set('apiSection', page.section);
|
|
} else {
|
|
url.searchParams.delete('apiSection');
|
|
}
|
|
|
|
if (page.topMenu === 'chat') {
|
|
url.searchParams.set('chatSection', page.section);
|
|
} else {
|
|
url.searchParams.delete('chatSection');
|
|
}
|
|
|
|
if (page.topMenu === 'play') {
|
|
url.searchParams.set('playSection', page.section);
|
|
} else {
|
|
url.searchParams.delete('playSection');
|
|
}
|
|
|
|
return url.toString();
|
|
}
|