diff --git a/apps/web/lib/env.ts b/apps/web/lib/env.ts new file mode 100644 index 0000000..eb31703 --- /dev/null +++ b/apps/web/lib/env.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; +import { validateMintelEnv, mintelEnvSchema } from "@mintel/next-utils"; + +/** + * Environment variable schema for the main website. + * Extends the default Mintel environment schema. + */ +export const envSchema = z.object({ + ...mintelEnvSchema, + + // Project specific overrides or additions + AUTH_COOKIE_NAME: z.string().default("mintel_gatekeeper_session"), + + // Analytics provider toggle + NEXT_PUBLIC_ANALYTICS_PROVIDER: z + .enum(["plausible", "umami"]) + .default("plausible"), + + // Plausible specifics (to be standardized later if needed) + NEXT_PUBLIC_PLAUSIBLE_DOMAIN: z.string().default("mintel.me"), + NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL: z.string().url().optional(), +}); + +/** + * Validated environment object. + */ +export const env = validateMintelEnv(envSchema.shape); + +/** + * For legacy compatibility with existing code. + */ +export function getRawEnv() { + return env; +} diff --git a/apps/web/src/utils/analytics/index.ts b/apps/web/src/utils/analytics/index.ts index 08ad158..8bb53db 100644 --- a/apps/web/src/utils/analytics/index.ts +++ b/apps/web/src/utils/analytics/index.ts @@ -3,9 +3,13 @@ * Clean constructor-based dependency injection */ -import type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig } from './interfaces'; -import { PlausibleAdapter } from './plausible-adapter'; -import { UmamiAdapter, type UmamiConfig } from './umami-adapter'; +import type { + AnalyticsAdapter, + AnalyticsEvent, + AnalyticsConfig, +} from "./interfaces"; +import { PlausibleAdapter } from "./plausible-adapter"; +import { UmamiAdapter, type UmamiConfig } from "./umami-adapter"; export class AnalyticsService { private adapter: AnalyticsAdapter; @@ -30,7 +34,7 @@ export class AnalyticsService { if (this.adapter.page) { return this.adapter.page(path, props); } - return this.track({ name: 'Pageview', props: { path, ...props } }); + return this.track({ name: "Pageview", props: { path, ...props } }); } async identify(userId: string, traits?: Record): Promise { @@ -46,28 +50,34 @@ export class AnalyticsService { async trackOutboundLink(url: string, text: string): Promise { return this.track({ - name: 'Outbound Link', - props: { url, text } + name: "Outbound Link", + props: { url, text }, }); } async trackSearch(query: string, path: string): Promise { return this.track({ - name: 'Search', - props: { query, path } + name: "Search", + props: { query, path }, }); } - async trackPageLoad(loadTime: number, path: string, userAgent: string): Promise { + async trackPageLoad( + loadTime: number, + path: string, + userAgent: string, + ): Promise { return this.track({ - name: 'Page Load', - props: { loadTime: Math.round(loadTime), path, userAgent } + name: "Page Load", + props: { loadTime: Math.round(loadTime), path, userAgent }, }); } } // Factory functions -export function createPlausibleAnalytics(config: AnalyticsConfig): AnalyticsService { +export function createPlausibleAnalytics( + config: AnalyticsConfig, +): AnalyticsService { return new AnalyticsService(new PlausibleAdapter(config)); } @@ -78,19 +88,23 @@ export function createUmamiAnalytics(config: UmamiConfig): AnalyticsService { // Default singleton let defaultAnalytics: AnalyticsService | null = null; +import { env } from "@/lib/env"; + export function getDefaultAnalytics(): AnalyticsService { if (!defaultAnalytics) { - const provider = process.env.NEXT_PUBLIC_ANALYTICS_PROVIDER || 'plausible'; + const provider = env.NEXT_PUBLIC_ANALYTICS_PROVIDER; - if (provider === 'umami') { + if (provider === "umami") { defaultAnalytics = createUmamiAnalytics({ - websiteId: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || '', - hostUrl: process.env.NEXT_PUBLIC_UMAMI_HOST_URL, + websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || "", + hostUrl: env.UMAMI_API_ENDPOINT, }); } else { defaultAnalytics = createPlausibleAnalytics({ - domain: process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN || 'mintel.me', - scriptUrl: process.env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL || 'https://plausible.yourdomain.com/js/script.js' + domain: env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN, + scriptUrl: + env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL || + "https://plausible.yourdomain.com/js/script.js", }); } } @@ -98,10 +112,13 @@ export function getDefaultAnalytics(): AnalyticsService { } // Convenience function -export async function track(name: string, props?: Record): Promise { +export async function track( + name: string, + props?: Record, +): Promise { return getDefaultAnalytics().trackEvent(name, props); } // Re-export for advanced usage export type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig }; -export { PlausibleAdapter }; \ No newline at end of file +export { PlausibleAdapter };