@humaan/payload-storage-imagekit 2.4.0 → 2.5.0
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/README.md +3 -2
- package/dist/migrate.d.ts +4 -4
- package/dist/migrate.js +44 -41
- package/dist/migrate.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,8 +110,9 @@ Behavior:
|
|
|
110
110
|
- Creates destination folders automatically if they do not exist
|
|
111
111
|
- Optional database update via Payload local API
|
|
112
112
|
- Dry-run is enabled by default (`dryRun: true`)
|
|
113
|
-
-
|
|
114
|
-
-
|
|
113
|
+
- Resolves each source asset by the stored `imagekit.fileId`, then copies the ImageKit-returned file path into the destination folder. This avoids mismatches between Payload's `filename` field and ImageKit's normalized file names.
|
|
114
|
+
- Reruns are idempotent, including dry-runs: if a document's stored `imagekit.fileId` already resolves to a file in the destination collection folder, that document is skipped.
|
|
115
|
+
- Handles [private files](#private-files): mark a collection `{ private: true }` and migrated documents are stored with `imagekit.isPrivate: true`. The fast server-side copy preserves the source file's ImageKit privacy; when the copy falls back to re-uploading from the source URL, the source URL is signed (so a private source can be read) and the file is re-uploaded with `isPrivateFile: true`. Because ImageKit privacy is fixed at upload, migrating assumes the destination collection's privacy matches the source — copying a public file into a `{ private: true }` collection keeps the file public on ImageKit.
|
|
115
116
|
|
|
116
117
|
Example:
|
|
117
118
|
|
package/dist/migrate.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export type MigrateFolderOptions = {
|
|
|
21
21
|
*/
|
|
22
22
|
from: string;
|
|
23
23
|
/**
|
|
24
|
-
* Optional Payload instance. When provided, the stored `imagekit.*`
|
|
25
|
-
*
|
|
24
|
+
* Optional Payload instance. When provided, the stored `imagekit.*` fields in the
|
|
25
|
+
* database will be updated to reflect the new ImageKit asset after copying.
|
|
26
26
|
*/
|
|
27
27
|
payload?: Payload;
|
|
28
28
|
/**
|
|
@@ -40,8 +40,8 @@ export type MigrateFolderOptions = {
|
|
|
40
40
|
*/
|
|
41
41
|
to: string;
|
|
42
42
|
/**
|
|
43
|
-
* ImageKit URL endpoint (e.g. `https://ik.imagekit.io/your_id`). Required to
|
|
44
|
-
*
|
|
43
|
+
* ImageKit URL endpoint (e.g. `https://ik.imagekit.io/your_id`). Required to sign
|
|
44
|
+
* private source URLs when the migrator falls back to re-uploading from the source URL.
|
|
45
45
|
*/
|
|
46
46
|
urlEndpoint: string;
|
|
47
47
|
};
|
package/dist/migrate.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import ImageKit from '@imagekit/nodejs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { getAdminThumbnailURL, getPrivateAdminThumbnailURL } from './getAdminThumbnailURL.js';
|
|
4
3
|
const DEFAULT_SIGNED_URL_EXPIRES_IN = 900;
|
|
5
4
|
const isPrivateCollectionConfig = (value)=>value !== true && typeof value === 'object' && value?.private === true;
|
|
6
5
|
const normalizeFolder = (folder)=>{
|
|
@@ -11,7 +10,7 @@ const normalizeFolder = (folder)=>{
|
|
|
11
10
|
return `/${normalized}`;
|
|
12
11
|
};
|
|
13
12
|
const getCollectionFolder = (rootFolder, collectionSlug)=>path.posix.join(rootFolder, collectionSlug);
|
|
14
|
-
const
|
|
13
|
+
const isFileInFolder = (filePath, folderPath)=>filePath === folderPath || filePath.startsWith(`${folderPath}/`);
|
|
15
14
|
const ensureFolderExists = async (client, folderPath)=>{
|
|
16
15
|
const normalized = normalizeFolder(folderPath);
|
|
17
16
|
const segments = normalized.split('/').filter(Boolean);
|
|
@@ -84,6 +83,18 @@ const uploadFileFromURL = async ({ client, fileName, folder, isPrivate, sourceUr
|
|
|
84
83
|
url: uploaded.url
|
|
85
84
|
};
|
|
86
85
|
};
|
|
86
|
+
const getSourceImagekitFields = async ({ client, fileId })=>{
|
|
87
|
+
const file = await client.files.get(fileId);
|
|
88
|
+
if (!file.fileId || !file.filePath || !file.url) {
|
|
89
|
+
throw new Error(`migrateFolder: missing ImageKit source file details for "${fileId}".`);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
fileId: file.fileId,
|
|
93
|
+
fileName: file.name || path.posix.basename(file.filePath),
|
|
94
|
+
filePath: file.filePath,
|
|
95
|
+
url: file.url
|
|
96
|
+
};
|
|
97
|
+
};
|
|
87
98
|
const getUploadedImagekitFields = async ({ client, fileId })=>{
|
|
88
99
|
const file = await client.files.get(fileId);
|
|
89
100
|
if (!file.fileId || !file.url) {
|
|
@@ -122,7 +133,6 @@ export const migrateFolder = async (options)=>{
|
|
|
122
133
|
}
|
|
123
134
|
const privateSlugs = new Set(Object.entries(collections).filter(([, value])=>isPrivateCollectionConfig(value)).map(([slug])=>slug));
|
|
124
135
|
const signedUrlExpiresIn = typeof signedUrlExpiresInOption === 'number' && signedUrlExpiresInOption > 0 ? signedUrlExpiresInOption : DEFAULT_SIGNED_URL_EXPIRES_IN;
|
|
125
|
-
const apiRoute = payload?.config.routes?.api ?? '/api';
|
|
126
136
|
const fromFolder = normalizeFolder(from);
|
|
127
137
|
const toFolder = normalizeFolder(to);
|
|
128
138
|
if (fromFolder === toFolder) {
|
|
@@ -170,26 +180,35 @@ export const migrateFolder = async (options)=>{
|
|
|
170
180
|
const doc = found.docs[i];
|
|
171
181
|
const filename = doc.filename;
|
|
172
182
|
const existingImagekit = doc.imagekit;
|
|
183
|
+
const displayName = filename || existingImagekit?.fileId || doc.id;
|
|
173
184
|
console.log(`[migrateFolder] [${i + 1}/${found.docs.length}] Processing document ${doc.id}`);
|
|
174
|
-
// Skip items that don't have ImageKit
|
|
175
|
-
if (!
|
|
185
|
+
// Skip items that don't have an ImageKit file ID to migrate from.
|
|
186
|
+
if (!existingImagekit?.fileId) {
|
|
176
187
|
skippedCount += 1;
|
|
177
|
-
console.log(`[migrateFolder] Skipping document ${doc.id} - missing
|
|
188
|
+
console.log(`[migrateFolder] Skipping document ${doc.id} - missing ImageKit file ID`);
|
|
178
189
|
continue;
|
|
179
190
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
try {
|
|
192
|
+
const destinationFolder = getCollectionFolder(toFolder, slug);
|
|
193
|
+
const source = await getSourceImagekitFields({
|
|
194
|
+
client,
|
|
195
|
+
fileId: existingImagekit.fileId
|
|
196
|
+
});
|
|
197
|
+
if (isFileInFolder(source.filePath, destinationFolder)) {
|
|
198
|
+
skippedCount += 1;
|
|
199
|
+
console.log(`[migrateFolder] Skipping document ${doc.id} - ImageKit file ID ${source.fileId} is already in ${destinationFolder}`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (dryRun) {
|
|
203
|
+
console.log(`[migrateFolder] [dry-run] Would copy ImageKit file ID ${source.fileId} from ${source.filePath} to ${destinationFolder}`);
|
|
204
|
+
console.log(`[migrateFolder] [dry-run] Would update Payload document: ${doc.id}`);
|
|
205
|
+
} else {
|
|
206
|
+
const destinationFilePath = path.posix.join(destinationFolder, source.fileName);
|
|
188
207
|
let uploaded;
|
|
189
208
|
try {
|
|
190
209
|
const copyResponse = await client.files.copy({
|
|
191
210
|
destinationPath: destinationFolder,
|
|
192
|
-
sourceFilePath
|
|
211
|
+
sourceFilePath: source.filePath
|
|
193
212
|
}).asResponse();
|
|
194
213
|
let copiedFileId = await readFileIdFromCopyResponse(copyResponse);
|
|
195
214
|
if (!copiedFileId) {
|
|
@@ -199,7 +218,7 @@ export const migrateFolder = async (options)=>{
|
|
|
199
218
|
});
|
|
200
219
|
}
|
|
201
220
|
if (!copiedFileId) {
|
|
202
|
-
throw new Error(`migrateFolder: copy request completed for "${
|
|
221
|
+
throw new Error(`migrateFolder: copy request completed for ImageKit file ID "${source.fileId}" from "${source.filePath}", but the destination asset at "${destinationFilePath}" was not discoverable via ImageKit after retrying.`);
|
|
203
222
|
}
|
|
204
223
|
uploaded = await getUploadedImagekitFields({
|
|
205
224
|
client,
|
|
@@ -209,37 +228,23 @@ export const migrateFolder = async (options)=>{
|
|
|
209
228
|
if (!isImageKitFileNotFoundError(error)) {
|
|
210
229
|
throw error;
|
|
211
230
|
}
|
|
212
|
-
console.warn(`[migrateFolder] Source file not found
|
|
231
|
+
console.warn(`[migrateFolder] Source file not found for ImageKit file ID ${source.fileId}. Falling back to resolved ImageKit URL for "${source.fileName}".`);
|
|
213
232
|
// A private source file 401s on its plain URL, so sign it before ImageKit
|
|
214
233
|
// fetches it, and re-upload as private so the copy keeps its privacy.
|
|
215
234
|
const sourceUrl = isCollectionPrivate ? client.helper.buildSrc({
|
|
216
235
|
expiresIn: signedUrlExpiresIn,
|
|
217
236
|
signed: true,
|
|
218
|
-
src:
|
|
237
|
+
src: source.url,
|
|
219
238
|
urlEndpoint
|
|
220
|
-
}) :
|
|
239
|
+
}) : source.url;
|
|
221
240
|
uploaded = await uploadFileFromURL({
|
|
222
241
|
client,
|
|
223
|
-
fileName:
|
|
242
|
+
fileName: source.fileName,
|
|
224
243
|
folder: destinationFolder,
|
|
225
244
|
isPrivate: isCollectionPrivate,
|
|
226
245
|
sourceUrl
|
|
227
246
|
});
|
|
228
247
|
}
|
|
229
|
-
const canonicalUrl = client.helper.buildSrc({
|
|
230
|
-
src: destinationFilePath,
|
|
231
|
-
urlEndpoint
|
|
232
|
-
});
|
|
233
|
-
const mimeType = doc.mimeType;
|
|
234
|
-
const adminThumbnailURL = isCollectionPrivate ? getPrivateAdminThumbnailURL({
|
|
235
|
-
filename,
|
|
236
|
-
mimeType
|
|
237
|
-
}, slug, apiRoute) : getAdminThumbnailURL({
|
|
238
|
-
imagekit: {
|
|
239
|
-
url: uploaded.url
|
|
240
|
-
},
|
|
241
|
-
mimeType
|
|
242
|
-
});
|
|
243
248
|
// Update the document with new ImageKit data
|
|
244
249
|
await payload.update({
|
|
245
250
|
id: doc.id,
|
|
@@ -250,17 +255,15 @@ export const migrateFolder = async (options)=>{
|
|
|
250
255
|
isPrivate: isCollectionPrivate,
|
|
251
256
|
thumbnailUrl: uploaded.thumbnailUrl,
|
|
252
257
|
url: uploaded.url
|
|
253
|
-
}
|
|
254
|
-
thumbnailURL: adminThumbnailURL || canonicalUrl,
|
|
255
|
-
url: canonicalUrl
|
|
258
|
+
}
|
|
256
259
|
}
|
|
257
260
|
});
|
|
258
261
|
updatedCount += 1;
|
|
259
|
-
console.log(`[migrateFolder] Content migrated! ${
|
|
260
|
-
} catch (error) {
|
|
261
|
-
errorCount += 1;
|
|
262
|
-
console.error(`[migrateFolder] Failed to migrate document "${doc.id}" for file "${filename}":`, error);
|
|
262
|
+
console.log(`[migrateFolder] Content migrated! ${displayName} (${doc.id})`);
|
|
263
263
|
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
errorCount += 1;
|
|
266
|
+
console.error(`[migrateFolder] Failed to migrate document "${doc.id}" for file "${displayName}":`, error);
|
|
264
267
|
}
|
|
265
268
|
}
|
|
266
269
|
results.push({
|
package/dist/migrate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/migrate.ts"],"sourcesContent":["import type { Payload, UploadCollectionSlug } from 'payload'\n\nimport ImageKit from '@imagekit/nodejs'\nimport path from 'node:path'\n\nimport { getAdminThumbnailURL, getPrivateAdminThumbnailURL } from './getAdminThumbnailURL.js'\n\ntype ImageKitCollectionConfig = { private?: boolean } | true\ntype ImageKitCollectionMap = Partial<Record<UploadCollectionSlug, ImageKitCollectionConfig>>\n\nconst DEFAULT_SIGNED_URL_EXPIRES_IN = 900\n\nconst isPrivateCollectionConfig = (value: ImageKitCollectionConfig | undefined): boolean =>\n value !== true && typeof value === 'object' && value?.private === true\n\nexport type MigrateFolderOptions = {\n /**\n * The Payload upload collections to migrate. Keys are collection slugs; the value is\n * `true`, or `{ private: true }` to mark migrated files as ImageKit private files.\n */\n collections: ImageKitCollectionMap\n /**\n * Dry-run mode. When `true`, migration logs what would happen without writing\n * to ImageKit or Payload. Defaults to `true`.\n * @default true\n */\n dryRun?: boolean\n /**\n * The source ImageKit root folder (e.g. `/staging`). Each collection subfolder\n * (`/staging/media`) will be copied into `to`.\n */\n from: string\n /**\n * Optional Payload instance. When provided, the stored `imagekit.*` and `url` fields\n * in the database will be updated to reflect the new file locations after copying.\n */\n payload?: Payload\n /**\n * ImageKit private API key.\n */\n privateKey: string\n /**\n * Expiry (in seconds) for the signed URL used to read a private source file when the\n * fast server-side copy is not possible and the migrator falls back to re-uploading\n * from the source URL. Defaults to 900 (15 minutes).\n */\n signedUrlExpiresIn?: number\n /**\n * The destination ImageKit root folder (e.g. `/production`).\n */\n to: string\n /**\n * ImageKit URL endpoint (e.g. `https://ik.imagekit.io/your_id`). Required to\n * reconstruct canonical `url` values when updating the database.\n */\n urlEndpoint: string\n}\n\nexport type MigrationJobResult = {\n collectionSlug: string\n jobId: string\n status: 'Completed' | 'Pending'\n}\n\nconst normalizeFolder = (folder: string): string => {\n const normalized = folder.trim().replace(/^\\/+|\\/+$/g, '')\n\n if (!normalized) {\n throw new Error('migrateFolder: folder path cannot be empty.')\n }\n\n return `/${normalized}`\n}\n\nconst getCollectionFolder = (rootFolder: string, collectionSlug: string): string =>\n path.posix.join(rootFolder, collectionSlug)\n\nconst getFilePath = (rootFolder: string, collectionSlug: string, filename: string): string =>\n path.posix.join(getCollectionFolder(rootFolder, collectionSlug), filename)\n\nconst ensureFolderExists = async (client: ImageKit, folderPath: string): Promise<void> => {\n const normalized = normalizeFolder(folderPath)\n const segments = normalized.split('/').filter(Boolean)\n\n let parentFolderPath = '/'\n\n for (const folderName of segments) {\n try {\n await client.folders.create({\n folderName,\n parentFolderPath,\n })\n } catch (error) {\n const message =\n error && typeof error === 'object' && 'message' in error ? String(error.message) : ''\n\n if (!/already exists/i.test(message)) {\n throw error\n }\n }\n\n parentFolderPath = path.posix.join(parentFolderPath, folderName)\n }\n}\n\nconst findImageKitFileId = async (\n imagekit: ImageKit,\n filePath: string,\n options?: {\n retries?: number\n retryDelayMs?: number\n },\n): Promise<string | undefined> => {\n const parentPath = path.posix.dirname(filePath)\n const name = path.posix.basename(filePath)\n\n const retries = options?.retries ?? 0\n const retryDelayMs = options?.retryDelayMs ?? 250\n const limit = 100\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n for (let skip = 0; ; skip += limit) {\n const assets = await imagekit.assets.list({\n type: 'file',\n limit,\n path: parentPath === '/' ? '/' : `${parentPath}/`,\n skip,\n })\n\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n break\n }\n }\n\n if (attempt < retries) {\n await new Promise((resolve) => {\n setTimeout(resolve, retryDelayMs)\n })\n }\n }\n\n return undefined\n}\n\nconst isImageKitFileNotFoundError = (error: unknown): boolean => {\n const message =\n error && typeof error === 'object' && 'message' in error ? String(error.message) : ''\n\n return /does not exist|not found|no such file/i.test(message)\n}\n\nconst uploadFileFromURL = async ({\n client,\n fileName,\n folder,\n isPrivate,\n sourceUrl,\n}: {\n client: ImageKit\n fileName: string\n folder: string\n isPrivate?: boolean\n sourceUrl: string\n}) => {\n const uploaded = await client.files.upload({\n file: sourceUrl,\n fileName,\n folder,\n useUniqueFileName: false,\n ...(isPrivate ? { isPrivateFile: true } : {}),\n })\n\n if (!uploaded.fileId || !uploaded.url) {\n throw new Error(`migrateFolder: upload from URL did not return file details for \"${fileName}\".`)\n }\n\n return {\n fileId: uploaded.fileId,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n}\n\nconst getUploadedImagekitFields = async ({\n client,\n fileId,\n}: {\n client: ImageKit\n fileId: string\n}): Promise<{ fileId: string; thumbnailUrl?: string; url: string }> => {\n const file = await client.files.get(fileId)\n\n if (!file.fileId || !file.url) {\n throw new Error(`migrateFolder: missing ImageKit file details for \"${fileId}\".`)\n }\n\n return {\n fileId: file.fileId,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n}\n\nconst readFileIdFromCopyResponse = async (response: Response): Promise<string | undefined> => {\n try {\n const result = (await response.json()) as { file_id?: unknown; fileId?: unknown }\n\n if (typeof result.fileId === 'string' && result.fileId.length > 0) {\n return result.fileId\n }\n\n if (typeof result.file_id === 'string' && result.file_id.length > 0) {\n return result.file_id\n }\n } catch {\n // Ignore empty or non-JSON responses and fall back to asset discovery.\n }\n\n return undefined\n}\n\nexport const migrateFolder = async (\n options: MigrateFolderOptions,\n): Promise<MigrationJobResult[]> => {\n const {\n collections,\n dryRun = true,\n from,\n payload,\n privateKey,\n signedUrlExpiresIn: signedUrlExpiresInOption,\n to,\n urlEndpoint,\n } = options\n\n if (!privateKey?.trim()) {\n throw new Error('migrateFolder: `privateKey` is required.')\n }\n\n if (!urlEndpoint?.trim()) {\n throw new Error('migrateFolder: `urlEndpoint` is required.')\n }\n\n if (!collections || Object.keys(collections).length === 0) {\n throw new Error('migrateFolder: `collections` must include at least one upload collection.')\n }\n\n const privateSlugs = new Set(\n Object.entries(collections)\n .filter(([, value]) => isPrivateCollectionConfig(value))\n .map(([slug]) => slug),\n )\n const signedUrlExpiresIn =\n typeof signedUrlExpiresInOption === 'number' && signedUrlExpiresInOption > 0\n ? signedUrlExpiresInOption\n : DEFAULT_SIGNED_URL_EXPIRES_IN\n const apiRoute = payload?.config.routes?.api ?? '/api'\n\n const fromFolder = normalizeFolder(from)\n const toFolder = normalizeFolder(to)\n\n if (fromFolder === toFolder) {\n throw new Error('migrateFolder: `from` and `to` cannot be the same folder.')\n }\n\n const client = new ImageKit({ privateKey })\n\n console.log(\n `[migrateFolder] Starting ${dryRun ? 'DRY-RUN' : 'LIVE'} migration from ${fromFolder} to ${toFolder} for ${Object.keys(collections).length} collection(s).`,\n )\n\n if (dryRun) {\n console.log(\n `[migrateFolder] [dry-run] Would ensure destination root folder exists: ${toFolder}`,\n )\n } else {\n console.log(`[migrateFolder] Ensuring destination root folder exists: ${toFolder}`)\n await ensureFolderExists(client, toFolder)\n }\n\n // Always copy file-by-file per collection\n const collectionSlugs = Object.keys(collections)\n const results: MigrationJobResult[] = []\n\n // Ensure destination folder structure exists\n if (!dryRun) {\n for (const slug of collectionSlugs) {\n const destinationFolderPath = path.posix.join(toFolder, slug)\n console.log(`[migrateFolder] Ensuring destination folder: ${destinationFolderPath}`)\n await ensureFolderExists(client, destinationFolderPath)\n }\n }\n\n // Process documents directly like the old migration script\n if (payload) {\n console.log('[migrateFolder] Starting document-by-document migration.')\n\n for (const slug of collectionSlugs) {\n const isCollectionPrivate = privateSlugs.has(slug)\n\n console.log(\n `[migrateFolder] Migrating collection \"${slug}\"${isCollectionPrivate ? ' (private)' : ''}...`,\n )\n\n const found = await payload.find({\n collection: slug,\n depth: 0,\n limit: 0,\n pagination: false,\n })\n\n console.log(\n `[migrateFolder] Processing ${found.docs.length} document(s) in collection \"${slug}\".`,\n )\n\n let updatedCount = 0\n let skippedCount = 0\n let errorCount = 0\n\n // Process each document one by one, like the old migration script\n for (let i = 0; i < found.docs.length; i++) {\n const doc = found.docs[i]\n const filename = (doc as { filename?: string }).filename\n const existingImagekit = (doc as any).imagekit\n\n console.log(`[migrateFolder] [${i + 1}/${found.docs.length}] Processing document ${doc.id}`)\n\n // Skip items that don't have ImageKit data or filename\n if (!filename || !existingImagekit?.fileId || !existingImagekit?.url) {\n skippedCount += 1\n console.log(\n `[migrateFolder] Skipping document ${doc.id} - missing filename or ImageKit data`,\n )\n continue\n }\n\n if (dryRun) {\n console.log(`[migrateFolder] [dry-run] Would upload: ${toFolder}/${slug}/${filename}`)\n console.log(`[migrateFolder] [dry-run] Would update Payload document: ${doc.id}`)\n } else {\n try {\n const sourceFilePath = getFilePath(fromFolder, slug, filename)\n const destinationFolder = getCollectionFolder(toFolder, slug)\n const destinationFilePath = getFilePath(toFolder, slug, filename)\n\n let uploaded: { fileId: string; thumbnailUrl?: string; url: string }\n\n try {\n const copyResponse = await client.files\n .copy({\n destinationPath: destinationFolder,\n sourceFilePath,\n })\n .asResponse()\n\n let copiedFileId = await readFileIdFromCopyResponse(copyResponse)\n\n if (!copiedFileId) {\n copiedFileId = await findImageKitFileId(client, destinationFilePath, {\n retries: 20,\n retryDelayMs: 1000,\n })\n }\n\n if (!copiedFileId) {\n throw new Error(\n `migrateFolder: copy request completed for \"${sourceFilePath}\", but the destination asset at \"${destinationFilePath}\" was not discoverable via ImageKit after retrying.`,\n )\n }\n\n uploaded = await getUploadedImagekitFields({\n client,\n fileId: copiedFileId,\n })\n } catch (error) {\n if (!isImageKitFileNotFoundError(error)) {\n throw error\n }\n\n console.warn(\n `[migrateFolder] Source file not found at ${sourceFilePath}. Falling back to stored ImageKit URL for \"${filename}\".`,\n )\n\n // A private source file 401s on its plain URL, so sign it before ImageKit\n // fetches it, and re-upload as private so the copy keeps its privacy.\n const sourceUrl = isCollectionPrivate\n ? client.helper.buildSrc({\n expiresIn: signedUrlExpiresIn,\n signed: true,\n src: existingImagekit.url,\n urlEndpoint,\n })\n : existingImagekit.url\n\n uploaded = await uploadFileFromURL({\n client,\n fileName: filename,\n folder: destinationFolder,\n isPrivate: isCollectionPrivate,\n sourceUrl,\n })\n }\n\n const canonicalUrl = client.helper.buildSrc({\n src: destinationFilePath,\n urlEndpoint,\n })\n\n const mimeType = (doc as { mimeType?: string }).mimeType\n const adminThumbnailURL = isCollectionPrivate\n ? getPrivateAdminThumbnailURL({ filename, mimeType }, slug, apiRoute)\n : getAdminThumbnailURL({ imagekit: { url: uploaded.url }, mimeType })\n\n // Update the document with new ImageKit data\n await payload.update({\n id: doc.id,\n collection: slug,\n data: {\n imagekit: {\n fileId: uploaded.fileId,\n isPrivate: isCollectionPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n thumbnailURL: adminThumbnailURL || canonicalUrl,\n url: canonicalUrl,\n },\n })\n\n updatedCount += 1\n console.log(`[migrateFolder] Content migrated! ${filename} (${doc.id})`)\n } catch (error) {\n errorCount += 1\n console.error(\n `[migrateFolder] Failed to migrate document \"${doc.id}\" for file \"${filename}\":`,\n error,\n )\n }\n }\n }\n\n results.push({\n collectionSlug: slug,\n jobId: `migrate:${slug}`,\n status: dryRun ? 'Pending' : 'Completed',\n })\n\n console.log(\n `[migrateFolder] Collection \"${slug}\" complete: updated=${updatedCount}, skipped=${skippedCount}, errors=${errorCount}.`,\n )\n }\n\n console.log('[migrateFolder] Document-by-document migration complete.')\n } else {\n console.log('[migrateFolder] No Payload instance provided - skipping migration.')\n }\n\n console.log(`[migrateFolder] ${dryRun ? 'DRY-RUN simulation' : 'Migration'} complete.`)\n\n return results\n}\n"],"names":["ImageKit","path","getAdminThumbnailURL","getPrivateAdminThumbnailURL","DEFAULT_SIGNED_URL_EXPIRES_IN","isPrivateCollectionConfig","value","private","normalizeFolder","folder","normalized","trim","replace","Error","getCollectionFolder","rootFolder","collectionSlug","posix","join","getFilePath","filename","ensureFolderExists","client","folderPath","segments","split","filter","Boolean","parentFolderPath","folderName","folders","create","error","message","String","test","findImageKitFileId","imagekit","filePath","options","parentPath","dirname","name","basename","retries","retryDelayMs","limit","attempt","skip","assets","list","type","found","find","asset","fileId","length","Promise","resolve","setTimeout","undefined","isImageKitFileNotFoundError","uploadFileFromURL","fileName","isPrivate","sourceUrl","uploaded","files","upload","file","useUniqueFileName","isPrivateFile","url","thumbnailUrl","getUploadedImagekitFields","get","thumbnail","readFileIdFromCopyResponse","response","result","json","file_id","migrateFolder","collections","dryRun","from","payload","privateKey","signedUrlExpiresIn","signedUrlExpiresInOption","to","urlEndpoint","Object","keys","privateSlugs","Set","entries","map","slug","apiRoute","config","routes","api","fromFolder","toFolder","console","log","collectionSlugs","results","destinationFolderPath","isCollectionPrivate","has","collection","depth","pagination","docs","updatedCount","skippedCount","errorCount","i","doc","existingImagekit","id","sourceFilePath","destinationFolder","destinationFilePath","copyResponse","copy","destinationPath","asResponse","copiedFileId","warn","helper","buildSrc","expiresIn","signed","src","canonicalUrl","mimeType","adminThumbnailURL","update","data","thumbnailURL","push","jobId","status"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/migrate.ts"],"sourcesContent":["import type { Payload, UploadCollectionSlug } from 'payload'\n\nimport ImageKit from '@imagekit/nodejs'\nimport path from 'node:path'\n\ntype ImageKitCollectionConfig = { private?: boolean } | true\ntype ImageKitCollectionMap = Partial<Record<UploadCollectionSlug, ImageKitCollectionConfig>>\n\nconst DEFAULT_SIGNED_URL_EXPIRES_IN = 900\n\nconst isPrivateCollectionConfig = (value: ImageKitCollectionConfig | undefined): boolean =>\n value !== true && typeof value === 'object' && value?.private === true\n\nexport type MigrateFolderOptions = {\n /**\n * The Payload upload collections to migrate. Keys are collection slugs; the value is\n * `true`, or `{ private: true }` to mark migrated files as ImageKit private files.\n */\n collections: ImageKitCollectionMap\n /**\n * Dry-run mode. When `true`, migration logs what would happen without writing\n * to ImageKit or Payload. Defaults to `true`.\n * @default true\n */\n dryRun?: boolean\n /**\n * The source ImageKit root folder (e.g. `/staging`). Each collection subfolder\n * (`/staging/media`) will be copied into `to`.\n */\n from: string\n /**\n * Optional Payload instance. When provided, the stored `imagekit.*` fields in the\n * database will be updated to reflect the new ImageKit asset after copying.\n */\n payload?: Payload\n /**\n * ImageKit private API key.\n */\n privateKey: string\n /**\n * Expiry (in seconds) for the signed URL used to read a private source file when the\n * fast server-side copy is not possible and the migrator falls back to re-uploading\n * from the source URL. Defaults to 900 (15 minutes).\n */\n signedUrlExpiresIn?: number\n /**\n * The destination ImageKit root folder (e.g. `/production`).\n */\n to: string\n /**\n * ImageKit URL endpoint (e.g. `https://ik.imagekit.io/your_id`). Required to sign\n * private source URLs when the migrator falls back to re-uploading from the source URL.\n */\n urlEndpoint: string\n}\n\nexport type MigrationJobResult = {\n collectionSlug: string\n jobId: string\n status: 'Completed' | 'Pending'\n}\n\nconst normalizeFolder = (folder: string): string => {\n const normalized = folder.trim().replace(/^\\/+|\\/+$/g, '')\n\n if (!normalized) {\n throw new Error('migrateFolder: folder path cannot be empty.')\n }\n\n return `/${normalized}`\n}\n\nconst getCollectionFolder = (rootFolder: string, collectionSlug: string): string =>\n path.posix.join(rootFolder, collectionSlug)\n\nconst isFileInFolder = (filePath: string, folderPath: string): boolean =>\n filePath === folderPath || filePath.startsWith(`${folderPath}/`)\n\nconst ensureFolderExists = async (client: ImageKit, folderPath: string): Promise<void> => {\n const normalized = normalizeFolder(folderPath)\n const segments = normalized.split('/').filter(Boolean)\n\n let parentFolderPath = '/'\n\n for (const folderName of segments) {\n try {\n await client.folders.create({\n folderName,\n parentFolderPath,\n })\n } catch (error) {\n const message =\n error && typeof error === 'object' && 'message' in error ? String(error.message) : ''\n\n if (!/already exists/i.test(message)) {\n throw error\n }\n }\n\n parentFolderPath = path.posix.join(parentFolderPath, folderName)\n }\n}\n\nconst findImageKitFileId = async (\n imagekit: ImageKit,\n filePath: string,\n options?: {\n retries?: number\n retryDelayMs?: number\n },\n): Promise<string | undefined> => {\n const parentPath = path.posix.dirname(filePath)\n const name = path.posix.basename(filePath)\n\n const retries = options?.retries ?? 0\n const retryDelayMs = options?.retryDelayMs ?? 250\n const limit = 100\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n for (let skip = 0; ; skip += limit) {\n const assets = await imagekit.assets.list({\n type: 'file',\n limit,\n path: parentPath === '/' ? '/' : `${parentPath}/`,\n skip,\n })\n\n const found = assets.find(\n (asset) => 'fileId' in asset && asset.filePath === filePath && asset.name === name,\n )\n\n if (found && 'fileId' in found) {\n return found.fileId\n }\n\n if (assets.length < limit) {\n break\n }\n }\n\n if (attempt < retries) {\n await new Promise((resolve) => {\n setTimeout(resolve, retryDelayMs)\n })\n }\n }\n\n return undefined\n}\n\nconst isImageKitFileNotFoundError = (error: unknown): boolean => {\n const message =\n error && typeof error === 'object' && 'message' in error ? String(error.message) : ''\n\n return /does not exist|not found|no such file/i.test(message)\n}\n\nconst uploadFileFromURL = async ({\n client,\n fileName,\n folder,\n isPrivate,\n sourceUrl,\n}: {\n client: ImageKit\n fileName: string\n folder: string\n isPrivate?: boolean\n sourceUrl: string\n}) => {\n const uploaded = await client.files.upload({\n file: sourceUrl,\n fileName,\n folder,\n useUniqueFileName: false,\n ...(isPrivate ? { isPrivateFile: true } : {}),\n })\n\n if (!uploaded.fileId || !uploaded.url) {\n throw new Error(`migrateFolder: upload from URL did not return file details for \"${fileName}\".`)\n }\n\n return {\n fileId: uploaded.fileId,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n }\n}\n\nconst getSourceImagekitFields = async ({\n client,\n fileId,\n}: {\n client: ImageKit\n fileId: string\n}): Promise<{\n fileId: string\n fileName: string\n filePath: string\n url: string\n}> => {\n const file = await client.files.get(fileId)\n\n if (!file.fileId || !file.filePath || !file.url) {\n throw new Error(`migrateFolder: missing ImageKit source file details for \"${fileId}\".`)\n }\n\n return {\n fileId: file.fileId,\n fileName: file.name || path.posix.basename(file.filePath),\n filePath: file.filePath,\n url: file.url,\n }\n}\n\nconst getUploadedImagekitFields = async ({\n client,\n fileId,\n}: {\n client: ImageKit\n fileId: string\n}): Promise<{ fileId: string; thumbnailUrl?: string; url: string }> => {\n const file = await client.files.get(fileId)\n\n if (!file.fileId || !file.url) {\n throw new Error(`migrateFolder: missing ImageKit file details for \"${fileId}\".`)\n }\n\n return {\n fileId: file.fileId,\n thumbnailUrl: file.thumbnail,\n url: file.url,\n }\n}\n\nconst readFileIdFromCopyResponse = async (response: Response): Promise<string | undefined> => {\n try {\n const result = (await response.json()) as { file_id?: unknown; fileId?: unknown }\n\n if (typeof result.fileId === 'string' && result.fileId.length > 0) {\n return result.fileId\n }\n\n if (typeof result.file_id === 'string' && result.file_id.length > 0) {\n return result.file_id\n }\n } catch {\n // Ignore empty or non-JSON responses and fall back to asset discovery.\n }\n\n return undefined\n}\n\nexport const migrateFolder = async (\n options: MigrateFolderOptions,\n): Promise<MigrationJobResult[]> => {\n const {\n collections,\n dryRun = true,\n from,\n payload,\n privateKey,\n signedUrlExpiresIn: signedUrlExpiresInOption,\n to,\n urlEndpoint,\n } = options\n\n if (!privateKey?.trim()) {\n throw new Error('migrateFolder: `privateKey` is required.')\n }\n\n if (!urlEndpoint?.trim()) {\n throw new Error('migrateFolder: `urlEndpoint` is required.')\n }\n\n if (!collections || Object.keys(collections).length === 0) {\n throw new Error('migrateFolder: `collections` must include at least one upload collection.')\n }\n\n const privateSlugs = new Set(\n Object.entries(collections)\n .filter(([, value]) => isPrivateCollectionConfig(value))\n .map(([slug]) => slug),\n )\n const signedUrlExpiresIn =\n typeof signedUrlExpiresInOption === 'number' && signedUrlExpiresInOption > 0\n ? signedUrlExpiresInOption\n : DEFAULT_SIGNED_URL_EXPIRES_IN\n const fromFolder = normalizeFolder(from)\n const toFolder = normalizeFolder(to)\n\n if (fromFolder === toFolder) {\n throw new Error('migrateFolder: `from` and `to` cannot be the same folder.')\n }\n\n const client = new ImageKit({ privateKey })\n\n console.log(\n `[migrateFolder] Starting ${dryRun ? 'DRY-RUN' : 'LIVE'} migration from ${fromFolder} to ${toFolder} for ${Object.keys(collections).length} collection(s).`,\n )\n\n if (dryRun) {\n console.log(\n `[migrateFolder] [dry-run] Would ensure destination root folder exists: ${toFolder}`,\n )\n } else {\n console.log(`[migrateFolder] Ensuring destination root folder exists: ${toFolder}`)\n await ensureFolderExists(client, toFolder)\n }\n\n // Always copy file-by-file per collection\n const collectionSlugs = Object.keys(collections)\n const results: MigrationJobResult[] = []\n\n // Ensure destination folder structure exists\n if (!dryRun) {\n for (const slug of collectionSlugs) {\n const destinationFolderPath = path.posix.join(toFolder, slug)\n console.log(`[migrateFolder] Ensuring destination folder: ${destinationFolderPath}`)\n await ensureFolderExists(client, destinationFolderPath)\n }\n }\n\n // Process documents directly like the old migration script\n if (payload) {\n console.log('[migrateFolder] Starting document-by-document migration.')\n\n for (const slug of collectionSlugs) {\n const isCollectionPrivate = privateSlugs.has(slug)\n\n console.log(\n `[migrateFolder] Migrating collection \"${slug}\"${isCollectionPrivate ? ' (private)' : ''}...`,\n )\n\n const found = await payload.find({\n collection: slug,\n depth: 0,\n limit: 0,\n pagination: false,\n })\n\n console.log(\n `[migrateFolder] Processing ${found.docs.length} document(s) in collection \"${slug}\".`,\n )\n\n let updatedCount = 0\n let skippedCount = 0\n let errorCount = 0\n\n // Process each document one by one, like the old migration script\n for (let i = 0; i < found.docs.length; i++) {\n const doc = found.docs[i]\n const filename = (doc as { filename?: string }).filename\n const existingImagekit = (doc as any).imagekit\n const displayName = filename || existingImagekit?.fileId || doc.id\n\n console.log(`[migrateFolder] [${i + 1}/${found.docs.length}] Processing document ${doc.id}`)\n\n // Skip items that don't have an ImageKit file ID to migrate from.\n if (!existingImagekit?.fileId) {\n skippedCount += 1\n console.log(`[migrateFolder] Skipping document ${doc.id} - missing ImageKit file ID`)\n continue\n }\n\n try {\n const destinationFolder = getCollectionFolder(toFolder, slug)\n const source = await getSourceImagekitFields({\n client,\n fileId: existingImagekit.fileId,\n })\n\n if (isFileInFolder(source.filePath, destinationFolder)) {\n skippedCount += 1\n console.log(\n `[migrateFolder] Skipping document ${doc.id} - ImageKit file ID ${source.fileId} is already in ${destinationFolder}`,\n )\n continue\n }\n\n if (dryRun) {\n console.log(\n `[migrateFolder] [dry-run] Would copy ImageKit file ID ${source.fileId} from ${source.filePath} to ${destinationFolder}`,\n )\n console.log(`[migrateFolder] [dry-run] Would update Payload document: ${doc.id}`)\n } else {\n const destinationFilePath = path.posix.join(destinationFolder, source.fileName)\n\n let uploaded: { fileId: string; thumbnailUrl?: string; url: string }\n\n try {\n const copyResponse = await client.files\n .copy({\n destinationPath: destinationFolder,\n sourceFilePath: source.filePath,\n })\n .asResponse()\n\n let copiedFileId = await readFileIdFromCopyResponse(copyResponse)\n\n if (!copiedFileId) {\n copiedFileId = await findImageKitFileId(client, destinationFilePath, {\n retries: 20,\n retryDelayMs: 1000,\n })\n }\n\n if (!copiedFileId) {\n throw new Error(\n `migrateFolder: copy request completed for ImageKit file ID \"${source.fileId}\" from \"${source.filePath}\", but the destination asset at \"${destinationFilePath}\" was not discoverable via ImageKit after retrying.`,\n )\n }\n\n uploaded = await getUploadedImagekitFields({\n client,\n fileId: copiedFileId,\n })\n } catch (error) {\n if (!isImageKitFileNotFoundError(error)) {\n throw error\n }\n\n console.warn(\n `[migrateFolder] Source file not found for ImageKit file ID ${source.fileId}. Falling back to resolved ImageKit URL for \"${source.fileName}\".`,\n )\n\n // A private source file 401s on its plain URL, so sign it before ImageKit\n // fetches it, and re-upload as private so the copy keeps its privacy.\n const sourceUrl = isCollectionPrivate\n ? client.helper.buildSrc({\n expiresIn: signedUrlExpiresIn,\n signed: true,\n src: source.url,\n urlEndpoint,\n })\n : source.url\n\n uploaded = await uploadFileFromURL({\n client,\n fileName: source.fileName,\n folder: destinationFolder,\n isPrivate: isCollectionPrivate,\n sourceUrl,\n })\n }\n\n // Update the document with new ImageKit data\n await payload.update({\n id: doc.id,\n collection: slug,\n data: {\n imagekit: {\n fileId: uploaded.fileId,\n isPrivate: isCollectionPrivate,\n thumbnailUrl: uploaded.thumbnailUrl,\n url: uploaded.url,\n },\n },\n })\n\n updatedCount += 1\n console.log(`[migrateFolder] Content migrated! ${displayName} (${doc.id})`)\n }\n } catch (error) {\n errorCount += 1\n console.error(\n `[migrateFolder] Failed to migrate document \"${doc.id}\" for file \"${displayName}\":`,\n error,\n )\n }\n }\n\n results.push({\n collectionSlug: slug,\n jobId: `migrate:${slug}`,\n status: dryRun ? 'Pending' : 'Completed',\n })\n\n console.log(\n `[migrateFolder] Collection \"${slug}\" complete: updated=${updatedCount}, skipped=${skippedCount}, errors=${errorCount}.`,\n )\n }\n\n console.log('[migrateFolder] Document-by-document migration complete.')\n } else {\n console.log('[migrateFolder] No Payload instance provided - skipping migration.')\n }\n\n console.log(`[migrateFolder] ${dryRun ? 'DRY-RUN simulation' : 'Migration'} complete.`)\n\n return results\n}\n"],"names":["ImageKit","path","DEFAULT_SIGNED_URL_EXPIRES_IN","isPrivateCollectionConfig","value","private","normalizeFolder","folder","normalized","trim","replace","Error","getCollectionFolder","rootFolder","collectionSlug","posix","join","isFileInFolder","filePath","folderPath","startsWith","ensureFolderExists","client","segments","split","filter","Boolean","parentFolderPath","folderName","folders","create","error","message","String","test","findImageKitFileId","imagekit","options","parentPath","dirname","name","basename","retries","retryDelayMs","limit","attempt","skip","assets","list","type","found","find","asset","fileId","length","Promise","resolve","setTimeout","undefined","isImageKitFileNotFoundError","uploadFileFromURL","fileName","isPrivate","sourceUrl","uploaded","files","upload","file","useUniqueFileName","isPrivateFile","url","thumbnailUrl","getSourceImagekitFields","get","getUploadedImagekitFields","thumbnail","readFileIdFromCopyResponse","response","result","json","file_id","migrateFolder","collections","dryRun","from","payload","privateKey","signedUrlExpiresIn","signedUrlExpiresInOption","to","urlEndpoint","Object","keys","privateSlugs","Set","entries","map","slug","fromFolder","toFolder","console","log","collectionSlugs","results","destinationFolderPath","isCollectionPrivate","has","collection","depth","pagination","docs","updatedCount","skippedCount","errorCount","i","doc","filename","existingImagekit","displayName","id","destinationFolder","source","destinationFilePath","copyResponse","copy","destinationPath","sourceFilePath","asResponse","copiedFileId","warn","helper","buildSrc","expiresIn","signed","src","update","data","push","jobId","status"],"mappings":"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"}
|