refactor: standardize env and analytics using enhanced @mintel/next-utils
Some checks failed
Some checks failed
This commit is contained in:
34
apps/web/lib/env.ts
Normal file
34
apps/web/lib/env.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -3,9 +3,13 @@
|
|||||||
* Clean constructor-based dependency injection
|
* Clean constructor-based dependency injection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig } from './interfaces';
|
import type {
|
||||||
import { PlausibleAdapter } from './plausible-adapter';
|
AnalyticsAdapter,
|
||||||
import { UmamiAdapter, type UmamiConfig } from './umami-adapter';
|
AnalyticsEvent,
|
||||||
|
AnalyticsConfig,
|
||||||
|
} from "./interfaces";
|
||||||
|
import { PlausibleAdapter } from "./plausible-adapter";
|
||||||
|
import { UmamiAdapter, type UmamiConfig } from "./umami-adapter";
|
||||||
|
|
||||||
export class AnalyticsService {
|
export class AnalyticsService {
|
||||||
private adapter: AnalyticsAdapter;
|
private adapter: AnalyticsAdapter;
|
||||||
@@ -30,7 +34,7 @@ export class AnalyticsService {
|
|||||||
if (this.adapter.page) {
|
if (this.adapter.page) {
|
||||||
return this.adapter.page(path, props);
|
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<string, any>): Promise<void> {
|
async identify(userId: string, traits?: Record<string, any>): Promise<void> {
|
||||||
@@ -46,28 +50,34 @@ export class AnalyticsService {
|
|||||||
|
|
||||||
async trackOutboundLink(url: string, text: string): Promise<void> {
|
async trackOutboundLink(url: string, text: string): Promise<void> {
|
||||||
return this.track({
|
return this.track({
|
||||||
name: 'Outbound Link',
|
name: "Outbound Link",
|
||||||
props: { url, text }
|
props: { url, text },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async trackSearch(query: string, path: string): Promise<void> {
|
async trackSearch(query: string, path: string): Promise<void> {
|
||||||
return this.track({
|
return this.track({
|
||||||
name: 'Search',
|
name: "Search",
|
||||||
props: { query, path }
|
props: { query, path },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async trackPageLoad(loadTime: number, path: string, userAgent: string): Promise<void> {
|
async trackPageLoad(
|
||||||
|
loadTime: number,
|
||||||
|
path: string,
|
||||||
|
userAgent: string,
|
||||||
|
): Promise<void> {
|
||||||
return this.track({
|
return this.track({
|
||||||
name: 'Page Load',
|
name: "Page Load",
|
||||||
props: { loadTime: Math.round(loadTime), path, userAgent }
|
props: { loadTime: Math.round(loadTime), path, userAgent },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory functions
|
// Factory functions
|
||||||
export function createPlausibleAnalytics(config: AnalyticsConfig): AnalyticsService {
|
export function createPlausibleAnalytics(
|
||||||
|
config: AnalyticsConfig,
|
||||||
|
): AnalyticsService {
|
||||||
return new AnalyticsService(new PlausibleAdapter(config));
|
return new AnalyticsService(new PlausibleAdapter(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,19 +88,23 @@ export function createUmamiAnalytics(config: UmamiConfig): AnalyticsService {
|
|||||||
// Default singleton
|
// Default singleton
|
||||||
let defaultAnalytics: AnalyticsService | null = null;
|
let defaultAnalytics: AnalyticsService | null = null;
|
||||||
|
|
||||||
|
import { env } from "@/lib/env";
|
||||||
|
|
||||||
export function getDefaultAnalytics(): AnalyticsService {
|
export function getDefaultAnalytics(): AnalyticsService {
|
||||||
if (!defaultAnalytics) {
|
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({
|
defaultAnalytics = createUmamiAnalytics({
|
||||||
websiteId: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || '',
|
websiteId: env.NEXT_PUBLIC_UMAMI_WEBSITE_ID || "",
|
||||||
hostUrl: process.env.NEXT_PUBLIC_UMAMI_HOST_URL,
|
hostUrl: env.UMAMI_API_ENDPOINT,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
defaultAnalytics = createPlausibleAnalytics({
|
defaultAnalytics = createPlausibleAnalytics({
|
||||||
domain: process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN || 'mintel.me',
|
domain: env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN,
|
||||||
scriptUrl: process.env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL || 'https://plausible.yourdomain.com/js/script.js'
|
scriptUrl:
|
||||||
|
env.NEXT_PUBLIC_PLAUSIBLE_SCRIPT_URL ||
|
||||||
|
"https://plausible.yourdomain.com/js/script.js",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,10 +112,13 @@ export function getDefaultAnalytics(): AnalyticsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convenience function
|
// Convenience function
|
||||||
export async function track(name: string, props?: Record<string, any>): Promise<void> {
|
export async function track(
|
||||||
|
name: string,
|
||||||
|
props?: Record<string, any>,
|
||||||
|
): Promise<void> {
|
||||||
return getDefaultAnalytics().trackEvent(name, props);
|
return getDefaultAnalytics().trackEvent(name, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export for advanced usage
|
// Re-export for advanced usage
|
||||||
export type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig };
|
export type { AnalyticsAdapter, AnalyticsEvent, AnalyticsConfig };
|
||||||
export { PlausibleAdapter };
|
export { PlausibleAdapter };
|
||||||
|
|||||||
Reference in New Issue
Block a user