engresearch's picture
Upload folder using huggingface_hub
7f88bdf verified
/** Monitoring and analytics integration for TenderHub. */
import type { User } from "@supabase/supabase-js";
// Sentry configuration
const getSentryDsn = () => process.env.NEXT_PUBLIC_SENTRY_DSN;
const isSentryEnabled = () => false; // Disabled for now: !!getSentryDsn() && process.env.NEXT_PUBLIC_SENTRY_ENABLED !== "false";
// PostHog configuration
const getPosthogKey = () => process.env.NEXT_PUBLIC_POSTHOG_KEY;
const getPosthogHost = () => process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://app.posthog.com";
const isPosthogEnabled = () => false; // Disabled for now: !!getPosthogKey();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let sentryModule: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let posthogClient: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function getSentry(): Promise<any> {
if (!isSentryEnabled()) return null;
if (!sentryModule) {
try {
// @ts-ignore -- optional dependency, loaded at runtime
sentryModule = await import("@sentry/nextjs");
} catch {
console.warn("Sentry not installed");
return null;
}
}
return sentryModule;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async function getPostHog(): Promise<any> {
if (!isPosthogEnabled()) return null;
if (!posthogClient) {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// @ts-ignore -- optional dependency, loaded at runtime
const posthog = await import("posthog-js") as any;
posthogClient = posthog.default.init(getPosthogKey()!, {
api_host: getPosthogHost(),
capture_pageview: true,
capture_pageleave: true,
persistence: "localStorage",
loaded: (ph: { opt_out_capturing: () => void }) => {
if (process.env.NODE_ENV === "development") {
ph.opt_out_capturing();
}
},
});
} catch {
console.warn("PostHog not installed");
return null;
}
}
return posthogClient;
}
// Error tracking
export async function captureException(error: Error, context?: Record<string, unknown>): Promise<void> {
console.error("[Error]", error, context);
const sentry = await getSentry();
if (sentry) {
if (context) {
sentry.withScope((scope: any) => {
Object.entries(context).forEach(([key, value]) => {
scope.setExtra(key, value);
});
scope.captureException(error);
});
} else {
sentry.captureException(error);
}
}
}
export async function captureMessage(message: string, level: "info" | "warning" | "error" = "info", context?: Record<string, unknown>): Promise<void> {
console.log(`[${level.toUpperCase()}]`, message, context);
const sentry = await getSentry();
if (sentry) {
if (context) {
sentry.withScope((scope: any) => {
Object.entries(context).forEach(([key, value]) => {
scope.setExtra(key, value);
});
scope.captureMessage(message, level);
});
} else {
sentry.captureMessage(message, level);
}
}
}
// User identification
export async function identifyUser(user: User, organizationId?: string): Promise<void> {
const posthog = await getPostHog();
if (posthog) {
posthog.identify(user.id, {
email: user.email,
organizationId,
});
}
const sentry = await getSentry();
if (sentry) {
sentry.setUser({
id: user.id,
email: user.email,
});
if (organizationId) {
sentry.setTag("organizationId", organizationId);
}
}
}
export async function resetUser(): Promise<void> {
const posthog = await getPostHog();
if (posthog) {
posthog.reset();
}
const sentry = await getSentry();
if (sentry) {
sentry.setUser(null);
}
}
// Event tracking
export async function trackEvent(
eventName: string,
properties?: Record<string, unknown>
): Promise<void> {
console.log("[Analytics]", eventName, properties);
const posthog = await getPostHog();
if (posthog) {
posthog.capture(eventName, properties);
}
}
// Product analytics events
export const AnalyticsEvents = {
// Auth
USER_SIGNUP: "user_signup",
USER_LOGIN: "user_login",
USER_LOGOUT: "user_logout",
// Tenders
TENDER_UPLOADED: "tender_uploaded",
TENDER_PROCESSING_STARTED: "tender_processing_started",
TENDER_ANALYSIS_VIEWED: "tender_analysis_viewed",
TENDER_UNLOCKED: "tender_unlocked",
// Drafts
DRAFT_GENERATED: "draft_generated",
DRAFT_EXPORTED: "draft_exported",
DRAFT_DOWNLOADED: "draft_downloaded",
// Payments
PAYMENT_INITIATED: "payment_initiated",
PAYMENT_COMPLETED: "payment_completed",
PAYMENT_FAILED: "payment_failed",
// Company Profile
PROFILE_COMPLETED: "profile_completed",
PROFILE_UPDATED: "profile_updated",
// Errors
EXTRACTION_FAILED: "extraction_failed",
DRAFT_GENERATION_FAILED: "draft_generation_failed",
UPLOAD_FAILED: "upload_failed",
} as const;
// Breadcrumbs for debugging
export async function addBreadcrumb(
message: string,
category?: string,
data?: Record<string, unknown>
): Promise<void> {
const sentry = await getSentry();
if (sentry) {
sentry.addBreadcrumb({
message,
category,
data,
level: "info",
});
}
}
// Performance monitoring
export async function startSpanSafe(
name: string,
op: string
): Promise<{ finish: () => void } | null> {
const sentry = await getSentry();
if (!sentry) return null;
// Use startSpan if available (Sentry v8+), otherwise no-op
if (typeof sentry.startSpan === "function") {
try {
sentry.startSpan({ name, op }, () => {});
} catch {
// Span API may not be available in all builds
}
}
// Return a no-op handle for backward compatibility
return { finish: () => {} };
}
// Initialize monitoring on app start
export async function initMonitoring(): Promise<void> {
if (isSentryEnabled()) {
await getSentry();
console.log("[Monitoring] Sentry initialized");
}
if (isPosthogEnabled()) {
await getPostHog();
console.log("[Monitoring] PostHog initialized");
}
}