Decouple Directus from project

This commit is contained in:
2026-06-21 09:33:54 -04:00
parent 31a8a43225
commit 9b7b5401b0
12 changed files with 83 additions and 877 deletions
+46 -10
View File
@@ -42,7 +42,8 @@ const directusAssetUrlTemplate =
process.env.DIRECTUS_ASSET_URL_TEMPLATE && !process.env.DIRECTUS_ASSET_URL_TEMPLATE.includes('example.com')
? process.env.DIRECTUS_ASSET_URL_TEMPLATE
: undefined;
const directusToken = process.env.DIRECTUS_ADMIN_TOKEN;
const directusToken = process.env.DIRECTUS_TOKEN ?? process.env.DIRECTUS_ADMIN_TOKEN;
const directusDebug = ['1', 'true', 'yes', 'on'].includes((process.env.DIRECTUS_DEBUG ?? '').toLowerCase());
const homepageBannerFolder = process.env.DIRECTUS_HOMEPAGE_BANNER_FOLDER ?? 'homepage-banners';
const cafePageFolder = process.env.DIRECTUS_CAFE_PAGE_FOLDER ?? 'cafe-page';
@@ -81,6 +82,15 @@ function directusHeaders(): Record<string, string> | undefined {
return { Authorization: `Bearer ${directusToken}` };
}
function directusLog(message: string): void {
if (!directusDebug) return;
console.warn(`[directus] ${message}`);
}
function directusEndpointLabel(endpoint: URL): string {
return `${endpoint.pathname}${endpoint.search}`;
}
async function readCollection<T>(collection: CollectionName): Promise<T[]> {
const endpoint = new URL(`/items/${collection}`, directusUrl);
endpoint.searchParams.set('limit', '100');
@@ -98,27 +108,43 @@ async function readCollection<T>(collection: CollectionName): Promise<T[]> {
}
const payload = (await response.json()) as { data?: T[] };
return payload.data ?? [];
} catch {
const data = payload.data ?? [];
directusLog(`collection ${collection}: read ${data.length} item(s)`);
return data;
} catch (error) {
directusLog(`collection ${collection}: using fallback after ${error instanceof Error ? error.message : String(error)}`);
return fallbackFor(collection) as T[];
}
}
async function readDirectusEndpoint<T>(endpoint: URL): Promise<T[]> {
directusLog(`GET ${directusEndpointLabel(endpoint)}`);
let response = await fetch(endpoint, {
headers: directusHeaders(),
});
if (response.status === 403 && directusToken) {
directusLog(`GET ${directusEndpointLabel(endpoint)} returned 403 with token; retrying without token`);
response = await fetch(endpoint);
}
if (!response.ok) {
throw new Error(`Directus responded with ${response.status}`);
let detail = '';
try {
const payload = (await response.json()) as { errors?: Array<{ message?: string; extensions?: { code?: string; reason?: string } }> };
const firstError = payload.errors?.[0];
detail = firstError?.extensions?.reason || firstError?.message || '';
} catch {
detail = '';
}
throw new Error(`Directus responded with ${response.status}${detail ? `: ${detail}` : ''}`);
}
const payload = (await response.json()) as { data?: T[] };
return payload.data ?? [];
const data = payload.data ?? [];
directusLog(`GET ${directusEndpointLabel(endpoint)} returned ${data.length} item(s)`);
return data;
}
function extensionFromFilename(filename?: string): string {
@@ -177,7 +203,10 @@ async function findFolderByName(name: string): Promise<DirectusFolder | null> {
async function getImagesFromFolder(folderName: string, fallbackImages: HomepageBannerImage[]): Promise<HomepageBannerImage[]> {
try {
const folder = await findFolderByName(folderName);
if (!folder) return fallbackImages;
if (!folder) {
directusLog(`folder "${folderName}": not found; using ${fallbackImages.length} fallback image(s)`);
return fallbackImages;
}
const endpoint = new URL('/files', directusUrl);
endpoint.searchParams.set('limit', '20');
@@ -192,8 +221,15 @@ async function getImagesFromFolder(folderName: string, fallbackImages: HomepageB
alt: file.description || file.title || file.filename_download || 'Swansea Airport',
}));
return images.length > 0 ? images : fallbackImages;
} catch {
if (images.length === 0) {
directusLog(`folder "${folderName}": no images found; using ${fallbackImages.length} fallback image(s)`);
return fallbackImages;
}
directusLog(`folder "${folderName}": using ${images.length} Directus image(s)`);
return images;
} catch (error) {
directusLog(`folder "${folderName}": using fallback after ${error instanceof Error ? error.message : String(error)}`);
return fallbackImages;
}
}
@@ -242,8 +278,8 @@ async function getRecurringEvents(): Promise<EventItem[]> {
const endpoint = new URL('/items/event_dates', directusUrl);
endpoint.searchParams.set('limit', '100');
endpoint.searchParams.set('sort', 'date');
endpoint.searchParams.set(
'fields',
endpoint.searchParams.set(
'fields',
'id,date,template.id,template.title,template.slug,template.description,template.image.id,template.image.filename_download,template.image.filename_disk,template.logo.id,template.logo.filename_download,template.logo.filename_disk,template.booking_url',
);