feat: integrate feedback module

This commit is contained in:
2026-02-08 21:48:55 +01:00
parent 453a603392
commit 7ec826dae3
48 changed files with 4413 additions and 1168 deletions

View File

@@ -22,6 +22,7 @@ function createConfig() {
isStaging: target === 'staging',
isTesting: target === 'testing',
isDevelopment: target === 'development',
feedbackEnabled: env.NEXT_PUBLIC_FEEDBACK_ENABLED,
baseUrl: env.NEXT_PUBLIC_BASE_URL,
@@ -67,6 +68,10 @@ function createConfig() {
internalUrl: env.INTERNAL_DIRECTUS_URL,
proxyPath: '/cms',
},
infraCMS: {
url: env.INFRA_DIRECTUS_URL || env.DIRECTUS_URL,
token: env.INFRA_DIRECTUS_TOKEN || env.DIRECTUS_API_TOKEN,
},
notifications: {
gotify: {
url: env.GOTIFY_URL,
@@ -135,6 +140,12 @@ export const config = {
get notifications() {
return getConfig().notifications;
},
get feedbackEnabled() {
return getConfig().feedbackEnabled;
},
get infraCMS() {
return getConfig().infraCMS;
},
};
/**

View File

@@ -1,4 +1,4 @@
import { createDirectus, rest, authentication, readItems, readCollections } from '@directus/sdk';
import { createDirectus, rest, authentication, staticToken, readItems, readCollections } from '@directus/sdk';
import { config } from './config';
import { getServerAppServices } from './services/create-services.server';
@@ -13,6 +13,7 @@ const effectiveUrl =
? `${window.location.origin}${proxyPath}`
: proxyPath;
// Initialize client with authentication plugin
const client = createDirectus(effectiveUrl).with(rest()).with(authentication());
/**
@@ -30,20 +31,48 @@ function formatError(error: any) {
return 'A system error occurred. Our team has been notified.';
}
let authPromise: Promise<void> | null = null;
export async function ensureAuthenticated() {
if (token) {
client.setToken(token);
(client as any).setToken(token);
return;
}
// Check if we already have a valid session token in memory (for login flow)
const existingToken = await (client as any).getToken();
if (existingToken) {
return;
}
if (adminEmail && password) {
try {
await client.login(adminEmail, password);
} catch (e) {
if (typeof window === 'undefined') {
getServerAppServices().errors.captureException(e, { part: 'directus_auth' });
}
console.error('Failed to authenticate with Directus:', e);
if (authPromise) {
return authPromise;
}
authPromise = (async () => {
try {
client.setToken(null as any);
await client.login(adminEmail, password);
console.log(`✅ Directus: Authenticated successfully as ${adminEmail}`);
} catch (e: any) {
if (typeof window === 'undefined') {
getServerAppServices().errors.captureException(e, { part: 'directus_auth' });
}
console.error(`Failed to authenticate with Directus (${adminEmail}):`, e.message);
if (shouldShowDevErrors && e.errors) {
console.error('Directus Auth Details:', JSON.stringify(e.errors, null, 2));
}
// Clear the promise on failure (especially on invalid credentials)
// so we can retry on next request if credentials were updated
authPromise = null;
throw e;
}
})();
return authPromise;
} else if (shouldShowDevErrors && !adminEmail && !password && !token) {
console.warn('Directus: No token or admin credentials provided.');
}
}

View File

@@ -53,6 +53,21 @@ export const envSchema = z
// Gotify
GOTIFY_URL: z.preprocess(preprocessEmptyString, z.string().url().optional()),
GOTIFY_TOKEN: z.preprocess(preprocessEmptyString, z.string().optional()),
// Gatekeeper
GATEKEEPER_URL: z.preprocess(
preprocessEmptyString,
z.string().url().default('http://gatekeeper:3000'),
),
NEXT_PUBLIC_FEEDBACK_ENABLED: z.preprocess(
(val) => val === 'true' || val === true,
z.boolean().default(false)
),
GATEKEEPER_BYPASS_ENABLED: z.preprocess(
(val) => val === 'true' || val === true,
z.boolean().default(false)
),
INFRA_DIRECTUS_URL: z.preprocess(preprocessEmptyString, z.string().url().optional()),
INFRA_DIRECTUS_TOKEN: z.preprocess(preprocessEmptyString, z.string().optional()),
})
.superRefine((data, ctx) => {
const target = data.NEXT_PUBLIC_TARGET || data.TARGET;
@@ -100,5 +115,10 @@ export function getRawEnv() {
TARGET: process.env.TARGET,
GOTIFY_URL: process.env.GOTIFY_URL,
GOTIFY_TOKEN: process.env.GOTIFY_TOKEN,
GATEKEEPER_URL: process.env.GATEKEEPER_URL,
NEXT_PUBLIC_FEEDBACK_ENABLED: process.env.NEXT_PUBLIC_FEEDBACK_ENABLED,
GATEKEEPER_BYPASS_ENABLED: process.env.GATEKEEPER_BYPASS_ENABLED,
INFRA_DIRECTUS_URL: process.env.INFRA_DIRECTUS_URL,
INFRA_DIRECTUS_TOKEN: process.env.INFRA_DIRECTUS_TOKEN,
};
}