diff --git a/src/app/api/admin/events/route.ts b/src/app/api/admin/events/route.ts index 1c699a6..5a8371e 100644 --- a/src/app/api/admin/events/route.ts +++ b/src/app/api/admin/events/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { verifyEventStaff, isValidGoogleDriveUrl, isWebPImage } from '@/lib/auth-server'; -import { imagekit } from '@/lib/imagekit'; +import { getImageKit } from '@/lib/imagekit'; import { formFile, formText, isUuid, jsonText, readFormLimited, readJsonObject } from '@/lib/security'; export const runtime = 'nodejs'; @@ -179,7 +179,7 @@ export async function POST(req: NextRequest) { // 5. Upload poster to ImageKit const cleanFileName = posterFile.name.replace(/[^a-zA-Z0-9.-]/g, '_'); - const uploadRes = await imagekit.upload({ + const uploadRes = await getImageKit().upload({ file: buffer, fileName: `event_${Date.now()}_${cleanFileName}`, folder: '/innovision/events', @@ -389,7 +389,7 @@ export async function PATCH(req: NextRequest) { } const cleanFileName = newPosterFile.name.replace(/[^a-zA-Z0-9.-]/g, '_'); - const uploadRes = await imagekit.upload({ + const uploadRes = await getImageKit().upload({ file: buffer, fileName: `event_${Date.now()}_${cleanFileName}`, folder: '/innovision/events', diff --git a/src/app/api/admin/gallery/route.ts b/src/app/api/admin/gallery/route.ts index 3917654..0572e12 100644 --- a/src/app/api/admin/gallery/route.ts +++ b/src/app/api/admin/gallery/route.ts @@ -1,6 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; import { verifyEventStaff, isWebPImage } from '@/lib/auth-server'; -import { imagekit } from '@/lib/imagekit'; +import { getImageKit } from '@/lib/imagekit'; import { formFile, formText, isImageKitPathInFolder, isUuid, readFormLimited, readJsonObject } from '@/lib/security'; export const runtime = 'nodejs'; @@ -108,7 +108,7 @@ export async function POST(req: NextRequest) { // 5. Upload image to ImageKit const cleanFileName = imageFile.name.replace(/[^a-zA-Z0-9.-]/g, '_'); - const uploadRes = await imagekit.upload({ + const uploadRes = await getImageKit().upload({ file: buffer, fileName: `gallery_${Date.now()}_${cleanFileName}`, folder: '/innovision/gallery', @@ -242,9 +242,9 @@ export async function DELETE(req: NextRequest) { try { // file_id comes from a database row, so confirm it really is a gallery image before using the ImageKit // private key to delete it (never registration proofs or event posters). - const details = await imagekit.getFileDetails(item.file_id); + const details = await getImageKit().getFileDetails(item.file_id); if (isImageKitPathInFolder(details.filePath, '/innovision/gallery')) { - await imagekit.deleteFile(item.file_id); + await getImageKit().deleteFile(item.file_id); } else { console.warn('Refusing to delete ImageKit file outside the gallery folder:', item.file_id, details.filePath); } diff --git a/src/lib/imagekit.ts b/src/lib/imagekit.ts index 87e47a0..8a55652 100644 --- a/src/lib/imagekit.ts +++ b/src/lib/imagekit.ts @@ -2,10 +2,21 @@ // (see src/lib/payment-proofs.ts). import ImageKit from 'imagekit'; -const privateKey = process.env.IMAGEKIT_PRIVATE_KEY || ''; +let client: ImageKit | null = null; -export const imagekit = new ImageKit({ - publicKey: process.env.IMAGEKIT_PUBLIC_KEY || process.env.NEXT_PUBLIC_IMAGEKIT_PUBLIC_KEY || '', - privateKey, - urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT || process.env.NEXT_PUBLIC_IMAGEKIT_URL_ENDPOINT || '', -}); +// Created on first use rather than at import time: `next build` imports route modules while collecting page data, +// and the ImageKit constructor throws when keys are missing (as they are in CI). +export function getImageKit(): ImageKit { + if (client) return client; + + const publicKey = process.env.IMAGEKIT_PUBLIC_KEY || process.env.NEXT_PUBLIC_IMAGEKIT_PUBLIC_KEY || ''; + const privateKey = process.env.IMAGEKIT_PRIVATE_KEY || ''; + const urlEndpoint = process.env.IMAGEKIT_URL_ENDPOINT || process.env.NEXT_PUBLIC_IMAGEKIT_URL_ENDPOINT || ''; + + if (!publicKey || !privateKey || !urlEndpoint) { + throw new Error('ImageKit configuration is missing.'); + } + + client = new ImageKit({ publicKey, privateKey, urlEndpoint }); + return client; +}