ultimate-jekyll-manager
Version:
Ultimate Jekyll dependency manager
214 lines (174 loc) • 6.14 kB
JavaScript
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
// Auth Module
export default function (Manager, options) {
// Shortcuts
const { webManager } = Manager;
// Get auth policy
const config = webManager.config.auth.config;
const policy = config.policy;
const authenticated = config.redirects.authenticated;
const unauthenticated = config.redirects.unauthenticated;
// Log policy
console.log('[Auth] policy:', policy, {
authenticated,
unauthenticated
});
// Track if we just signed out to avoid redirect loops
let justSignedOut = false;
// Setup Auth listener
try {
webManager.auth().listen({}, async (state) => {
const user = state.user;
const url = new URL(window.location.href);
const authReturnUrl = url.searchParams.get('authReturnUrl');
const authSignout = url.searchParams.get('authSignout');
// Log
console.log('[Auth] state changed:', state);
// Set user ID for analytics tracking
setAnalyticsUserId(user, webManager);
// Check if we're in the process of signing out
if (authSignout === 'true' && user) {
// Mark that we're about to sign out
justSignedOut = true;
return; // Let pages.js handle the signout
}
// Handle authentication state changes and page policies
if (user) {
// User is authenticated
// Check if this is a new user account (created in last 5 minutes) and send metadata
const accountAge = Date.now() - new Date(user.metadata.creationTime).getTime();
const fiveMinutes = 5 * 60 * 1000;
/* @dev-only:start */
{
// Log account age for debugging
const ageInMinutes = Math.floor(accountAge / 1000 / 60);
console.log('[Auth] Account age:', ageInMinutes, 'minutes');
}
/* @dev-only:end */
// Send user signup metadata if account is new
if (accountAge < fiveMinutes) {
await sendUserSignupMetadata(user, webManager);
}
// Check if page requires user to be unauthenticated (e.g., signin page)
if (policy === 'unauthenticated') {
// Check for authReturnUrl first (takes precedence)
if (authReturnUrl) {
redirect(authReturnUrl);
return;
}
// Otherwise redirect to default authenticated destination
redirect(authenticated);
}
} else {
// User is not authenticated
// If we just signed out and have authReturnUrl, stay on the page
if (justSignedOut && authReturnUrl) {
justSignedOut = false; // Reset flag
return; // Stay on current page to allow re-authentication
}
// Check if page requires authentication (e.g., account page)
if (policy === 'authenticated') {
redirect(unauthenticated, window.location.href);
}
}
});
} catch (e) {
console.warn('[Auth] Error setting up auth listener:', e);
return;
}
}
// Redirect function
function redirect(url, returnUrl) {
if (!url) {
return;
}
// Set the authReturnUrl to the current URL
const newURL = new URL(url, window.location.origin);
// Attach return URL
if (returnUrl) {
newURL.searchParams.set('authReturnUrl', returnUrl);
}
// Log
console.log('[Auth] Redirecting to:', newURL.href);
// Quit on testing
// return;
// Redirect to the new URL
window.location.href = newURL;
}
function setAnalyticsUserId(user, webManager) {
const userId = user?.uid;
const email = user?.email;
const metaPixelId = webManager.config.tracking['meta-pixel'];
// Short-circuit if no user
if (!userId) {
// Clear user ID when logged out
gtag('set', { user_id: null });
// Facebook Pixel - Clear advanced matching
fbq('init', metaPixelId, {});
// TikTok Pixel - Clear user data
ttq.identify({});
// Return early
return;
}
// Google Analytics 4 - Set user ID and user properties
gtag('set', {
user_id: userId,
user_properties: {
email_domain: email ? email.split('@')[1] : undefined
}
});
// Facebook Pixel - Set advanced matching with user data
fbq('init', metaPixelId, {
external_id: userId,
// em: email ? btoa(email.toLowerCase().trim()) : undefined,
em: email,
// ph: phone ? btoa(phone.trim()) : undefined
});
// TikTok Pixel - Identify user
ttq.identify({
external_id: userId,
// email: email ? btoa(email.toLowerCase().trim()) : undefined,
email: email,
// phone_number: phone ? btoa(phone.trim()) : undefined
});
}
// Send user metadata to server (affiliate, UTM params, etc.)
async function sendUserSignupMetadata(user, webManager) {
try {
// Get affiliate data from storage
const affiliateData = webManager.storage().get('marketing.affiliate', null);
const utmData = webManager.storage().get('marketing.utm', null);
const signupSent = webManager.storage().get('marketing.signupSent', false);
// Only proceed if we haven't sent signup metadata yet
if (signupSent) {
return;
}
// Build the payload
const payload = {
// @TODO: REMOVE ONCE LEGACY SERVER CODE IS GONE
affiliateCode: affiliateData?.code || '',
// New structure
affiliateData: affiliateData || {},
utmData: utmData || {},
context: webManager.utilities().getContext(),
};
// Get server API URL
const serverApiURL = `${webManager.getApiUrl()}/backend-manager`;
// Make API call to send signup metadata
const response = await authorizedFetch(serverApiURL, {
method: 'POST',
response: 'json',
body: {
command: 'user:sign-up',
payload: payload
}
});
// Log
console.log('[Auth] User metadata sent successfully:', response);
// Mark signup as sent (keep the marketing data for reference)
webManager.storage().set('marketing.signupSent', true);
} catch (error) {
console.error('[Auth] Error sending user metadata:', error);
// Don't throw - we don't want to block the signup flow
}
}