Skip consent API fetch for new visitors
When visitorId is null (no localStorage entry), the client now skips both the cookie check and the /consents/:id fetch that was always returning 404 for first-time visitors. The visitor ID is created lazily on first consent action instead. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -33,7 +33,7 @@ import type {
|
||||
Regulation,
|
||||
VisitorConsent,
|
||||
} from "./types";
|
||||
import { getOrCreateVisitorId } from "./visitor";
|
||||
import { getOrCreateVisitorId, getVisitorId } from "./visitor";
|
||||
|
||||
export type {
|
||||
BannerConfig,
|
||||
@@ -49,7 +49,7 @@ export type {
|
||||
export class CookieBannerClient {
|
||||
private readonly baseUrl: URL;
|
||||
private readonly bannerId: string;
|
||||
private readonly visitorId: string;
|
||||
private visitorId: string | null;
|
||||
private readonly lang: string;
|
||||
|
||||
private readonly integrations: ConsentIntegration[];
|
||||
@@ -68,7 +68,7 @@ export class CookieBannerClient {
|
||||
}
|
||||
this.baseUrl = new URL(base);
|
||||
this.bannerId = config.bannerId;
|
||||
this.visitorId = getOrCreateVisitorId(config.bannerId);
|
||||
this.visitorId = getVisitorId(config.bannerId);
|
||||
this.lang = detectLanguage(config.lang);
|
||||
this.integrations = createDefaultIntegrations();
|
||||
}
|
||||
@@ -97,50 +97,52 @@ export class CookieBannerClient {
|
||||
}
|
||||
this.startDetector(config);
|
||||
|
||||
const cookie = getConsentCookie();
|
||||
if (cookie && cookie.bid === this.bannerId && cookie.v === config.version && cookie.vid === this.visitorId) {
|
||||
this.consent = {
|
||||
visitor_id: cookie.vid,
|
||||
version: cookie.v,
|
||||
action: cookie.action,
|
||||
consent_data: cookie.data,
|
||||
created_at: "",
|
||||
};
|
||||
this._gpcApplied = cookie.action === "GPC";
|
||||
this.activate(cookie.data);
|
||||
void flush(this.bannerId);
|
||||
return;
|
||||
}
|
||||
if (this.visitorId) {
|
||||
const cookie = getConsentCookie();
|
||||
if (cookie && cookie.bid === this.bannerId && cookie.v === config.version && cookie.vid === this.visitorId) {
|
||||
this.consent = {
|
||||
visitor_id: cookie.vid,
|
||||
version: cookie.v,
|
||||
action: cookie.action,
|
||||
consent_data: cookie.data,
|
||||
created_at: "",
|
||||
};
|
||||
this._gpcApplied = cookie.action === "GPC";
|
||||
this.activate(cookie.data);
|
||||
void flush(this.bannerId);
|
||||
return;
|
||||
}
|
||||
|
||||
const consentUrl = new URL(
|
||||
`${this.bannerId}/consents/${this.visitorId}`,
|
||||
this.baseUrl,
|
||||
);
|
||||
const apiConsent = await fetchJSON<VisitorConsent>(consentUrl).catch(
|
||||
(err) => {
|
||||
if (err instanceof NotFoundError) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
|
||||
if (apiConsent && apiConsent.version === config.version) {
|
||||
this.consent = apiConsent;
|
||||
this._gpcApplied = apiConsent.action === "GPC";
|
||||
setConsentCookie(
|
||||
{
|
||||
bid: this.bannerId,
|
||||
v: apiConsent.version,
|
||||
vid: apiConsent.visitor_id,
|
||||
action: apiConsent.action,
|
||||
data: apiConsent.consent_data,
|
||||
},
|
||||
config.consent_expiry_days,
|
||||
const consentUrl = new URL(
|
||||
`${this.bannerId}/consents/${this.visitorId}`,
|
||||
this.baseUrl,
|
||||
);
|
||||
this.activate(apiConsent.consent_data);
|
||||
} else {
|
||||
this.consent = null;
|
||||
const apiConsent = await fetchJSON<VisitorConsent>(consentUrl).catch(
|
||||
(err) => {
|
||||
if (err instanceof NotFoundError) {
|
||||
return null;
|
||||
}
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
|
||||
if (apiConsent && apiConsent.version === config.version) {
|
||||
this.consent = apiConsent;
|
||||
this._gpcApplied = apiConsent.action === "GPC";
|
||||
setConsentCookie(
|
||||
{
|
||||
bid: this.bannerId,
|
||||
v: apiConsent.version,
|
||||
vid: apiConsent.visitor_id,
|
||||
action: apiConsent.action,
|
||||
data: apiConsent.consent_data,
|
||||
},
|
||||
config.consent_expiry_days,
|
||||
);
|
||||
this.activate(apiConsent.consent_data);
|
||||
} else {
|
||||
this.consent = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.consent && this.gpcDetected) {
|
||||
@@ -225,6 +227,13 @@ export class CookieBannerClient {
|
||||
this.recordConsent("CUSTOMIZE", consentData);
|
||||
}
|
||||
|
||||
private ensureVisitorId(): string {
|
||||
if (!this.visitorId) {
|
||||
this.visitorId = getOrCreateVisitorId(this.bannerId);
|
||||
}
|
||||
return this.visitorId;
|
||||
}
|
||||
|
||||
private recordConsent(
|
||||
action: ConsentAction,
|
||||
consentData: Record<string, boolean>,
|
||||
@@ -232,9 +241,10 @@ export class CookieBannerClient {
|
||||
this._gpcApplied = action === "GPC";
|
||||
|
||||
const cfg = this.config;
|
||||
const visitorId = this.ensureVisitorId();
|
||||
|
||||
this.consent = {
|
||||
visitor_id: this.visitorId,
|
||||
visitor_id: visitorId,
|
||||
version: cfg.version,
|
||||
action,
|
||||
consent_data: consentData,
|
||||
@@ -245,7 +255,7 @@ export class CookieBannerClient {
|
||||
{
|
||||
bid: this.bannerId,
|
||||
v: cfg.version,
|
||||
vid: this.visitorId,
|
||||
vid: visitorId,
|
||||
action,
|
||||
data: consentData,
|
||||
},
|
||||
@@ -256,7 +266,7 @@ export class CookieBannerClient {
|
||||
|
||||
const url = new URL(`${this.bannerId}/consents`, this.baseUrl);
|
||||
const body = {
|
||||
visitor_id: this.visitorId,
|
||||
visitor_id: visitorId,
|
||||
version: cfg.version,
|
||||
action,
|
||||
consent_data: consentData,
|
||||
|
||||
@@ -14,18 +14,23 @@
|
||||
|
||||
import { COOKIE_NAME } from "./cookie";
|
||||
|
||||
export function getOrCreateVisitorId(bannerId: string): string {
|
||||
export function getVisitorId(bannerId: string): string | null {
|
||||
const key = `${COOKIE_NAME}:${bannerId}:vid`;
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(key);
|
||||
if (stored) {
|
||||
return stored;
|
||||
}
|
||||
return localStorage.getItem(key);
|
||||
} catch {
|
||||
// localStorage unavailable
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function getOrCreateVisitorId(bannerId: string): string {
|
||||
const existing = getVisitorId(bannerId);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
const key = `${COOKIE_NAME}:${bannerId}:vid`;
|
||||
const array = new Uint8Array(16);
|
||||
crypto.getRandomValues(array);
|
||||
const id = Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
|
||||
|
||||
Reference in New Issue
Block a user