@ai-stack/payloadcms 3.76.0-beta.5 → 3.76.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +5 -0
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/upload.js +2 -2
- package/dist/endpoints/upload.js.map +1 -1
- package/dist/utilities/encryption.js +15 -9
- package/dist/utilities/encryption.js.map +1 -1
- package/dist/utilities/images/fetchImages.js +2 -6
- package/dist/utilities/images/fetchImages.js.map +1 -1
- package/dist/utilities/runtime/resolveServerURL.d.ts +3 -0
- package/dist/utilities/runtime/resolveServerURL.js +67 -0
- package/dist/utilities/runtime/resolveServerURL.js.map +1 -0
- package/package.json +1 -1
package/dist/defaults.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare const PLUGIN_API_ENDPOINT_GENERATE_UPLOAD = "/plugin-ai/generate/
|
|
|
8
8
|
export declare const PLUGIN_FETCH_FIELDS_ENDPOINT = "/plugin-ai/fetch-fields";
|
|
9
9
|
export declare const PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK = "/plugin-ai/videogen/webhook";
|
|
10
10
|
export declare const PLUGIN_API_ENDPOINT_FETCH_VOICES = "/plugin-ai/elevenlabs/fetch-voices";
|
|
11
|
+
export declare const PLUGIN_SERVER_URL_ENV_KEYS: readonly ["SERVER_URL", "NEXT_PUBLIC_SERVER_URL"];
|
|
11
12
|
export declare const PLUGIN_DEFAULT_OPENAI_MODEL = "gpt-4o-mini";
|
|
12
13
|
export declare const PLUGIN_DEFAULT_ANTHROPIC_MODEL = "claude-3-5-sonnet-latest";
|
|
13
14
|
export declare const excludeCollections: string[];
|
package/dist/defaults.js
CHANGED
|
@@ -9,6 +9,11 @@ export const PLUGIN_API_ENDPOINT_GENERATE_UPLOAD = `${PLUGIN_API_ENDPOINT_GENERA
|
|
|
9
9
|
export const PLUGIN_FETCH_FIELDS_ENDPOINT = `${PLUGIN_API_ENDPOINT_BASE}/fetch-fields`;
|
|
10
10
|
export const PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK = `${PLUGIN_API_ENDPOINT_BASE}/videogen/webhook`;
|
|
11
11
|
export const PLUGIN_API_ENDPOINT_FETCH_VOICES = `${PLUGIN_API_ENDPOINT_BASE}/elevenlabs/fetch-voices`;
|
|
12
|
+
// Runtime URL resolution fallbacks
|
|
13
|
+
export const PLUGIN_SERVER_URL_ENV_KEYS = [
|
|
14
|
+
'SERVER_URL',
|
|
15
|
+
'NEXT_PUBLIC_SERVER_URL'
|
|
16
|
+
];
|
|
12
17
|
// LLM Settings
|
|
13
18
|
export const PLUGIN_DEFAULT_OPENAI_MODEL = `gpt-4o-mini`;
|
|
14
19
|
export const PLUGIN_DEFAULT_ANTHROPIC_MODEL = `claude-3-5-sonnet-latest`;
|
package/dist/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["export const PLUGIN_NAME = 'plugin-ai'\nexport const PLUGIN_INSTRUCTIONS_TABLE = `${PLUGIN_NAME}-instructions`\nexport const PLUGIN_AI_JOBS_TABLE = `${PLUGIN_NAME}-ai-jobs`\nexport const PLUGIN_LEXICAL_EDITOR_FEATURE = `${PLUGIN_NAME}-actions-feature`\n\n// Endpoint defaults\nexport const PLUGIN_API_ENDPOINT_BASE = `/${PLUGIN_NAME}`\nexport const PLUGIN_API_ENDPOINT_GENERATE = `${PLUGIN_API_ENDPOINT_BASE}/generate`\nexport const PLUGIN_API_ENDPOINT_GENERATE_UPLOAD = `${PLUGIN_API_ENDPOINT_GENERATE}/upload`\nexport const PLUGIN_FETCH_FIELDS_ENDPOINT = `${PLUGIN_API_ENDPOINT_BASE}/fetch-fields`\nexport const PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK = `${PLUGIN_API_ENDPOINT_BASE}/videogen/webhook`\nexport const PLUGIN_API_ENDPOINT_FETCH_VOICES = `${PLUGIN_API_ENDPOINT_BASE}/elevenlabs/fetch-voices`\n\n// LLM Settings\nexport const PLUGIN_DEFAULT_OPENAI_MODEL = `gpt-4o-mini`\nexport const PLUGIN_DEFAULT_ANTHROPIC_MODEL = `claude-3-5-sonnet-latest`\n\nexport const excludeCollections = [\n 'payload-preferences',\n 'payload-migrations',\n 'payload-locked-documents',\n 'ai-providers',\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_AI_JOBS_TABLE,\n]\n"],"names":["PLUGIN_NAME","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_AI_JOBS_TABLE","PLUGIN_LEXICAL_EDITOR_FEATURE","PLUGIN_API_ENDPOINT_BASE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_FETCH_FIELDS_ENDPOINT","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_API_ENDPOINT_FETCH_VOICES","PLUGIN_DEFAULT_OPENAI_MODEL","PLUGIN_DEFAULT_ANTHROPIC_MODEL","excludeCollections"],"mappings":"AAAA,OAAO,MAAMA,cAAc,YAAW;AACtC,OAAO,MAAMC,4BAA4B,CAAC,EAAED,YAAY,aAAa,CAAC,CAAA;AACtE,OAAO,MAAME,uBAAuB,CAAC,EAAEF,YAAY,QAAQ,CAAC,CAAA;AAC5D,OAAO,MAAMG,gCAAgC,CAAC,EAAEH,YAAY,gBAAgB,CAAC,CAAA;AAE7E,oBAAoB;AACpB,OAAO,MAAMI,2BAA2B,CAAC,CAAC,EAAEJ,YAAY,CAAC,CAAA;AACzD,OAAO,MAAMK,+BAA+B,CAAC,EAAED,yBAAyB,SAAS,CAAC,CAAA;AAClF,OAAO,MAAME,sCAAsC,CAAC,EAAED,6BAA6B,OAAO,CAAC,CAAA;AAC3F,OAAO,MAAME,+BAA+B,CAAC,EAAEH,yBAAyB,aAAa,CAAC,CAAA;AACtF,OAAO,MAAMI,uCAAuC,CAAC,EAAEJ,yBAAyB,iBAAiB,CAAC,CAAA;AAClG,OAAO,MAAMK,mCAAmC,CAAC,EAAEL,yBAAyB,wBAAwB,CAAC,CAAA;AAErG,eAAe;AACf,OAAO,
|
|
1
|
+
{"version":3,"sources":["../src/defaults.ts"],"sourcesContent":["export const PLUGIN_NAME = 'plugin-ai'\nexport const PLUGIN_INSTRUCTIONS_TABLE = `${PLUGIN_NAME}-instructions`\nexport const PLUGIN_AI_JOBS_TABLE = `${PLUGIN_NAME}-ai-jobs`\nexport const PLUGIN_LEXICAL_EDITOR_FEATURE = `${PLUGIN_NAME}-actions-feature`\n\n// Endpoint defaults\nexport const PLUGIN_API_ENDPOINT_BASE = `/${PLUGIN_NAME}`\nexport const PLUGIN_API_ENDPOINT_GENERATE = `${PLUGIN_API_ENDPOINT_BASE}/generate`\nexport const PLUGIN_API_ENDPOINT_GENERATE_UPLOAD = `${PLUGIN_API_ENDPOINT_GENERATE}/upload`\nexport const PLUGIN_FETCH_FIELDS_ENDPOINT = `${PLUGIN_API_ENDPOINT_BASE}/fetch-fields`\nexport const PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK = `${PLUGIN_API_ENDPOINT_BASE}/videogen/webhook`\nexport const PLUGIN_API_ENDPOINT_FETCH_VOICES = `${PLUGIN_API_ENDPOINT_BASE}/elevenlabs/fetch-voices`\n\n// Runtime URL resolution fallbacks\nexport const PLUGIN_SERVER_URL_ENV_KEYS = ['SERVER_URL', 'NEXT_PUBLIC_SERVER_URL'] as const\n\n// LLM Settings\nexport const PLUGIN_DEFAULT_OPENAI_MODEL = `gpt-4o-mini`\nexport const PLUGIN_DEFAULT_ANTHROPIC_MODEL = `claude-3-5-sonnet-latest`\n\nexport const excludeCollections = [\n 'payload-preferences',\n 'payload-migrations',\n 'payload-locked-documents',\n 'ai-providers',\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_AI_JOBS_TABLE,\n]\n"],"names":["PLUGIN_NAME","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_AI_JOBS_TABLE","PLUGIN_LEXICAL_EDITOR_FEATURE","PLUGIN_API_ENDPOINT_BASE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_FETCH_FIELDS_ENDPOINT","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_API_ENDPOINT_FETCH_VOICES","PLUGIN_SERVER_URL_ENV_KEYS","PLUGIN_DEFAULT_OPENAI_MODEL","PLUGIN_DEFAULT_ANTHROPIC_MODEL","excludeCollections"],"mappings":"AAAA,OAAO,MAAMA,cAAc,YAAW;AACtC,OAAO,MAAMC,4BAA4B,CAAC,EAAED,YAAY,aAAa,CAAC,CAAA;AACtE,OAAO,MAAME,uBAAuB,CAAC,EAAEF,YAAY,QAAQ,CAAC,CAAA;AAC5D,OAAO,MAAMG,gCAAgC,CAAC,EAAEH,YAAY,gBAAgB,CAAC,CAAA;AAE7E,oBAAoB;AACpB,OAAO,MAAMI,2BAA2B,CAAC,CAAC,EAAEJ,YAAY,CAAC,CAAA;AACzD,OAAO,MAAMK,+BAA+B,CAAC,EAAED,yBAAyB,SAAS,CAAC,CAAA;AAClF,OAAO,MAAME,sCAAsC,CAAC,EAAED,6BAA6B,OAAO,CAAC,CAAA;AAC3F,OAAO,MAAME,+BAA+B,CAAC,EAAEH,yBAAyB,aAAa,CAAC,CAAA;AACtF,OAAO,MAAMI,uCAAuC,CAAC,EAAEJ,yBAAyB,iBAAiB,CAAC,CAAA;AAClG,OAAO,MAAMK,mCAAmC,CAAC,EAAEL,yBAAyB,wBAAwB,CAAC,CAAA;AAErG,mCAAmC;AACnC,OAAO,MAAMM,6BAA6B;IAAC;IAAc;CAAyB,CAAS;AAE3F,eAAe;AACf,OAAO,MAAMC,8BAA8B,CAAC,WAAW,CAAC,CAAA;AACxD,OAAO,MAAMC,iCAAiC,CAAC,wBAAwB,CAAC,CAAA;AAExE,OAAO,MAAMC,qBAAqB;IAChC;IACA;IACA;IACA;IACAZ;IACAC;CACD,CAAA"}
|
package/dist/endpoints/upload.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as process from 'node:process';
|
|
2
1
|
import { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK, PLUGIN_INSTRUCTIONS_TABLE } from '../defaults.js';
|
|
3
2
|
import { registerEditorHelper } from '../libraries/handlebars/helpers.js';
|
|
4
3
|
import { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js';
|
|
@@ -10,6 +9,7 @@ import { extractImageData } from '../utilities/images/extractImageData.js';
|
|
|
10
9
|
import { fetchImages } from '../utilities/images/fetchImages.js';
|
|
11
10
|
import { resolveImageReferences } from '../utilities/images/resolveImageReferences.js';
|
|
12
11
|
import { lexicalToPromptTemplate } from '../utilities/lexical/lexicalToPromptTemplate.js';
|
|
12
|
+
import { resolveServerURL } from '../utilities/runtime/resolveServerURL.js';
|
|
13
13
|
import { sanitizeLog } from '../utilities/sanitizeLog.js';
|
|
14
14
|
/**
|
|
15
15
|
* Image/video/audio upload generation endpoint handler.
|
|
@@ -139,7 +139,7 @@ import { sanitizeLog } from '../utilities/sanitizeLog.js';
|
|
|
139
139
|
}), `— AI Plugin: Executing media generation`);
|
|
140
140
|
}
|
|
141
141
|
// Prepare callback URL for async jobs
|
|
142
|
-
const serverURL = req
|
|
142
|
+
const serverURL = resolveServerURL(req);
|
|
143
143
|
const callbackUrl = serverURL ? `${serverURL.replace(/\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}` : undefined;
|
|
144
144
|
const modelId = instructions['model-id'];
|
|
145
145
|
const aiSettings = await req.payload.findGlobal({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/upload.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { Field, PayloadRequest } from 'payload'\n\nimport * as process from 'node:process'\n\nimport type { PluginConfig } from '../types.js'\n\nimport { checkAccess } from '../access/checkAccess.js'\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n PLUGIN_INSTRUCTIONS_TABLE,\n} from '../defaults.js'\nimport { registerEditorHelper } from '../libraries/handlebars/helpers.js'\nimport { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js'\nimport { resolveEffectiveInstructionSettings } from '../utilities/ai/resolveEffectiveInstructionSettings.js'\nimport { extendContextWithPromptFields } from '../utilities/buildPromptUtils.js'\nimport { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js'\nimport { getFieldBySchemaPath } from '../utilities/fields/getFieldBySchemaPath.js'\nimport { extractImageData } from '../utilities/images/extractImageData.js'\nimport { type FetchableImage, fetchImages } from '../utilities/images/fetchImages.js'\nimport { resolveImageReferences } from '../utilities/images/resolveImageReferences.js'\nimport { lexicalToPromptTemplate } from '../utilities/lexical/lexicalToPromptTemplate.js'\nimport { sanitizeLog } from '../utilities/sanitizeLog.js'\n\n/**\n * Image/video/audio upload generation endpoint handler.\n * Uses payload.ai.generateMedia for media generation.\n */\nexport const uploadHandler = (pluginConfig: PluginConfig) => async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n // await checkAccess(req, pluginConfig)\n\n const data = await req.json?.()\n\n const { collectionSlug, documentId, options } = data\n const { instructionId } = options\n let docData = {}\n\n if (documentId) {\n try {\n docData = await req.payload.findByID({\n id: documentId,\n collection: collectionSlug as string,\n draft: true,\n req, // Pass req to ensure access control is applied\n })\n } catch (e) {\n req.payload.logger.error(\n e,\n '— AI Plugin: Error fetching document, you should try again after enabling drafts for this collection',\n )\n }\n }\n\n const contextData = {\n ...docData,\n ...data.doc,\n }\n\n let instructions: Record<string, unknown> = { images: [], 'model-id': '', prompt: '' }\n\n if (instructionId) {\n // Verify user has access to the specific instruction\n instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n }\n\n let { prompt: promptTemplate = '' } = instructions\n\n // Convert Lexical JSON to string template if needed\n if (promptTemplate && typeof promptTemplate === 'object') {\n promptTemplate = lexicalToPromptTemplate(promptTemplate)\n }\n\n const { images: sampleImages = [] } = instructions\n const schemaPath = String(instructions['schema-path'])\n registerEditorHelper(req.payload, schemaPath)\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate as string)) {\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n const extendedContext = extendContextWithPromptFields(\n contextData,\n { type: String(instructions['field-type']), collection: collectionSlug },\n pluginConfig,\n )\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n sanitizeLog({\n contextDataKeys: Object.keys(contextData),\n contextDataSample: Object.fromEntries(\n Object.entries(contextData).map(([k, v]) => [\n k,\n typeof v === 'object' ? `[object]` : v,\n ]),\n ),\n promptTemplate,\n }),\n `— AI Plugin: DEBUG upload context before replacePlaceholders`,\n )\n }\n\n const text = await replacePlaceholders(promptTemplate as string, extendedContext)\n const uploadCollectionSlug = instructions['relation-to']\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n text,\n contextData,\n req,\n collectionSlug,\n )\n\n // Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images\n const images = [\n ...extractImageData(processedPrompt),\n ...resolvedImages,\n ...(sampleImages as unknown[]),\n ] as FetchableImage[]\n\n // Process images - convert to ImagePart format using helper\n const editImages: ImagePart[] = await fetchImages(req, images)\n\n let promptToUse = processedPrompt\n let targetField: Field | null | undefined\n\n try {\n const targetCollection = req.payload.config.collections.find((c) => c.slug === collectionSlug)\n if (targetCollection && schemaPath) {\n targetField = getFieldBySchemaPath(targetCollection, schemaPath)\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error finding field for hooks')\n }\n\n if (targetField && (targetField as any).custom?.ai?.beforeGenerate) {\n const beforeHooks = (targetField as any).custom.ai.beforeGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of beforeHooks) {\n const result = await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n prompt: promptToUse,\n req,\n })\n\n if (result) {\n if (result.prompt) {\n promptToUse = result.prompt\n }\n if (result.instructions) {\n instructions = {\n ...instructions,\n ...result.instructions,\n }\n }\n }\n }\n }\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(sanitizeLog({ text: promptToUse }), `— AI Plugin: Executing media generation`)\n }\n\n // Prepare callback URL for async jobs\n const serverURL =\n req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL\n\n const callbackUrl = serverURL\n ? `${serverURL.replace(/\\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}`\n : undefined\n\n const modelId = instructions['model-id']\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-providers',\n context: { unsafe: true },\n })\n const { effectiveSettings: modelSettings, settingsName } = resolveEffectiveInstructionSettings({\n defaults: aiSettings?.defaults as Record<string, unknown> | undefined,\n instructions,\n })\n\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${modelId}`)\n }\n\n const generateParams = {\n callbackUrl,\n images: editImages,\n instructionId,\n model: modelSettings.model as string,\n prompt: promptToUse,\n provider: modelSettings.provider as string,\n providerOptions: modelSettings,\n ...modelSettings,\n }\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n sanitizeLog(generateParams),\n '— AI Plugin: Final generation parameters for media',\n )\n }\n\n // Use payload.ai.generateMedia directly! 🎉\n const result = await req.payload.ai.generateMedia(generateParams)\n\n if (targetField && (targetField as any).custom?.ai?.afterGenerate) {\n const afterHooks = (targetField as any).custom.ai.afterGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of afterHooks) {\n await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n req,\n result,\n })\n }\n }\n\n // If model returned files immediately, proceed with upload\n if (result && 'files' in result && Array.isArray(result.files) && result.files.length > 0) {\n const uploadedDocs: Array<{ alt?: string; id: number | string }> = []\n\n for (const file of result.files) {\n let assetData: { alt?: string; id: number | string }\n\n // Create a synthetic result for the single file to pass to mediaUpload\n const singleFileResult = {\n files: [file],\n }\n\n if (typeof pluginConfig.mediaUpload === 'function') {\n const uploadResult = await pluginConfig.mediaUpload(singleFileResult, {\n collection: uploadCollectionSlug as string,\n request: req,\n })\n assetData = { id: uploadResult.id, alt: (uploadResult as any).alt }\n } else {\n const created = await req.payload.create({\n collection: uploadCollectionSlug as string,\n data: { alt: text },\n file, // Pass the file object directly: { data, mimetype, name, size }\n req, // Pass req to ensure access control is applied\n })\n assetData = { id: created.id, alt: created.alt as string }\n }\n\n if (assetData.id) {\n uploadedDocs.push(assetData)\n }\n }\n\n if (uploadedDocs.length === 0) {\n req.payload.logger.error(\n 'Error uploading generated media, is your media upload function correct?',\n )\n throw new Error('Error uploading generated media!')\n }\n\n // Check if target field supports multiple values\n let hasMany = false\n if (targetField) {\n if (\n targetField.type === 'relationship' ||\n targetField.type === 'upload' ||\n targetField.type === 'select'\n ) {\n hasMany = (targetField as any).hasMany === true\n }\n }\n\n if (hasMany) {\n return new Response(\n JSON.stringify({\n result: uploadedDocs.map((d) => ({\n id: d.id,\n alt: d.alt,\n })),\n }),\n )\n }\n\n return new Response(\n JSON.stringify({\n result: {\n id: uploadedDocs[0].id,\n alt: uploadedDocs[0].alt,\n },\n }),\n )\n }\n\n // Otherwise, assume async job launch\n if (result && ('jobId' in result || 'taskId' in result)) {\n const externalTaskId = result.jobId || result.taskId\n const status = result.status || 'queued'\n const progress = result.progress ?? 0\n\n // Create AI Job doc and return only its id\n const createdJob = await req.payload.create({\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n instructionId,\n progress,\n status,\n task_id: externalTaskId,\n },\n overrideAccess: true,\n req,\n })\n\n return new Response(JSON.stringify({ job: { id: createdJob.id } }), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n throw new Error('Unexpected model response.')\n } catch (error) {\n req.payload.logger.error(\n // @ts-expect-error\n error?.type || (error as Error).message,\n '— AI Plugin: Error generating media upload:',\n )\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') || message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n}\n"],"names":["process","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_INSTRUCTIONS_TABLE","registerEditorHelper","replacePlaceholders","resolveEffectiveInstructionSettings","extendContextWithPromptFields","buildSmartPrompt","isGenericPrompt","getFieldBySchemaPath","extractImageData","fetchImages","resolveImageReferences","lexicalToPromptTemplate","sanitizeLog","uploadHandler","pluginConfig","req","data","json","collectionSlug","documentId","options","instructionId","docData","payload","findByID","id","collection","draft","e","logger","error","contextData","doc","instructions","images","prompt","promptTemplate","sampleImages","schemaPath","String","documentData","debugging","info","smartPrompt","extendedContext","type","contextDataKeys","Object","keys","contextDataSample","fromEntries","entries","map","k","v","text","uploadCollectionSlug","resolvedImages","processedPrompt","editImages","promptToUse","targetField","targetCollection","config","collections","find","c","slug","custom","ai","beforeGenerate","beforeHooks","hook","result","field","headers","serverURL","env","SERVER_URL","NEXT_PUBLIC_SERVER_URL","callbackUrl","replace","undefined","modelId","aiSettings","findGlobal","context","unsafe","effectiveSettings","modelSettings","settingsName","defaults","Error","generateParams","model","provider","providerOptions","generateMedia","afterGenerate","afterHooks","Array","isArray","files","length","uploadedDocs","file","assetData","singleFileResult","mediaUpload","uploadResult","request","alt","created","create","push","hasMany","Response","JSON","stringify","d","externalTaskId","jobId","taskId","status","progress","createdJob","task_id","overrideAccess","job","message","includes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/upload.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { Field, PayloadRequest } from 'payload'\n\nimport type { PluginConfig } from '../types.js'\n\nimport { checkAccess } from '../access/checkAccess.js'\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK,\n PLUGIN_INSTRUCTIONS_TABLE,\n} from '../defaults.js'\nimport { registerEditorHelper } from '../libraries/handlebars/helpers.js'\nimport { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js'\nimport { resolveEffectiveInstructionSettings } from '../utilities/ai/resolveEffectiveInstructionSettings.js'\nimport { extendContextWithPromptFields } from '../utilities/buildPromptUtils.js'\nimport { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js'\nimport { getFieldBySchemaPath } from '../utilities/fields/getFieldBySchemaPath.js'\nimport { extractImageData } from '../utilities/images/extractImageData.js'\nimport { type FetchableImage, fetchImages } from '../utilities/images/fetchImages.js'\nimport { resolveImageReferences } from '../utilities/images/resolveImageReferences.js'\nimport { lexicalToPromptTemplate } from '../utilities/lexical/lexicalToPromptTemplate.js'\nimport { resolveServerURL } from '../utilities/runtime/resolveServerURL.js'\nimport { sanitizeLog } from '../utilities/sanitizeLog.js'\n\n/**\n * Image/video/audio upload generation endpoint handler.\n * Uses payload.ai.generateMedia for media generation.\n */\nexport const uploadHandler = (pluginConfig: PluginConfig) => async (req: PayloadRequest) => {\n try {\n // Check authentication and authorization first\n // await checkAccess(req, pluginConfig)\n const data = await req.json?.()\n\n const { collectionSlug, documentId, options } = data\n const { instructionId } = options\n let docData = {}\n\n if (documentId) {\n try {\n docData = await req.payload.findByID({\n id: documentId,\n collection: collectionSlug as string,\n draft: true,\n req, // Pass req to ensure access control is applied\n })\n } catch (e) {\n req.payload.logger.error(\n e,\n '— AI Plugin: Error fetching document, you should try again after enabling drafts for this collection',\n )\n }\n }\n\n const contextData = {\n ...docData,\n ...data.doc,\n }\n\n let instructions: Record<string, unknown> = { images: [], 'model-id': '', prompt: '' }\n\n if (instructionId) {\n // Verify user has access to the specific instruction\n instructions = await req.payload.findByID({\n id: instructionId,\n collection: PLUGIN_INSTRUCTIONS_TABLE,\n req, // Pass req to ensure access control is applied\n })\n }\n\n let { prompt: promptTemplate = '' } = instructions\n\n // Convert Lexical JSON to string template if needed\n if (promptTemplate && typeof promptTemplate === 'object') {\n promptTemplate = lexicalToPromptTemplate(promptTemplate)\n }\n\n const { images: sampleImages = [] } = instructions\n const schemaPath = String(instructions['schema-path'])\n registerEditorHelper(req.payload, schemaPath)\n\n // Smart fallback: if prompt is generic, build a contextual prompt from field metadata\n if (isGenericPrompt(promptTemplate as string)) {\n promptTemplate = buildSmartPrompt({\n documentData: contextData,\n payload: req.payload,\n schemaPath,\n })\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n { smartPrompt: promptTemplate },\n `— AI Plugin: Using smart fallback prompt for ${schemaPath}`,\n )\n }\n }\n\n const extendedContext = extendContextWithPromptFields(\n contextData,\n { type: String(instructions['field-type']), collection: collectionSlug },\n pluginConfig,\n )\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n sanitizeLog({\n contextDataKeys: Object.keys(contextData),\n contextDataSample: Object.fromEntries(\n Object.entries(contextData).map(([k, v]) => [\n k,\n typeof v === 'object' ? `[object]` : v,\n ]),\n ),\n promptTemplate,\n }),\n `— AI Plugin: DEBUG upload context before replacePlaceholders`,\n )\n }\n\n const text = await replacePlaceholders(promptTemplate as string, extendedContext)\n const uploadCollectionSlug = instructions['relation-to']\n\n // Resolve @field:filename references from the prompt\n const { images: resolvedImages, processedPrompt } = await resolveImageReferences(\n text,\n contextData,\n req,\n collectionSlug,\n )\n\n // Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images\n const images = [\n ...extractImageData(processedPrompt),\n ...resolvedImages,\n ...(sampleImages as unknown[]),\n ] as FetchableImage[]\n\n // Process images - convert to ImagePart format using helper\n const editImages: ImagePart[] = await fetchImages(req, images)\n\n let promptToUse = processedPrompt\n let targetField: Field | null | undefined\n\n try {\n const targetCollection = req.payload.config.collections.find((c) => c.slug === collectionSlug)\n if (targetCollection && schemaPath) {\n targetField = getFieldBySchemaPath(targetCollection, schemaPath)\n }\n } catch (e) {\n req.payload.logger.error(e, '— AI Plugin: Error finding field for hooks')\n }\n\n if (targetField && (targetField as any).custom?.ai?.beforeGenerate) {\n const beforeHooks = (targetField as any).custom.ai.beforeGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of beforeHooks) {\n const result = await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n prompt: promptToUse,\n req,\n })\n\n if (result) {\n if (result.prompt) {\n promptToUse = result.prompt\n }\n if (result.instructions) {\n instructions = {\n ...instructions,\n ...result.instructions,\n }\n }\n }\n }\n }\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(sanitizeLog({ text: promptToUse }), `— AI Plugin: Executing media generation`)\n }\n\n // Prepare callback URL for async jobs\n const serverURL = resolveServerURL(req)\n\n const callbackUrl = serverURL\n ? `${serverURL.replace(/\\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}`\n : undefined\n\n const modelId = instructions['model-id']\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-providers',\n context: { unsafe: true },\n })\n const { effectiveSettings: modelSettings, settingsName } = resolveEffectiveInstructionSettings({\n defaults: aiSettings?.defaults as Record<string, unknown> | undefined,\n instructions,\n })\n\n if (!settingsName) {\n throw new Error(`Unsupported model-id: ${modelId}`)\n }\n\n const generateParams = {\n callbackUrl,\n images: editImages,\n instructionId,\n model: modelSettings.model as string,\n prompt: promptToUse,\n provider: modelSettings.provider as string,\n providerOptions: modelSettings,\n ...modelSettings,\n }\n\n if (pluginConfig.debugging) {\n req.payload.logger.info(\n sanitizeLog(generateParams),\n '— AI Plugin: Final generation parameters for media',\n )\n }\n\n // Use payload.ai.generateMedia directly! 🎉\n const result = await req.payload.ai.generateMedia(generateParams)\n\n if (targetField && (targetField as any).custom?.ai?.afterGenerate) {\n const afterHooks = (targetField as any).custom.ai.afterGenerate as Array<\n (args: any) => Promise<any>\n >\n for (const hook of afterHooks) {\n await hook({\n doc: contextData,\n field: targetField,\n headers: req.headers,\n instructions,\n payload: req.payload,\n req,\n result,\n })\n }\n }\n\n // If model returned files immediately, proceed with upload\n if (result && 'files' in result && Array.isArray(result.files) && result.files.length > 0) {\n const uploadedDocs: Array<{ alt?: string; id: number | string }> = []\n\n for (const file of result.files) {\n let assetData: { alt?: string; id: number | string }\n\n // Create a synthetic result for the single file to pass to mediaUpload\n const singleFileResult = {\n files: [file],\n }\n\n if (typeof pluginConfig.mediaUpload === 'function') {\n const uploadResult = await pluginConfig.mediaUpload(singleFileResult, {\n collection: uploadCollectionSlug as string,\n request: req,\n })\n assetData = { id: uploadResult.id, alt: (uploadResult as any).alt }\n } else {\n const created = await req.payload.create({\n collection: uploadCollectionSlug as string,\n data: { alt: text },\n file, // Pass the file object directly: { data, mimetype, name, size }\n req, // Pass req to ensure access control is applied\n })\n assetData = { id: created.id, alt: created.alt as string }\n }\n\n if (assetData.id) {\n uploadedDocs.push(assetData)\n }\n }\n\n if (uploadedDocs.length === 0) {\n req.payload.logger.error(\n 'Error uploading generated media, is your media upload function correct?',\n )\n throw new Error('Error uploading generated media!')\n }\n\n // Check if target field supports multiple values\n let hasMany = false\n if (targetField) {\n if (\n targetField.type === 'relationship' ||\n targetField.type === 'upload' ||\n targetField.type === 'select'\n ) {\n hasMany = (targetField as any).hasMany === true\n }\n }\n\n if (hasMany) {\n return new Response(\n JSON.stringify({\n result: uploadedDocs.map((d) => ({\n id: d.id,\n alt: d.alt,\n })),\n }),\n )\n }\n\n return new Response(\n JSON.stringify({\n result: {\n id: uploadedDocs[0].id,\n alt: uploadedDocs[0].alt,\n },\n }),\n )\n }\n\n // Otherwise, assume async job launch\n if (result && ('jobId' in result || 'taskId' in result)) {\n const externalTaskId = result.jobId || result.taskId\n const status = result.status || 'queued'\n const progress = result.progress ?? 0\n\n // Create AI Job doc and return only its id\n const createdJob = await req.payload.create({\n collection: PLUGIN_AI_JOBS_TABLE,\n data: {\n instructionId,\n progress,\n status,\n task_id: externalTaskId,\n },\n overrideAccess: true,\n req,\n })\n\n return new Response(JSON.stringify({ job: { id: createdJob.id } }), {\n headers: { 'Content-Type': 'application/json' },\n })\n }\n\n throw new Error('Unexpected model response.')\n } catch (error) {\n req.payload.logger.error(\n // @ts-expect-error\n error?.type || (error as Error).message,\n '— AI Plugin: Error generating media upload:',\n )\n const message =\n error && typeof error === 'object' && 'message' in error\n ? (error as Error).message\n : String(error)\n return new Response(JSON.stringify({ error: message }), {\n headers: { 'Content-Type': 'application/json' },\n status:\n message.includes('Authentication required') || message.includes('Insufficient permissions')\n ? 401\n : 500,\n })\n }\n}\n"],"names":["PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK","PLUGIN_INSTRUCTIONS_TABLE","registerEditorHelper","replacePlaceholders","resolveEffectiveInstructionSettings","extendContextWithPromptFields","buildSmartPrompt","isGenericPrompt","getFieldBySchemaPath","extractImageData","fetchImages","resolveImageReferences","lexicalToPromptTemplate","resolveServerURL","sanitizeLog","uploadHandler","pluginConfig","req","data","json","collectionSlug","documentId","options","instructionId","docData","payload","findByID","id","collection","draft","e","logger","error","contextData","doc","instructions","images","prompt","promptTemplate","sampleImages","schemaPath","String","documentData","debugging","info","smartPrompt","extendedContext","type","contextDataKeys","Object","keys","contextDataSample","fromEntries","entries","map","k","v","text","uploadCollectionSlug","resolvedImages","processedPrompt","editImages","promptToUse","targetField","targetCollection","config","collections","find","c","slug","custom","ai","beforeGenerate","beforeHooks","hook","result","field","headers","serverURL","callbackUrl","replace","undefined","modelId","aiSettings","findGlobal","context","unsafe","effectiveSettings","modelSettings","settingsName","defaults","Error","generateParams","model","provider","providerOptions","generateMedia","afterGenerate","afterHooks","Array","isArray","files","length","uploadedDocs","file","assetData","singleFileResult","mediaUpload","uploadResult","request","alt","created","create","push","hasMany","Response","JSON","stringify","d","externalTaskId","jobId","taskId","status","progress","createdJob","task_id","overrideAccess","job","message","includes"],"mappings":"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"}
|
|
@@ -8,17 +8,16 @@ export function encrypt(text, secret) {
|
|
|
8
8
|
if (!secret) {
|
|
9
9
|
throw new Error('No secret provided for encryption');
|
|
10
10
|
}
|
|
11
|
-
//
|
|
12
|
-
const key = crypto.createHash('sha256').update(secret).digest();
|
|
11
|
+
// Cloudflare Workers' Node compatibility can require explicit UTF-8 handling.
|
|
12
|
+
const key = crypto.createHash('sha256').update(String(secret), 'utf8').digest();
|
|
13
13
|
const iv = crypto.randomBytes(ivLength);
|
|
14
14
|
const cipher = crypto.createCipheriv(algorithm, key, iv);
|
|
15
|
-
let encrypted = cipher.update(text);
|
|
15
|
+
let encrypted = cipher.update(Buffer.from(String(text), 'utf8'));
|
|
16
16
|
encrypted = Buffer.concat([
|
|
17
17
|
encrypted,
|
|
18
18
|
cipher.final()
|
|
19
19
|
]);
|
|
20
|
-
|
|
21
|
-
return result;
|
|
20
|
+
return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
|
|
22
21
|
}
|
|
23
22
|
export function decrypt(text, secret) {
|
|
24
23
|
if (!text) {
|
|
@@ -29,17 +28,24 @@ export function decrypt(text, secret) {
|
|
|
29
28
|
}
|
|
30
29
|
try {
|
|
31
30
|
const textParts = text.split(':');
|
|
32
|
-
const
|
|
31
|
+
const ivHex = textParts.shift();
|
|
32
|
+
if (!ivHex) {
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
36
|
+
if (iv.length !== ivLength) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
33
39
|
const encryptedText = Buffer.from(textParts.join(':'), 'hex');
|
|
34
|
-
const key = crypto.createHash('sha256').update(secret).digest();
|
|
40
|
+
const key = crypto.createHash('sha256').update(String(secret), 'utf8').digest();
|
|
35
41
|
const decipher = crypto.createDecipheriv(algorithm, key, iv);
|
|
36
42
|
let decrypted = decipher.update(encryptedText);
|
|
37
43
|
decrypted = Buffer.concat([
|
|
38
44
|
decrypted,
|
|
39
45
|
decipher.final()
|
|
40
46
|
]);
|
|
41
|
-
return decrypted.toString();
|
|
42
|
-
} catch (
|
|
47
|
+
return decrypted.toString('utf8');
|
|
48
|
+
} catch (_error) {
|
|
43
49
|
// If decryption fails, return original text (might be already plain or invalid)
|
|
44
50
|
return text;
|
|
45
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for encryption')\n }\n\n //
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for encryption')\n }\n\n // Cloudflare Workers' Node compatibility can require explicit UTF-8 handling.\n const key = crypto.createHash('sha256').update(String(secret), 'utf8').digest()\n const iv = crypto.randomBytes(ivLength)\n const cipher = crypto.createCipheriv(algorithm, key, iv)\n let encrypted = cipher.update(Buffer.from(String(text), 'utf8'))\n encrypted = Buffer.concat([encrypted, cipher.final()])\n\n return `${iv.toString('hex')}:${encrypted.toString('hex')}`\n}\n\nexport function decrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for decryption')\n }\n\n try {\n const textParts = text.split(':')\n const ivHex = textParts.shift()\n if (!ivHex) {\n return text\n }\n\n const iv = Buffer.from(ivHex, 'hex')\n if (iv.length !== ivLength) {\n return text\n }\n\n const encryptedText = Buffer.from(textParts.join(':'), 'hex')\n const key = crypto.createHash('sha256').update(String(secret), 'utf8').digest()\n const decipher = crypto.createDecipheriv(algorithm, key, iv)\n let decrypted = decipher.update(encryptedText)\n decrypted = Buffer.concat([decrypted, decipher.final()])\n\n return decrypted.toString('utf8')\n } catch (_error) {\n // If decryption fails, return original text (might be already plain or invalid)\n return text\n }\n}\n"],"names":["crypto","algorithm","ivLength","encrypt","text","secret","Error","key","createHash","update","String","digest","iv","randomBytes","cipher","createCipheriv","encrypted","Buffer","from","concat","final","toString","decrypt","textParts","split","ivHex","shift","length","encryptedText","join","decipher","createDecipheriv","decrypted","_error"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAE3B,MAAMC,YAAY;AAClB,MAAMC,WAAW;AAEjB,OAAO,SAASC,QAAQC,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,8EAA8E;IAC9E,MAAMC,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACC,OAAOL,SAAS,QAAQM,MAAM;IAC7E,MAAMC,KAAKZ,OAAOa,WAAW,CAACX;IAC9B,MAAMY,SAASd,OAAOe,cAAc,CAACd,WAAWM,KAAKK;IACrD,IAAII,YAAYF,OAAOL,MAAM,CAACQ,OAAOC,IAAI,CAACR,OAAON,OAAO;IACxDY,YAAYC,OAAOE,MAAM,CAAC;QAACH;QAAWF,OAAOM,KAAK;KAAG;IAErD,OAAO,CAAC,EAAER,GAAGS,QAAQ,CAAC,OAAO,CAAC,EAAEL,UAAUK,QAAQ,CAAC,OAAO,CAAC;AAC7D;AAEA,OAAO,SAASC,QAAQlB,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAI;QACF,MAAMiB,YAAYnB,KAAKoB,KAAK,CAAC;QAC7B,MAAMC,QAAQF,UAAUG,KAAK;QAC7B,IAAI,CAACD,OAAO;YACV,OAAOrB;QACT;QAEA,MAAMQ,KAAKK,OAAOC,IAAI,CAACO,OAAO;QAC9B,IAAIb,GAAGe,MAAM,KAAKzB,UAAU;YAC1B,OAAOE;QACT;QAEA,MAAMwB,gBAAgBX,OAAOC,IAAI,CAACK,UAAUM,IAAI,CAAC,MAAM;QACvD,MAAMtB,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACC,OAAOL,SAAS,QAAQM,MAAM;QAC7E,MAAMmB,WAAW9B,OAAO+B,gBAAgB,CAAC9B,WAAWM,KAAKK;QACzD,IAAIoB,YAAYF,SAASrB,MAAM,CAACmB;QAChCI,YAAYf,OAAOE,MAAM,CAAC;YAACa;YAAWF,SAASV,KAAK;SAAG;QAEvD,OAAOY,UAAUX,QAAQ,CAAC;IAC5B,EAAE,OAAOY,QAAQ;QACf,gFAAgF;QAChF,OAAO7B;IACT;AACF"}
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { resolveAbsoluteURL } from '../runtime/resolveServerURL.js';
|
|
2
2
|
/**
|
|
3
3
|
* Fetch a single image and convert to an AI SDK ImagePart.
|
|
4
4
|
*/ async function fetchSingleImage(req, img) {
|
|
5
|
-
const
|
|
6
|
-
let url = img.image.thumbnailURL || img.image.url;
|
|
7
|
-
if (!url.startsWith('http')) {
|
|
8
|
-
url = `${String(serverURL)}${String(url)}`;
|
|
9
|
-
}
|
|
5
|
+
const url = resolveAbsoluteURL(img.image.thumbnailURL || img.image.url, req);
|
|
10
6
|
const response = await fetch(url, {
|
|
11
7
|
headers: {
|
|
12
8
|
Authorization: `Bearer ${req.headers.get('Authorization')?.split('Bearer ')[1] || ''}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utilities/images/fetchImages.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { PayloadRequest } from 'payload'\n\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/utilities/images/fetchImages.ts"],"sourcesContent":["import type { ImagePart } from 'ai'\nimport type { PayloadRequest } from 'payload'\n\nimport { resolveAbsoluteURL } from '../runtime/resolveServerURL.js'\n\nexport interface FetchableImage {\n image: {\n mimeType?: string\n thumbnailURL?: string\n url: string\n }\n}\n\n/**\n * Fetch a single image and convert to an AI SDK ImagePart.\n */\nasync function fetchSingleImage(\n req: PayloadRequest,\n img: FetchableImage,\n): Promise<ImagePart> {\n const url = resolveAbsoluteURL(img.image.thumbnailURL || img.image.url, req)\n\n const response = await fetch(url, {\n headers: {\n Authorization: `Bearer ${req.headers.get('Authorization')?.split('Bearer ')[1] || ''}`,\n },\n method: 'GET',\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch image: ${response.statusText}`)\n }\n\n const blob = await response.blob()\n const arrayBuffer = await blob.arrayBuffer()\n\n return {\n type: 'image',\n image: arrayBuffer,\n mediaType: img.image.mimeType || blob.type || 'image/png',\n }\n}\n\n/**\n * Fetches images in parallel from a list of objects containing URLs\n * and converts them to AI SDK compatible ImageParts.\n */\nexport async function fetchImages(\n req: PayloadRequest,\n images: FetchableImage[],\n): Promise<ImagePart[]> {\n if (images.length === 0) {\n return []\n }\n\n const results = await Promise.allSettled(\n images.map((img) => fetchSingleImage(req, img)),\n )\n\n const imageParts: ImagePart[] = []\n for (const result of results) {\n if (result.status === 'fulfilled') {\n imageParts.push(result.value)\n } else {\n req.payload.logger.error(result.reason, '— AI Plugin: Error fetching reference image')\n }\n }\n\n if (imageParts.length === 0 && images.length > 0) {\n throw new Error(\n \"We couldn't fetch any of the images. Please ensure the images are accessible and hosted publicly.\",\n )\n }\n\n return imageParts\n}\n"],"names":["resolveAbsoluteURL","fetchSingleImage","req","img","url","image","thumbnailURL","response","fetch","headers","Authorization","get","split","method","ok","Error","statusText","blob","arrayBuffer","type","mediaType","mimeType","fetchImages","images","length","results","Promise","allSettled","map","imageParts","result","status","push","value","payload","logger","error","reason"],"mappings":"AAGA,SAASA,kBAAkB,QAAQ,iCAAgC;AAUnE;;CAEC,GACD,eAAeC,iBACbC,GAAmB,EACnBC,GAAmB;IAEnB,MAAMC,MAAMJ,mBAAmBG,IAAIE,KAAK,CAACC,YAAY,IAAIH,IAAIE,KAAK,CAACD,GAAG,EAAEF;IAExE,MAAMK,WAAW,MAAMC,MAAMJ,KAAK;QAChCK,SAAS;YACPC,eAAe,CAAC,OAAO,EAAER,IAAIO,OAAO,CAACE,GAAG,CAAC,kBAAkBC,MAAM,UAAU,CAAC,EAAE,IAAI,GAAG,CAAC;QACxF;QACAC,QAAQ;IACV;IAEA,IAAI,CAACN,SAASO,EAAE,EAAE;QAChB,MAAM,IAAIC,MAAM,CAAC,uBAAuB,EAAER,SAASS,UAAU,CAAC,CAAC;IACjE;IAEA,MAAMC,OAAO,MAAMV,SAASU,IAAI;IAChC,MAAMC,cAAc,MAAMD,KAAKC,WAAW;IAE1C,OAAO;QACLC,MAAM;QACNd,OAAOa;QACPE,WAAWjB,IAAIE,KAAK,CAACgB,QAAQ,IAAIJ,KAAKE,IAAI,IAAI;IAChD;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeG,YACpBpB,GAAmB,EACnBqB,MAAwB;IAExB,IAAIA,OAAOC,MAAM,KAAK,GAAG;QACvB,OAAO,EAAE;IACX;IAEA,MAAMC,UAAU,MAAMC,QAAQC,UAAU,CACtCJ,OAAOK,GAAG,CAAC,CAACzB,MAAQF,iBAAiBC,KAAKC;IAG5C,MAAM0B,aAA0B,EAAE;IAClC,KAAK,MAAMC,UAAUL,QAAS;QAC5B,IAAIK,OAAOC,MAAM,KAAK,aAAa;YACjCF,WAAWG,IAAI,CAACF,OAAOG,KAAK;QAC9B,OAAO;YACL/B,IAAIgC,OAAO,CAACC,MAAM,CAACC,KAAK,CAACN,OAAOO,MAAM,EAAE;QAC1C;IACF;IAEA,IAAIR,WAAWL,MAAM,KAAK,KAAKD,OAAOC,MAAM,GAAG,GAAG;QAChD,MAAM,IAAIT,MACR;IAEJ;IAEA,OAAOc;AACT"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { PLUGIN_SERVER_URL_ENV_KEYS } from '../../defaults.js';
|
|
2
|
+
const normalizeServerURL = (value)=>{
|
|
3
|
+
if (!value) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
if (!trimmed) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const parsed = new URL(trimmed);
|
|
12
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const normalizedPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/+$/, '');
|
|
16
|
+
return `${parsed.origin}${normalizedPath}`;
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const getProcessEnvValue = (key)=>{
|
|
22
|
+
if (typeof process === 'undefined' || !process.env) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const value = process.env[key];
|
|
26
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
27
|
+
};
|
|
28
|
+
const getHeaderOrigin = (req)=>{
|
|
29
|
+
const forwardedProto = req.headers.get('x-forwarded-proto')?.split(',')[0]?.trim();
|
|
30
|
+
const forwardedHost = req.headers.get('x-forwarded-host')?.split(',')[0]?.trim();
|
|
31
|
+
if (forwardedProto && forwardedHost) {
|
|
32
|
+
return `${forwardedProto}://${forwardedHost}`;
|
|
33
|
+
}
|
|
34
|
+
const host = req.headers.get('host')?.trim();
|
|
35
|
+
if (!host) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return `https://${host}`;
|
|
39
|
+
};
|
|
40
|
+
export function resolveServerURL(req) {
|
|
41
|
+
const candidates = [
|
|
42
|
+
req.payload.config?.serverURL || undefined,
|
|
43
|
+
...PLUGIN_SERVER_URL_ENV_KEYS.map((key)=>getProcessEnvValue(key)),
|
|
44
|
+
req.url,
|
|
45
|
+
getHeaderOrigin(req)
|
|
46
|
+
];
|
|
47
|
+
for (const candidate of candidates){
|
|
48
|
+
const normalized = normalizeServerURL(candidate);
|
|
49
|
+
if (normalized) {
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
export function resolveAbsoluteURL(input, req) {
|
|
56
|
+
if (/^https?:\/\//i.test(input)) {
|
|
57
|
+
return input;
|
|
58
|
+
}
|
|
59
|
+
const baseURL = resolveServerURL(req);
|
|
60
|
+
if (!baseURL) {
|
|
61
|
+
throw new Error('Could not resolve a server URL for relative asset path. Set `payload.config.serverURL` or SERVER_URL.');
|
|
62
|
+
}
|
|
63
|
+
const normalizedPath = input.startsWith('/') ? input : `/${input}`;
|
|
64
|
+
return `${baseURL}${normalizedPath}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=resolveServerURL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utilities/runtime/resolveServerURL.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { PLUGIN_SERVER_URL_ENV_KEYS } from '../../defaults.js'\n\nconst normalizeServerURL = (value: string | undefined): string | undefined => {\n if (!value) {\n return undefined\n }\n\n const trimmed = value.trim()\n if (!trimmed) {\n return undefined\n }\n\n try {\n const parsed = new URL(trimmed)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return undefined\n }\n\n const normalizedPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/+$/, '')\n return `${parsed.origin}${normalizedPath}`\n } catch {\n return undefined\n }\n}\n\nconst getProcessEnvValue = (key: string): string | undefined => {\n if (typeof process === 'undefined' || !process.env) {\n return undefined\n }\n\n const value = process.env[key]\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nconst getHeaderOrigin = (req: PayloadRequest): string | undefined => {\n const forwardedProto = req.headers.get('x-forwarded-proto')?.split(',')[0]?.trim()\n const forwardedHost = req.headers.get('x-forwarded-host')?.split(',')[0]?.trim()\n\n if (forwardedProto && forwardedHost) {\n return `${forwardedProto}://${forwardedHost}`\n }\n\n const host = req.headers.get('host')?.trim()\n if (!host) {\n return undefined\n }\n\n return `https://${host}`\n}\n\nexport function resolveServerURL(req: PayloadRequest): string | undefined {\n const candidates: Array<string | undefined> = [\n req.payload.config?.serverURL || undefined,\n ...PLUGIN_SERVER_URL_ENV_KEYS.map((key) => getProcessEnvValue(key)),\n req.url,\n getHeaderOrigin(req),\n ]\n\n for (const candidate of candidates) {\n const normalized = normalizeServerURL(candidate)\n if (normalized) {\n return normalized\n }\n }\n\n return undefined\n}\n\nexport function resolveAbsoluteURL(input: string, req: PayloadRequest): string {\n if (/^https?:\\/\\//i.test(input)) {\n return input\n }\n\n const baseURL = resolveServerURL(req)\n if (!baseURL) {\n throw new Error(\n 'Could not resolve a server URL for relative asset path. Set `payload.config.serverURL` or SERVER_URL.',\n )\n }\n\n const normalizedPath = input.startsWith('/') ? input : `/${input}`\n return `${baseURL}${normalizedPath}`\n}\n"],"names":["PLUGIN_SERVER_URL_ENV_KEYS","normalizeServerURL","value","undefined","trimmed","trim","parsed","URL","protocol","normalizedPath","pathname","replace","origin","getProcessEnvValue","key","process","env","length","getHeaderOrigin","req","forwardedProto","headers","get","split","forwardedHost","host","resolveServerURL","candidates","payload","config","serverURL","map","url","candidate","normalized","resolveAbsoluteURL","input","test","baseURL","Error","startsWith"],"mappings":"AAEA,SAASA,0BAA0B,QAAQ,oBAAmB;AAE9D,MAAMC,qBAAqB,CAACC;IAC1B,IAAI,CAACA,OAAO;QACV,OAAOC;IACT;IAEA,MAAMC,UAAUF,MAAMG,IAAI;IAC1B,IAAI,CAACD,SAAS;QACZ,OAAOD;IACT;IAEA,IAAI;QACF,MAAMG,SAAS,IAAIC,IAAIH;QACvB,IAAIE,OAAOE,QAAQ,KAAK,WAAWF,OAAOE,QAAQ,KAAK,UAAU;YAC/D,OAAOL;QACT;QAEA,MAAMM,iBAAiBH,OAAOI,QAAQ,KAAK,MAAM,KAAKJ,OAAOI,QAAQ,CAACC,OAAO,CAAC,QAAQ;QACtF,OAAO,CAAC,EAAEL,OAAOM,MAAM,CAAC,EAAEH,eAAe,CAAC;IAC5C,EAAE,OAAM;QACN,OAAON;IACT;AACF;AAEA,MAAMU,qBAAqB,CAACC;IAC1B,IAAI,OAAOC,YAAY,eAAe,CAACA,QAAQC,GAAG,EAAE;QAClD,OAAOb;IACT;IAEA,MAAMD,QAAQa,QAAQC,GAAG,CAACF,IAAI;IAC9B,OAAO,OAAOZ,UAAU,YAAYA,MAAMe,MAAM,GAAG,IAAIf,QAAQC;AACjE;AAEA,MAAMe,kBAAkB,CAACC;IACvB,MAAMC,iBAAiBD,IAAIE,OAAO,CAACC,GAAG,CAAC,sBAAsBC,MAAM,IAAI,CAAC,EAAE,EAAElB;IAC5E,MAAMmB,gBAAgBL,IAAIE,OAAO,CAACC,GAAG,CAAC,qBAAqBC,MAAM,IAAI,CAAC,EAAE,EAAElB;IAE1E,IAAIe,kBAAkBI,eAAe;QACnC,OAAO,CAAC,EAAEJ,eAAe,GAAG,EAAEI,cAAc,CAAC;IAC/C;IAEA,MAAMC,OAAON,IAAIE,OAAO,CAACC,GAAG,CAAC,SAASjB;IACtC,IAAI,CAACoB,MAAM;QACT,OAAOtB;IACT;IAEA,OAAO,CAAC,QAAQ,EAAEsB,KAAK,CAAC;AAC1B;AAEA,OAAO,SAASC,iBAAiBP,GAAmB;IAClD,MAAMQ,aAAwC;QAC5CR,IAAIS,OAAO,CAACC,MAAM,EAAEC,aAAa3B;WAC9BH,2BAA2B+B,GAAG,CAAC,CAACjB,MAAQD,mBAAmBC;QAC9DK,IAAIa,GAAG;QACPd,gBAAgBC;KACjB;IAED,KAAK,MAAMc,aAAaN,WAAY;QAClC,MAAMO,aAAajC,mBAAmBgC;QACtC,IAAIC,YAAY;YACd,OAAOA;QACT;IACF;IAEA,OAAO/B;AACT;AAEA,OAAO,SAASgC,mBAAmBC,KAAa,EAAEjB,GAAmB;IACnE,IAAI,gBAAgBkB,IAAI,CAACD,QAAQ;QAC/B,OAAOA;IACT;IAEA,MAAME,UAAUZ,iBAAiBP;IACjC,IAAI,CAACmB,SAAS;QACZ,MAAM,IAAIC,MACR;IAEJ;IAEA,MAAM9B,iBAAiB2B,MAAMI,UAAU,CAAC,OAAOJ,QAAQ,CAAC,CAAC,EAAEA,MAAM,CAAC;IAClE,OAAO,CAAC,EAAEE,QAAQ,EAAE7B,eAAe,CAAC;AACtC"}
|