feat: Add deployment target configuration for environment-specific settings, Sentry integration, and CMS notice logic.
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Failing after 56s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Has been skipped
Build & Deploy KLZ Cables / 🏗️ Build App (push) Has been skipped
Build & Deploy KLZ Cables / 🏗️ Build Gatekeeper (push) Has been skipped
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been skipped
Build & Deploy KLZ Cables / ⚡ PageSpeed (push) Has been skipped
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 2s
Some checks failed
Build & Deploy KLZ Cables / 🔍 Prepare Environment (push) Failing after 56s
Build & Deploy KLZ Cables / 🧪 Quality Assurance (push) Has been skipped
Build & Deploy KLZ Cables / 🏗️ Build App (push) Has been skipped
Build & Deploy KLZ Cables / 🏗️ Build Gatekeeper (push) Has been skipped
Build & Deploy KLZ Cables / 🚀 Deploy (push) Has been skipped
Build & Deploy KLZ Cables / ⚡ PageSpeed (push) Has been skipped
Build & Deploy KLZ Cables / 🔔 Notifications (push) Successful in 2s
This commit is contained in:
@@ -13,11 +13,15 @@ let memoizedConfig: ReturnType<typeof createConfig> | undefined;
|
||||
function createConfig() {
|
||||
const env = envSchema.parse(getRawEnv());
|
||||
|
||||
const target = env.NEXT_PUBLIC_TARGET || env.TARGET;
|
||||
|
||||
return {
|
||||
env: env.NODE_ENV,
|
||||
isProduction: env.NODE_ENV === 'production',
|
||||
isDevelopment: env.NODE_ENV === 'development',
|
||||
isTest: env.NODE_ENV === 'test',
|
||||
target,
|
||||
isProduction: target === 'production',
|
||||
isStaging: target === 'staging',
|
||||
isTesting: target === 'testing',
|
||||
isDevelopment: target === 'development' || env.NODE_ENV === 'development',
|
||||
|
||||
baseUrl: env.NEXT_PUBLIC_BASE_URL,
|
||||
|
||||
@@ -87,15 +91,21 @@ export const config = {
|
||||
get env() {
|
||||
return getConfig().env;
|
||||
},
|
||||
get target() {
|
||||
return getConfig().target;
|
||||
},
|
||||
get isProduction() {
|
||||
return getConfig().isProduction;
|
||||
},
|
||||
get isStaging() {
|
||||
return getConfig().isStaging;
|
||||
},
|
||||
get isTesting() {
|
||||
return getConfig().isTesting;
|
||||
},
|
||||
get isDevelopment() {
|
||||
return getConfig().isDevelopment;
|
||||
},
|
||||
get isTest() {
|
||||
return getConfig().isTest;
|
||||
},
|
||||
get baseUrl() {
|
||||
return getConfig().baseUrl;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createDirectus, rest, authentication, readItems, readCollections } from '@directus/sdk';
|
||||
import { config } from './config';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
const { url, adminEmail, password, token, proxyPath, internalUrl } = config.directus;
|
||||
|
||||
@@ -8,6 +9,21 @@ const effectiveUrl = typeof window === 'undefined' && internalUrl ? internalUrl
|
||||
|
||||
const client = createDirectus(effectiveUrl).with(rest()).with(authentication());
|
||||
|
||||
/**
|
||||
* 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.message || 'An unexpected error occurred.';
|
||||
}
|
||||
return 'A system error occurred. Our team has been notified.';
|
||||
}
|
||||
|
||||
export async function ensureAuthenticated() {
|
||||
if (token) {
|
||||
client.setToken(token);
|
||||
@@ -17,6 +33,7 @@ export async function ensureAuthenticated() {
|
||||
try {
|
||||
await client.login(adminEmail, password);
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
console.error('Failed to authenticate with Directus:', e);
|
||||
}
|
||||
}
|
||||
@@ -61,6 +78,7 @@ export async function getProducts(locale: string = 'de') {
|
||||
);
|
||||
return items.map((item) => mapDirectusProduct(item, locale));
|
||||
} catch (error) {
|
||||
Sentry.captureException(error);
|
||||
console.error('Error fetching products:', error);
|
||||
return [];
|
||||
}
|
||||
@@ -86,6 +104,7 @@ export async function getProductBySlug(slug: string, locale: string = 'de') {
|
||||
if (!items || items.length === 0) return null;
|
||||
return mapDirectusProduct(items[0], locale);
|
||||
} catch (error) {
|
||||
Sentry.captureException(error);
|
||||
console.error(`Error fetching product ${slug}:`, error);
|
||||
return null;
|
||||
}
|
||||
@@ -98,13 +117,15 @@ export async function checkHealth() {
|
||||
await ensureAuthenticated();
|
||||
await client.request(readCollections());
|
||||
} catch (e: any) {
|
||||
Sentry.captureException(e);
|
||||
console.error('Directus authentication failed during health check:', e);
|
||||
return {
|
||||
status: 'error',
|
||||
message:
|
||||
'Authentication failed. Check your DIRECTUS_ADMIN_EMAIL and DIRECTUS_ADMIN_PASSWORD.',
|
||||
message: shouldShowDevErrors
|
||||
? 'Authentication failed. Check your DIRECTUS_ADMIN_EMAIL and DIRECTUS_ADMIN_PASSWORD.'
|
||||
: 'CMS is currently unavailable due to an internal authentication error.',
|
||||
code: 'AUTH_FAILED',
|
||||
details: e.message,
|
||||
details: shouldShowDevErrors ? e.message : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -112,6 +133,7 @@ export async function checkHealth() {
|
||||
try {
|
||||
await client.request(readItems('products', { limit: 1 }));
|
||||
} catch (e: any) {
|
||||
Sentry.captureException(e);
|
||||
if (
|
||||
e.message?.includes('does not exist') ||
|
||||
e.code === 'INVALID_PAYLOAD' ||
|
||||
@@ -119,23 +141,28 @@ export async function checkHealth() {
|
||||
) {
|
||||
return {
|
||||
status: 'error',
|
||||
message: 'The "products" collection is missing or inaccessible. Please sync your data.',
|
||||
message: shouldShowDevErrors
|
||||
? 'The "products" collection is missing or inaccessible. Please sync your data.'
|
||||
: 'Required data structures are currently unavailable.',
|
||||
code: 'SCHEMA_MISSING',
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'error',
|
||||
message: `Schema error: ${e.message}`,
|
||||
message: shouldShowDevErrors
|
||||
? `Schema error: ${e.message}`
|
||||
: 'The data schema is currently misconfigured.',
|
||||
code: 'SCHEMA_ERROR',
|
||||
};
|
||||
}
|
||||
|
||||
return { status: 'ok', message: 'Directus is reachable and responding.' };
|
||||
} catch (error: any) {
|
||||
Sentry.captureException(error);
|
||||
console.error('Directus health check failed with unexpected error:', error);
|
||||
return {
|
||||
status: 'error',
|
||||
message: error.message || 'An unexpected error occurred while connecting to the CMS.',
|
||||
message: formatError(error),
|
||||
code: error.code || 'UNKNOWN',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ const preprocessEmptyString = (val: unknown) => (val === '' ? undefined : val);
|
||||
export const envSchema = z.object({
|
||||
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
||||
NEXT_PUBLIC_BASE_URL: z.preprocess(preprocessEmptyString, z.string().url()),
|
||||
NEXT_PUBLIC_TARGET: z
|
||||
.enum(['development', 'testing', 'staging', 'production'])
|
||||
.default('development'),
|
||||
|
||||
// Analytics
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.preprocess(preprocessEmptyString, z.string().optional()),
|
||||
@@ -45,6 +48,9 @@ export const envSchema = z.object({
|
||||
DIRECTUS_ADMIN_PASSWORD: z.preprocess(preprocessEmptyString, z.string().optional()),
|
||||
DIRECTUS_API_TOKEN: z.preprocess(preprocessEmptyString, z.string().optional()),
|
||||
INTERNAL_DIRECTUS_URL: z.preprocess(preprocessEmptyString, z.string().url().optional()),
|
||||
|
||||
// Deploy Target
|
||||
TARGET: z.enum(['development', 'testing', 'staging', 'production']).default('development'),
|
||||
});
|
||||
|
||||
export type Env = z.infer<typeof envSchema>;
|
||||
@@ -57,6 +63,7 @@ export function getRawEnv() {
|
||||
return {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
|
||||
NEXT_PUBLIC_TARGET: process.env.NEXT_PUBLIC_TARGET,
|
||||
NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
|
||||
NEXT_PUBLIC_UMAMI_SCRIPT_URL: process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL,
|
||||
SENTRY_DSN: process.env.SENTRY_DSN,
|
||||
@@ -72,5 +79,6 @@ export function getRawEnv() {
|
||||
DIRECTUS_ADMIN_PASSWORD: process.env.DIRECTUS_ADMIN_PASSWORD,
|
||||
DIRECTUS_API_TOKEN: process.env.DIRECTUS_API_TOKEN,
|
||||
INTERNAL_DIRECTUS_URL: process.env.INTERNAL_DIRECTUS_URL,
|
||||
TARGET: process.env.TARGET,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user