import { readItems, readCollections } from '@directus/sdk'; import { createMintelDirectusClient, ensureDirectusAuthenticated } from '@mintel/next-utils'; import { config } from './config'; import { getServerAppServices } from './services/create-services.server'; /** * Directus Schema Definitions */ export interface Schema { products: any[]; categories: any[]; contact_submissions: any[]; product_requests: any[]; translations: any[]; categories_link: any[]; } // Initialize client using Mintel standards (environment-aware) const client = createMintelDirectusClient(); /** * Helper to determine if we should show detailed errors */ const shouldShowDevErrors = config.isTesting || config.isDevelopment; /** * Genericizes error messages for production/staging */ function formatError(error: any) { if (shouldShowDevErrors) { return error.errors?.[0]?.message || error.message || 'An unexpected error occurred.'; } return 'A system error occurred. Our team has been notified.'; } export async function ensureAuthenticated() { try { await ensureDirectusAuthenticated(client); } catch (e: any) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(e, { part: 'directus_auth' }); } console.error(`Failed to authenticate with Directus:`, e.message); throw e; } } /** * Maps the new translation-based schema back to the application's Product interface */ function mapDirectusProduct(item: any, locale: string): any { const langCode = locale === 'en' ? 'en-US' : 'de-DE'; const translation = item.translations?.find((t: any) => t.languages_code === langCode) || item.translations?.[0] || {}; return { id: item.id, sku: item.sku, title: translation.name || '', description: translation.description || '', content: translation.content || '', technicalData: { technicalItems: translation.technical_items || [], voltageTables: translation.voltage_tables || [], }, locale: locale, // Use standardized proxy path for assets to avoid CORS data_sheet_url: item.data_sheet ? `/api/directus/assets/${item.data_sheet}` : null, categories: (item.categories_link || []) .map((c: any) => c.categories_id?.translations?.[0]?.name) .filter(Boolean), }; } export async function getProducts(locale: string = 'de') { await ensureAuthenticated(); try { const items = await client.request( readItems('products', { fields: ['*', 'translations.*', 'categories_link.categories_id.translations.name'], }), ); return items.map((item) => mapDirectusProduct(item, locale)); } catch (error) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(error, { part: 'directus_get_products' }); } console.error('Error fetching products:', error); return []; } } export async function getProductBySlug(slug: string, locale: string = 'de') { await ensureAuthenticated(); const langCode = locale === 'en' ? 'en-US' : 'de-DE'; try { const items = await client.request( readItems('products', { filter: { translations: { slug: { _eq: slug }, languages_code: { _eq: langCode }, }, }, fields: ['*', 'translations.*', 'categories_link.categories_id.translations.name'], limit: 1, }), ); if (!items || items.length === 0) return null; return mapDirectusProduct(items[0], locale); } catch (error) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(error, { part: 'directus_get_product_by_slug', slug, }); } console.error(`Error fetching product ${slug}:`, error); return null; } } export async function checkHealth() { try { // 1. Connectivity & Auth Check try { await ensureAuthenticated(); await client.request(readCollections()); } catch (e: any) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(e, { part: 'directus_health_auth' }); } console.error('Directus authentication or collection-read failed during health check:', e); return { status: 'error', message: shouldShowDevErrors ? `Directus Health Error: ${e.message || 'Unknown'}` : 'CMS is currently unavailable due to an internal authentication or connection error.', code: e.code || 'HEALTH_AUTH_FAILED', details: shouldShowDevErrors ? { message: e.message, code: e.code, errors: e.errors } : undefined, }; } // 2. Schema check (does the contact_submissions table exist?) try { await client.request(readItems('contact_submissions', { limit: 1 })); } catch (e: any) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(e, { part: 'directus_health_schema' }); } if ( e.message?.includes('does not exist') || e.code === 'INVALID_PAYLOAD' || e.status === 404 ) { return { status: 'error', message: shouldShowDevErrors ? `The "contact_submissions" collection is missing or inaccessible. Error: ${e.message || 'Unknown'}` : 'Required data structures are currently unavailable.', code: 'SCHEMA_MISSING', }; } return { status: 'error', message: shouldShowDevErrors ? `Schema error: ${e.errors?.[0]?.message || e.message || 'Unknown error'}` : 'The data schema is currently misconfigured.', code: 'SCHEMA_ERROR', }; } return { status: 'ok', message: 'Directus is reachable and responding.' }; } catch (error: any) { if (typeof window === 'undefined') { getServerAppServices().errors.captureException(error, { part: 'directus_health_critical' }); } console.error('Directus health check failed with unexpected error:', error); return { status: 'error', message: formatError(error), code: error.code || 'UNKNOWN', }; } } export default client;