@maas/payload-plugin-media-cloud 0.0.19 → 0.0.21
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/components/upload-handler/upload-handler.mjs +29 -35
- package/dist/components/upload-handler/upload-handler.mjs.map +1 -1
- package/dist/endpoints/muxAssetHandler.mjs +1 -1
- package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
- package/dist/endpoints/muxWebhookHandler.mjs +1 -1
- package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
- package/dist/endpoints/tusFileExistsHandler.d.mts +11 -0
- package/dist/endpoints/tusFileExistsHandler.mjs +25 -0
- package/dist/endpoints/tusFileExistsHandler.mjs.map +1 -0
- package/dist/types/errors.d.mts +1 -0
- package/dist/types/errors.mjs +1 -0
- package/dist/types/errors.mjs.map +1 -1
- package/dist/utils/tus.mjs +7 -15
- package/dist/utils/tus.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { MediaCloudErrors } from "../../types/errors.mjs";
|
|
3
|
+
import { MediaCloudErrors, MediaCloudLogs } from "../../types/errors.mjs";
|
|
4
4
|
import { useErrorHandler } from "../../hooks/useErrorHandler.mjs";
|
|
5
|
-
import { getFileType, isVideo
|
|
5
|
+
import { generateUniqueFilename, getFileType, isVideo } from "../../utils/file.mjs";
|
|
6
6
|
import { useMediaCloudEmitter } from "../../hooks/useMediaCloudEmitter.mjs";
|
|
7
7
|
import * as upchunk from "@mux/upchunk";
|
|
8
8
|
import * as tus from "tus-js-client";
|
|
@@ -21,26 +21,13 @@ const TUS_RETRY_DELAYS = [
|
|
|
21
21
|
5e3
|
|
22
22
|
];
|
|
23
23
|
/**
|
|
24
|
-
* Utility function to parse upload ID from URL
|
|
25
|
-
* @param uploadUrl - The upload URL to parse
|
|
26
|
-
* @returns The extracted upload ID or empty string if parsing fails
|
|
27
|
-
*/
|
|
28
|
-
function parseFilename(uploadUrl) {
|
|
29
|
-
if (!uploadUrl) {
|
|
30
|
-
logError(MediaCloudErrors.UPLOAD_NO_URL.message);
|
|
31
|
-
return "";
|
|
32
|
-
}
|
|
33
|
-
return new URL(uploadUrl).pathname.split("/").pop() || "";
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
24
|
* Handles Mux video upload with progress tracking
|
|
37
25
|
* @param args - The upload arguments including file, server URL, and callbacks
|
|
38
26
|
* @returns Promise that resolves to upload result or null if upload fails
|
|
39
27
|
*/
|
|
40
28
|
async function muxUpload(args) {
|
|
41
|
-
const { file, serverURL, apiRoute, mimeType
|
|
42
|
-
const filename =
|
|
43
|
-
updateFilename(filename);
|
|
29
|
+
const { file, serverURL, apiRoute, mimeType } = args;
|
|
30
|
+
const filename = file.name;
|
|
44
31
|
try {
|
|
45
32
|
const { url, uploadId } = await (await fetch(`${serverURL}${apiRoute}/mux/upload`, {
|
|
46
33
|
body: JSON.stringify({
|
|
@@ -94,12 +81,12 @@ async function muxUpload(args) {
|
|
|
94
81
|
* @returns Promise that resolves to upload result or null if upload fails
|
|
95
82
|
*/
|
|
96
83
|
async function tusUpload(args) {
|
|
97
|
-
const { apiRoute, serverURL, file, mimeType
|
|
98
|
-
const filename =
|
|
84
|
+
const { apiRoute, serverURL, file, mimeType } = args;
|
|
85
|
+
const filename = file.name;
|
|
99
86
|
const filetype = file.type;
|
|
100
87
|
const filesize = file.size.toString();
|
|
101
88
|
return new Promise((resolve) => {
|
|
102
|
-
|
|
89
|
+
new tus.Upload(file, {
|
|
103
90
|
endpoint: `${serverURL}${apiRoute}/uploads`,
|
|
104
91
|
retryDelays: TUS_RETRY_DELAYS,
|
|
105
92
|
chunkSize: TUS_CHUNK_SIZE,
|
|
@@ -118,33 +105,30 @@ async function tusUpload(args) {
|
|
|
118
105
|
},
|
|
119
106
|
onProgress: function(bytesUploaded, bytesTotal) {
|
|
120
107
|
const percentage = Math.round(bytesUploaded / bytesTotal * 100);
|
|
121
|
-
const filename$1 = parseFilename(upload?.url);
|
|
122
108
|
emitter.emit("updateUpload", {
|
|
123
|
-
filename
|
|
109
|
+
filename,
|
|
124
110
|
progress: percentage
|
|
125
111
|
});
|
|
126
112
|
},
|
|
127
113
|
onSuccess: function() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
fetch(`${serverURL}${apiRoute}/uploads/${filename$1}/process`);
|
|
114
|
+
emitter.emit("uploadComplete", { filename });
|
|
115
|
+
fetch(`${serverURL}${apiRoute}/uploads/${filename}/process`);
|
|
131
116
|
},
|
|
132
|
-
onUploadUrlAvailable: function() {
|
|
133
|
-
|
|
134
|
-
updateFilename(filename$1);
|
|
135
|
-
emitter.emit("addUpload", { filename: filename$1 });
|
|
117
|
+
onUploadUrlAvailable: async function() {
|
|
118
|
+
emitter.emit("addUpload", { filename });
|
|
136
119
|
resolve({
|
|
137
|
-
filename
|
|
120
|
+
filename,
|
|
138
121
|
mimeType,
|
|
139
122
|
storage: "s3"
|
|
140
123
|
});
|
|
141
124
|
}
|
|
142
|
-
});
|
|
143
|
-
upload.start();
|
|
125
|
+
}).start();
|
|
144
126
|
});
|
|
145
127
|
}
|
|
128
|
+
const { log } = useErrorHandler();
|
|
146
129
|
const UploadHandler = createClientUploadHandler({ handler: async function(args) {
|
|
147
130
|
const { serverURL, apiRoute, file, updateFilename } = args;
|
|
131
|
+
console.log("upload handler called", file);
|
|
148
132
|
try {
|
|
149
133
|
const mimeType = await getFileType(file) || file.type || "application/octet-stream";
|
|
150
134
|
if (!mimeType) {
|
|
@@ -152,12 +136,22 @@ const UploadHandler = createClientUploadHandler({ handler: async function(args)
|
|
|
152
136
|
return null;
|
|
153
137
|
}
|
|
154
138
|
const isVideoFile = await isVideo(file);
|
|
139
|
+
let mappedFile = file;
|
|
140
|
+
if (!isVideoFile) try {
|
|
141
|
+
if ((await fetch(`${serverURL}${apiRoute}/uploads/${file.name}/exists`)).status === 200) {
|
|
142
|
+
log(MediaCloudLogs.S3_STORE_FILE_FOUND);
|
|
143
|
+
const newFilename = generateUniqueFilename(file.name);
|
|
144
|
+
mappedFile = new File([file], newFilename, { type: file.type });
|
|
145
|
+
await updateFilename(newFilename);
|
|
146
|
+
}
|
|
147
|
+
} catch (_error) {
|
|
148
|
+
logError(MediaCloudErrors.NAMING_FUNCTION_ERROR.message);
|
|
149
|
+
}
|
|
155
150
|
const uploadArgs = {
|
|
156
|
-
file,
|
|
151
|
+
file: mappedFile,
|
|
157
152
|
serverURL,
|
|
158
153
|
apiRoute,
|
|
159
|
-
mimeType
|
|
160
|
-
updateFilename
|
|
154
|
+
mimeType
|
|
161
155
|
};
|
|
162
156
|
if (isVideoFile) return await muxUpload(uploadArgs);
|
|
163
157
|
else return await tusUpload(uploadArgs);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upload-handler.mjs","names":["
|
|
1
|
+
{"version":3,"file":"upload-handler.mjs","names":["uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudErrors, MediaCloudLogs } from '../../types/errors'\nimport { useMediaCloudEmitter } from '../../hooks/useMediaCloudEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, generateUniqueFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n}\n\ninterface UploadResult {\n filename: string\n uploadId?: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n filename: string\n}\n\nconst { logError, throwError } = useErrorHandler()\nconst emitter = useMediaCloudEmitter()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType } = args\n\n const filename = file.name\n try {\n // Request upload URL from Mux\n const response = await fetch(`${serverURL}${apiRoute}/mux/upload`, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('addUpload', {\n filename,\n uploadId,\n polling: false,\n pollingUrl: `${serverURL}${apiRoute}/mux/asset`,\n })\n\n // Set up event handlers\n uploader.on('error', function () {\n logError(MediaCloudErrors.MUX_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n emitter.emit('removeUpload', { uploadId })\n })\n\n uploader.on('progress', function (progress) {\n emitter.emit('updateUpload', {\n filename,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', function () {\n emitter.emit('uploadComplete', { filename })\n })\n\n // Update collection entry\n // with filename, uploadId, mimeType, and storage\n return {\n filename,\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_DIRECT_UPLOAD_ERROR.message)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n metadata: {\n filetype,\n filesize,\n filename,\n contentType: filetype,\n contentDisposition: 'inline',\n contentLength: filesize,\n },\n onError: function () {\n logError(MediaCloudErrors.TUS_UPLOAD_ERROR.message)\n toast.error('File upload failed')\n resolve(null)\n },\n onProgress: function (bytesUploaded, bytesTotal) {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n emitter.emit('updateUpload', {\n filename,\n progress: percentage,\n })\n },\n onSuccess: function () {\n emitter.emit('uploadComplete', { filename })\n\n // Trigger post upload processing\n fetch(`${serverURL}${apiRoute}/uploads/${filename}/process`)\n },\n onUploadUrlAvailable: async function () {\n // Add upload to UI\n emitter.emit('addUpload', { filename })\n\n // Update collection entry\n // with filename, mimeType, and storage\n resolve({\n filename,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nconst { log } = useErrorHandler()\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async function (args) {\n const { serverURL, apiRoute, file, updateFilename } = args\n console.log('upload handler called', file)\n try {\n const mimeType =\n (await getFileType(file)) || file.type || 'application/octet-stream'\n if (!mimeType) {\n throwError(MediaCloudErrors.FILE_TYPE_UNKNOWN)\n return null\n }\n\n const isVideoFile = await isVideo(file)\n\n // Clone file\n let mappedFile = file\n\n if (!isVideoFile) {\n try {\n // Check if file with same name exists\n const response = await fetch(\n `${serverURL}${apiRoute}/uploads/${file.name}/exists`\n )\n\n // If file exists, generate a cloned file with a unique filename\n if (response.status === 200) {\n log(MediaCloudLogs.S3_STORE_FILE_FOUND)\n\n const newFilename = generateUniqueFilename(file.name)\n mappedFile = new File([file], newFilename, { type: file.type })\n await updateFilename(newFilename)\n }\n } catch (_error) {\n logError(MediaCloudErrors.NAMING_FUNCTION_ERROR.message)\n }\n }\n\n const uploadArgs: UploadArgs = {\n file: mappedFile,\n serverURL,\n apiRoute,\n mimeType,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (error) {\n console.error(\n '[PLUGIN-MEDIA-CLOUD] Upload handler detailed error:',\n error\n )\n logError(MediaCloudErrors.UPLOAD_HANDLER_ERROR.message)\n toast.error(\n `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n )\n return null\n }\n },\n})\n"],"mappings":"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"}
|
|
@@ -21,7 +21,7 @@ function getMuxAssetHandler(args) {
|
|
|
21
21
|
pagination: false
|
|
22
22
|
});
|
|
23
23
|
if (docs.length > 0) {
|
|
24
|
-
const { id
|
|
24
|
+
const { id } = docs[0];
|
|
25
25
|
const width = Array.from(asset.tracks ?? []).reduce((max, track) => track.type === "video" && track.max_width ? Math.max(max, track.max_width) : max, 0);
|
|
26
26
|
const height = Array.from(asset.tracks ?? []).reduce((max, track) => track.type === "video" && track.max_height ? Math.max(max, track.max_height) : max, 0);
|
|
27
27
|
await payload.update({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"muxAssetHandler.mjs","names":[],"sources":["../../src/endpoints/muxAssetHandler.ts"],"sourcesContent":["import type { Mux } from '@mux/mux-node'\nimport type { PayloadHandler } from 'payload'\nimport type { StaticRenditions } from '../types'\n\ninterface GetMuxAssetHandlerArgs {\n getMuxClient: () => Mux\n}\n\nexport function getMuxAssetHandler(\n args: GetMuxAssetHandlerArgs\n): PayloadHandler {\n const { getMuxClient } = args\n\n return async (req) => {\n try {\n const mux = getMuxClient()\n const { query } = req\n\n const uploadId = query.upload_id as string\n\n if (!uploadId) {\n return Response.json(\n { message: 'Upload ID is required' },\n { status: 400 }\n )\n }\n\n const assets = await mux.video.assets.list({\n limit: 1,\n upload_id: uploadId,\n })\n\n const asset = assets?.data[0]\n\n if (!asset) {\n return Response.json(\n { message: 'No asset found for the given upload ID' },\n { status: 404 }\n )\n }\n\n if (asset.status === 'ready') {\n const { payload } = req\n\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n 'mux.uploadId': {\n equals: uploadId,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n if (docs.length > 0) {\n const { id
|
|
1
|
+
{"version":3,"file":"muxAssetHandler.mjs","names":[],"sources":["../../src/endpoints/muxAssetHandler.ts"],"sourcesContent":["import type { Mux } from '@mux/mux-node'\nimport type { PayloadHandler } from 'payload'\nimport type { StaticRenditions } from '../types'\n\ninterface GetMuxAssetHandlerArgs {\n getMuxClient: () => Mux\n}\n\nexport function getMuxAssetHandler(\n args: GetMuxAssetHandlerArgs\n): PayloadHandler {\n const { getMuxClient } = args\n\n return async (req) => {\n try {\n const mux = getMuxClient()\n const { query } = req\n\n const uploadId = query.upload_id as string\n\n if (!uploadId) {\n return Response.json(\n { message: 'Upload ID is required' },\n { status: 400 }\n )\n }\n\n const assets = await mux.video.assets.list({\n limit: 1,\n upload_id: uploadId,\n })\n\n const asset = assets?.data[0]\n\n if (!asset) {\n return Response.json(\n { message: 'No asset found for the given upload ID' },\n { status: 404 }\n )\n }\n\n if (asset.status === 'ready') {\n const { payload } = req\n\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n 'mux.uploadId': {\n equals: uploadId,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n if (docs.length > 0) {\n const { id } = docs[0]\n\n const width = Array.from(asset.tracks ?? []).reduce(\n (max, track) =>\n track.type === 'video' && track.max_width\n ? Math.max(max, track.max_width)\n : max,\n 0\n )\n\n const height = Array.from(asset.tracks ?? []).reduce(\n (max, track) =>\n track.type === 'video' && track.max_height\n ? Math.max(max, track.max_height)\n : max,\n 0\n )\n\n await payload.update({\n collection: 'media',\n id,\n data: {\n mux: {\n status: asset.status,\n assetId: asset.id,\n playbackId: asset.playback_ids?.[0]?.id,\n aspectRatio: asset.aspect_ratio,\n duration: asset.duration,\n tracks: asset.tracks,\n maxResolutionTier: asset.max_resolution_tier,\n videoQuality: asset.video_quality,\n staticRenditions: asset.static_renditions as StaticRenditions,\n },\n width,\n height,\n },\n })\n }\n\n return Response.json(\n {\n ready: asset.status === 'ready',\n asset,\n },\n { status: 200 }\n )\n } else {\n return Response.json(\n {\n ready: false,\n asset,\n },\n { status: 200 }\n )\n }\n } catch (_error) {\n return Response.json(\n { message: 'Failed to fetch Mux asset' },\n { status: 500 }\n )\n }\n }\n}\n"],"mappings":";AAQA,SAAgB,mBACd,MACgB;CAChB,MAAM,EAAE,iBAAiB;AAEzB,QAAO,OAAO,QAAQ;AACpB,MAAI;GACF,MAAM,MAAM,cAAc;GAC1B,MAAM,EAAE,UAAU;GAElB,MAAM,WAAW,MAAM;AAEvB,OAAI,CAAC,SACH,QAAO,SAAS,KACd,EAAE,SAAS,yBAAyB,EACpC,EAAE,QAAQ,KAAK,CAChB;GAQH,MAAM,SALS,MAAM,IAAI,MAAM,OAAO,KAAK;IACzC,OAAO;IACP,WAAW;IACZ,CAAC,GAEoB,KAAK;AAE3B,OAAI,CAAC,MACH,QAAO,SAAS,KACd,EAAE,SAAS,0CAA0C,EACrD,EAAE,QAAQ,KAAK,CAChB;AAGH,OAAI,MAAM,WAAW,SAAS;IAC5B,MAAM,EAAE,YAAY;IAEpB,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;KAClC,YAAY;KACZ,OAAO,EACL,gBAAgB,EACd,QAAQ,UACT,EACF;KACD,OAAO;KACP,YAAY;KACb,CAAC;AAEF,QAAI,KAAK,SAAS,GAAG;KACnB,MAAM,EAAE,OAAO,KAAK;KAEpB,MAAM,QAAQ,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,QAC1C,KAAK,UACJ,MAAM,SAAS,WAAW,MAAM,YAC5B,KAAK,IAAI,KAAK,MAAM,UAAU,GAC9B,KACN,EACD;KAED,MAAM,SAAS,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,QAC3C,KAAK,UACJ,MAAM,SAAS,WAAW,MAAM,aAC5B,KAAK,IAAI,KAAK,MAAM,WAAW,GAC/B,KACN,EACD;AAED,WAAM,QAAQ,OAAO;MACnB,YAAY;MACZ;MACA,MAAM;OACJ,KAAK;QACH,QAAQ,MAAM;QACd,SAAS,MAAM;QACf,YAAY,MAAM,eAAe,IAAI;QACrC,aAAa,MAAM;QACnB,UAAU,MAAM;QAChB,QAAQ,MAAM;QACd,mBAAmB,MAAM;QACzB,cAAc,MAAM;QACpB,kBAAkB,MAAM;QACzB;OACD;OACA;OACD;MACF,CAAC;;AAGJ,WAAO,SAAS,KACd;KACE,OAAO,MAAM,WAAW;KACxB;KACD,EACD,EAAE,QAAQ,KAAK,CAChB;SAED,QAAO,SAAS,KACd;IACE,OAAO;IACP;IACD,EACD,EAAE,QAAQ,KAAK,CAChB;WAEI,QAAQ;AACf,UAAO,SAAS,KACd,EAAE,SAAS,6BAA6B,EACxC,EAAE,QAAQ,KAAK,CAChB"}
|
|
@@ -44,7 +44,7 @@ async function updateMuxAsset(args) {
|
|
|
44
44
|
pagination: false
|
|
45
45
|
});
|
|
46
46
|
if (docs.length > 0) {
|
|
47
|
-
const { id
|
|
47
|
+
const { id } = docs[0];
|
|
48
48
|
const width = Array.from(asset.tracks ?? []).reduce((max, track) => track.type === "video" && track.max_width ? Math.max(max, track.max_width) : max, 0);
|
|
49
49
|
const height = Array.from(asset.tracks ?? []).reduce((max, track) => track.type === "video" && track.max_height ? Math.max(max, track.max_height) : max, 0);
|
|
50
50
|
await payload.update({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"muxWebhookHandler.mjs","names":[],"sources":["../../src/endpoints/muxWebhookHandler.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { Mux } from '@mux/mux-node'\nimport type { BasePayload, PayloadHandler } from 'payload'\nimport type { StaticRenditions } from '../types'\n\ninterface GetMuxWebhookHandlerArgs {\n getMuxClient: () => Mux\n}\n\nconst { logError, log } = useErrorHandler()\n\nexport function getMuxWebhookHandler(\n args: GetMuxWebhookHandlerArgs\n): PayloadHandler {\n const { getMuxClient } = args\n\n return async (req) => {\n const mux = getMuxClient()\n\n try {\n const body = req.text ? await req.text() : ''\n const headers = req.headers\n\n if (!body) {\n logError(MediaCloudErrors.MUX_WEBHOOK_BODY_INVALID.message)\n }\n\n // Verify the webhook signature\n mux.webhooks.verifySignature(body, headers)\n\n // Parse the request body\n const event = JSON.parse(body)\n\n // Handle the event\n switch (event.type) {\n case 'video.asset.created':\n case 'video.asset.errored':\n case 'video.asset.deleted':\n log(`Received Mux webhook: ${event.type}, ${event.object.id}`)\n break\n case 'video.asset.ready':\n case 'video.asset.static_renditions.ready':\n case 'video.asset.static_renditions.deleted':\n await updateMuxAsset({ asset: event.object, payload: req.payload })\n break\n }\n\n return Response.json({ message: 'Webhook received' }, { status: 200 })\n } catch (error) {\n return Response.json(\n { message: error instanceof Error ? error.message : String(error) },\n { status: 500 }\n )\n }\n }\n}\n\ninterface UpdateMuxAssetArgs {\n asset: Mux.Video.Asset\n payload: BasePayload\n}\n\nasync function updateMuxAsset(args: UpdateMuxAssetArgs): Promise<void> {\n const { asset, payload } = args\n\n if (asset.status === 'ready') {\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n 'mux.assetId': {\n equals: asset.id,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n if (docs.length > 0) {\n const { id
|
|
1
|
+
{"version":3,"file":"muxWebhookHandler.mjs","names":[],"sources":["../../src/endpoints/muxWebhookHandler.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { Mux } from '@mux/mux-node'\nimport type { BasePayload, PayloadHandler } from 'payload'\nimport type { StaticRenditions } from '../types'\n\ninterface GetMuxWebhookHandlerArgs {\n getMuxClient: () => Mux\n}\n\nconst { logError, log } = useErrorHandler()\n\nexport function getMuxWebhookHandler(\n args: GetMuxWebhookHandlerArgs\n): PayloadHandler {\n const { getMuxClient } = args\n\n return async (req) => {\n const mux = getMuxClient()\n\n try {\n const body = req.text ? await req.text() : ''\n const headers = req.headers\n\n if (!body) {\n logError(MediaCloudErrors.MUX_WEBHOOK_BODY_INVALID.message)\n }\n\n // Verify the webhook signature\n mux.webhooks.verifySignature(body, headers)\n\n // Parse the request body\n const event = JSON.parse(body)\n\n // Handle the event\n switch (event.type) {\n case 'video.asset.created':\n case 'video.asset.errored':\n case 'video.asset.deleted':\n log(`Received Mux webhook: ${event.type}, ${event.object.id}`)\n break\n case 'video.asset.ready':\n case 'video.asset.static_renditions.ready':\n case 'video.asset.static_renditions.deleted':\n await updateMuxAsset({ asset: event.object, payload: req.payload })\n break\n }\n\n return Response.json({ message: 'Webhook received' }, { status: 200 })\n } catch (error) {\n return Response.json(\n { message: error instanceof Error ? error.message : String(error) },\n { status: 500 }\n )\n }\n }\n}\n\ninterface UpdateMuxAssetArgs {\n asset: Mux.Video.Asset\n payload: BasePayload\n}\n\nasync function updateMuxAsset(args: UpdateMuxAssetArgs): Promise<void> {\n const { asset, payload } = args\n\n if (asset.status === 'ready') {\n const { docs } = await payload.find({\n collection: 'media',\n where: {\n 'mux.assetId': {\n equals: asset.id,\n },\n },\n limit: 1,\n pagination: false,\n })\n\n if (docs.length > 0) {\n const { id } = docs[0]\n\n const width = Array.from(asset.tracks ?? []).reduce(\n (max, track) =>\n track.type === 'video' && track.max_width\n ? Math.max(max, track.max_width)\n : max,\n 0\n )\n\n const height = Array.from(asset.tracks ?? []).reduce(\n (max, track) =>\n track.type === 'video' && track.max_height\n ? Math.max(max, track.max_height)\n : max,\n 0\n )\n\n await payload.update({\n collection: 'media',\n id,\n data: {\n mux: {\n status: asset.status,\n assetId: asset.id,\n playbackId: asset.playback_ids?.[0]?.id,\n aspectRatio: asset.aspect_ratio,\n duration: asset.duration,\n tracks: asset.tracks,\n maxResolutionTier: asset.max_resolution_tier,\n videoQuality: asset.video_quality,\n staticRenditions: asset.static_renditions as StaticRenditions,\n },\n width,\n height,\n },\n })\n }\n }\n}\n"],"mappings":";;;;AAWA,MAAM,EAAE,UAAU,QAAQ,iBAAiB;AAE3C,SAAgB,qBACd,MACgB;CAChB,MAAM,EAAE,iBAAiB;AAEzB,QAAO,OAAO,QAAQ;EACpB,MAAM,MAAM,cAAc;AAE1B,MAAI;GACF,MAAM,OAAO,IAAI,OAAO,MAAM,IAAI,MAAM,GAAG;GAC3C,MAAM,UAAU,IAAI;AAEpB,OAAI,CAAC,KACH,UAAS,iBAAiB,yBAAyB,QAAQ;AAI7D,OAAI,SAAS,gBAAgB,MAAM,QAAQ;GAG3C,MAAM,QAAQ,KAAK,MAAM,KAAK;AAG9B,WAAQ,MAAM,MAAd;IACE,KAAK;IACL,KAAK;IACL,KAAK;AACH,SAAI,yBAAyB,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK;AAC9D;IACF,KAAK;IACL,KAAK;IACL,KAAK;AACH,WAAM,eAAe;MAAE,OAAO,MAAM;MAAQ,SAAS,IAAI;MAAS,CAAC;AACnE;;AAGJ,UAAO,SAAS,KAAK,EAAE,SAAS,oBAAoB,EAAE,EAAE,QAAQ,KAAK,CAAC;WAC/D,OAAO;AACd,UAAO,SAAS,KACd,EAAE,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE,EACnE,EAAE,QAAQ,KAAK,CAChB;;;;AAUP,eAAe,eAAe,MAAyC;CACrE,MAAM,EAAE,OAAO,YAAY;AAE3B,KAAI,MAAM,WAAW,SAAS;EAC5B,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;GAClC,YAAY;GACZ,OAAO,EACL,eAAe,EACb,QAAQ,MAAM,IACf,EACF;GACD,OAAO;GACP,YAAY;GACb,CAAC;AAEF,MAAI,KAAK,SAAS,GAAG;GACnB,MAAM,EAAE,OAAO,KAAK;GAEpB,MAAM,QAAQ,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,QAC1C,KAAK,UACJ,MAAM,SAAS,WAAW,MAAM,YAC5B,KAAK,IAAI,KAAK,MAAM,UAAU,GAC9B,KACN,EACD;GAED,MAAM,SAAS,MAAM,KAAK,MAAM,UAAU,EAAE,CAAC,CAAC,QAC3C,KAAK,UACJ,MAAM,SAAS,WAAW,MAAM,aAC5B,KAAK,IAAI,KAAK,MAAM,WAAW,GAC/B,KACN,EACD;AAED,SAAM,QAAQ,OAAO;IACnB,YAAY;IACZ;IACA,MAAM;KACJ,KAAK;MACH,QAAQ,MAAM;MACd,SAAS,MAAM;MACf,YAAY,MAAM,eAAe,IAAI;MACrC,aAAa,MAAM;MACnB,UAAU,MAAM;MAChB,QAAQ,MAAM;MACd,mBAAmB,MAAM;MACzB,cAAc,MAAM;MACpB,kBAAkB,MAAM;MACzB;KACD;KACA;KACD;IACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { S3Store } from "../tus/stores/s3/s3-store.mjs";
|
|
2
|
+
import { PayloadHandler } from "payload";
|
|
3
|
+
|
|
4
|
+
//#region src/endpoints/tusFileExistsHandler.d.ts
|
|
5
|
+
interface GetTusPostProcessorHandlerArgs {
|
|
6
|
+
s3Store: S3Store;
|
|
7
|
+
}
|
|
8
|
+
declare function getTusFileExistsHandler(args: GetTusPostProcessorHandlerArgs): PayloadHandler;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { getTusFileExistsHandler };
|
|
11
|
+
//# sourceMappingURL=tusFileExistsHandler.d.mts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { MediaCloudErrors } from "../types/errors.mjs";
|
|
2
|
+
import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/endpoints/tusFileExistsHandler.ts
|
|
5
|
+
function getTusFileExistsHandler(args) {
|
|
6
|
+
const { s3Store } = args;
|
|
7
|
+
const { throwError, logError } = useErrorHandler();
|
|
8
|
+
return async function(req) {
|
|
9
|
+
try {
|
|
10
|
+
const { routeParams } = req;
|
|
11
|
+
const filename = routeParams?.filename;
|
|
12
|
+
if (!filename) throwError(MediaCloudErrors.FILE_MISSING_NAME);
|
|
13
|
+
const url = s3Store.getUrl(filename);
|
|
14
|
+
if ((await fetch(url)).status === 200) return Response.json({ message: "File found" }, { status: 200 });
|
|
15
|
+
return Response.json({ message: "File not found" }, { status: 404 });
|
|
16
|
+
} catch (_error) {
|
|
17
|
+
logError(MediaCloudErrors.FILE_DIMENSIONS_ERROR.message);
|
|
18
|
+
return Response.json({ message: "Failed to process asset" }, { status: 500 });
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { getTusFileExistsHandler };
|
|
25
|
+
//# sourceMappingURL=tusFileExistsHandler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tusFileExistsHandler.mjs","names":[],"sources":["../../src/endpoints/tusFileExistsHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport { S3Store } from '../tus/stores/s3/s3-store'\n\ninterface GetTusPostProcessorHandlerArgs {\n s3Store: S3Store\n}\n\nexport function getTusFileExistsHandler(\n args: GetTusPostProcessorHandlerArgs\n): PayloadHandler {\n const { s3Store } = args\n const { throwError, logError } = useErrorHandler()\n\n return async function (req) {\n try {\n const { routeParams } = req\n const filename = routeParams?.filename as string\n\n if (!filename) {\n throwError(MediaCloudErrors.FILE_MISSING_NAME)\n }\n\n const url = s3Store.getUrl(filename)\n const response = await fetch(url)\n\n if (response.status === 200) {\n return Response.json({ message: 'File found' }, { status: 200 })\n }\n\n return Response.json({ message: 'File not found' }, { status: 404 })\n } catch (_error) {\n logError(MediaCloudErrors.FILE_DIMENSIONS_ERROR.message)\n return Response.json(\n { message: 'Failed to process asset' },\n { status: 500 }\n )\n }\n }\n}\n"],"mappings":";;;;AAUA,SAAgB,wBACd,MACgB;CAChB,MAAM,EAAE,YAAY;CACpB,MAAM,EAAE,YAAY,aAAa,iBAAiB;AAElD,QAAO,eAAgB,KAAK;AAC1B,MAAI;GACF,MAAM,EAAE,gBAAgB;GACxB,MAAM,WAAW,aAAa;AAE9B,OAAI,CAAC,SACH,YAAW,iBAAiB,kBAAkB;GAGhD,MAAM,MAAM,QAAQ,OAAO,SAAS;AAGpC,QAFiB,MAAM,MAAM,IAAI,EAEpB,WAAW,IACtB,QAAO,SAAS,KAAK,EAAE,SAAS,cAAc,EAAE,EAAE,QAAQ,KAAK,CAAC;AAGlE,UAAO,SAAS,KAAK,EAAE,SAAS,kBAAkB,EAAE,EAAE,QAAQ,KAAK,CAAC;WAC7D,QAAQ;AACf,YAAS,iBAAiB,sBAAsB,QAAQ;AACxD,UAAO,SAAS,KACd,EAAE,SAAS,2BAA2B,EACtC,EAAE,QAAQ,KAAK,CAChB"}
|
package/dist/types/errors.d.mts
CHANGED
|
@@ -114,6 +114,7 @@ declare enum MediaCloudLogs {
|
|
|
114
114
|
S3_STORE_READ_RETRY = "Failed to read file, trying again",
|
|
115
115
|
S3_STORE_READ_FAILED = "Failed to read file",
|
|
116
116
|
S3_STORE_FILE_NOT_FOUND = "No file found",
|
|
117
|
+
S3_STORE_FILE_FOUND = "File found",
|
|
117
118
|
S3_STORE_RETRIEVE_PARTS_ERROR = "Error retrieving parts",
|
|
118
119
|
S3_STORE_METADATA_SAVING = "Saving metadata",
|
|
119
120
|
S3_STORE_METADATA_SAVED = "Metadata file saved",
|
package/dist/types/errors.mjs
CHANGED
|
@@ -114,6 +114,7 @@ let MediaCloudLogs = /* @__PURE__ */ function(MediaCloudLogs$1) {
|
|
|
114
114
|
MediaCloudLogs$1["S3_STORE_READ_RETRY"] = "Failed to read file, trying again";
|
|
115
115
|
MediaCloudLogs$1["S3_STORE_READ_FAILED"] = "Failed to read file";
|
|
116
116
|
MediaCloudLogs$1["S3_STORE_FILE_NOT_FOUND"] = "No file found";
|
|
117
|
+
MediaCloudLogs$1["S3_STORE_FILE_FOUND"] = "File found";
|
|
117
118
|
MediaCloudLogs$1["S3_STORE_RETRIEVE_PARTS_ERROR"] = "Error retrieving parts";
|
|
118
119
|
MediaCloudLogs$1["S3_STORE_METADATA_SAVING"] = "Saving metadata";
|
|
119
120
|
MediaCloudLogs$1["S3_STORE_METADATA_SAVED"] = "Metadata file saved";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n MUX_WEBHOOK_BODY_INVALID: {\n message: 'Invalid Mux webhook body',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n\n // File Errors\n FILE_TYPE_UNKNOWN: {\n message: 'Unable to determine file type',\n errorCode: 400,\n },\n FILE_TYPE_ERROR: {\n message: 'Error determining file type',\n errorCode: 500,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_METADATA_CACHE_CLEARED = 'Removing cached data',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,MAAa,mBAAmB;CAE9B,oBAAoB;EAClB,SACE;EACF,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,wBAAwB;EACtB,SAAS;EACT,WAAW;EACZ;CACD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,0BAA0B;EACxB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SACE;EACF,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CAGD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,gBAAgB;EACd,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,eAAe;EACb,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACF;AAED,IAAY,4DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/types/errors.ts"],"sourcesContent":["export const MediaCloudErrors = {\n // Mux Errors\n MUX_CONFIG_MISSING: {\n message:\n 'Mux configuration (tokenId and tokenSecret) must be provided in pluginOptions to use Mux',\n errorCode: 400,\n },\n MUX_CONFIG_INCOMPLETE: {\n message: 'Mux configuration is missing. Mux features will not be available',\n errorCode: 400,\n },\n MUX_UPLOAD_ID_MISSING: {\n message: 'No upload-id found for upload',\n errorCode: 400,\n },\n MUX_ASSET_DELETE_ERROR: {\n message: 'Error deleting Mux asset',\n errorCode: 500,\n },\n MUX_UPLOAD_ERROR: {\n message: 'Mux video upload failed',\n errorCode: 500,\n },\n MUX_DIRECT_UPLOAD_ERROR: {\n message: 'Mux direct upload failed',\n errorCode: 500,\n },\n MUX_CREATE_UPLOAD_ERROR: {\n message: 'Error in Mux create upload handler',\n errorCode: 500,\n },\n MUX_REQUEST_NO_JSON: {\n message: 'Request does not support json() method',\n errorCode: 400,\n },\n MUX_WEBHOOK_BODY_INVALID: {\n message: 'Invalid Mux webhook body',\n errorCode: 400,\n },\n\n // S3 Errors\n S3_CONFIG_MISSING: {\n message:\n 'S3 configuration (bucket, region, accessKeyId, secretAccessKey) must be provided in pluginOptions',\n errorCode: 400,\n },\n S3_DELETE_ERROR: {\n message: 'Error deleting file from S3',\n errorCode: 500,\n },\n S3_GET_OBJECT_ERROR: {\n message: 'Error getting object from S3',\n errorCode: 500,\n },\n S3_UNIQUE_NAME_ERROR: {\n message: 'Could not find a unique file name after maximum tries',\n errorCode: 500,\n },\n\n // TUS Errors\n TUS_UPLOAD_ERROR: {\n message: 'TUS file upload error occurred',\n errorCode: 500,\n },\n\n // General Errors\n UNKNOWN_STORAGE_TYPE: {\n message: 'Unknown storage type for media',\n errorCode: 500,\n },\n\n // File Errors\n FILE_TYPE_UNKNOWN: {\n message: 'Unable to determine file type',\n errorCode: 400,\n },\n FILE_TYPE_ERROR: {\n message: 'Error determining file type',\n errorCode: 500,\n },\n FILENAME_SANITIZE_ERROR: {\n message: 'Error sanitizing filename',\n errorCode: 500,\n },\n FILE_MISSING_NAME: {\n message: 'Filename is missing, cannot parse file',\n errorCode: 500,\n },\n FILE_NOT_FOUND: {\n message: 'File not found',\n errorCode: 404,\n },\n FILE_DIMENSIONS_ERROR: {\n message: 'Error setting media dimensions',\n errorCode: 500,\n },\n\n // Upload Errors\n UPLOAD_NO_URL: {\n message: 'No upload URL provided, cannot parse upload ID',\n errorCode: 400,\n },\n UPLOAD_HANDLER_ERROR: {\n message: 'Upload handler error occurred',\n errorCode: 500,\n },\n UPLOAD_POLLING_ERROR: {\n message: 'Polling error for upload',\n errorCode: 500,\n },\n\n // Plugin Errors\n PLUGIN_NOT_CONFIGURED: {\n message: 'Payload Media Cloud plugin is not configured',\n errorCode: 500,\n },\n NAMING_FUNCTION_ERROR: {\n message: 'Error in namingFunction',\n errorCode: 500,\n },\n} as const\n\nexport enum MediaCloudLogs {\n S3_STORE_MULTIPART_INIT = 'Initializing multipart upload',\n S3_STORE_MULTIPART_CREATED = 'Multipart upload created',\n S3_STORE_UPLOAD_FAILED = 'Failed to finish upload',\n S3_STORE_READ_ATTEMPT = 'Attempting to read file from S3',\n S3_STORE_READ_SUCCESS = 'Successfully read file from S3',\n S3_STORE_READ_RETRY = 'Failed to read file, trying again',\n S3_STORE_READ_FAILED = 'Failed to read file',\n S3_STORE_FILE_NOT_FOUND = 'No file found',\n S3_STORE_FILE_FOUND = 'File found',\n S3_STORE_RETRIEVE_PARTS_ERROR = 'Error retrieving parts',\n S3_STORE_METADATA_SAVING = 'Saving metadata',\n S3_STORE_METADATA_SAVED = 'Metadata file saved',\n S3_STORE_METADATA_CACHE_CLEARED = 'Removing cached data',\n S3_STORE_INCOMPLETE_PART_UPLOADED = 'Finished uploading incomplete part',\n S3_STORE_CHUNK_REMOVAL_FAILED = 'Failed to remove chunk',\n}\n"],"mappings":";AAAA,MAAa,mBAAmB;CAE9B,oBAAoB;EAClB,SACE;EACF,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,wBAAwB;EACtB,SAAS;EACT,WAAW;EACZ;CACD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,0BAA0B;EACxB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SACE;EACF,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,qBAAqB;EACnB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,kBAAkB;EAChB,SAAS;EACT,WAAW;EACZ;CAGD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,iBAAiB;EACf,SAAS;EACT,WAAW;EACZ;CACD,yBAAyB;EACvB,SAAS;EACT,WAAW;EACZ;CACD,mBAAmB;EACjB,SAAS;EACT,WAAW;EACZ;CACD,gBAAgB;EACd,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CAGD,eAAe;EACb,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CACD,sBAAsB;EACpB,SAAS;EACT,WAAW;EACZ;CAGD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACD,uBAAuB;EACrB,SAAS;EACT,WAAW;EACZ;CACF;AAED,IAAY,4DAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/utils/tus.mjs
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
|
-
import { MediaCloudErrors } from "../types/errors.mjs";
|
|
2
|
-
import { useErrorHandler } from "../hooks/useErrorHandler.mjs";
|
|
3
1
|
import { generateUniqueFilename } from "./file.mjs";
|
|
4
2
|
import { getTusPostProcessorHandler } from "../endpoints/tusPostProcessorHandler.mjs";
|
|
3
|
+
import { getTusFileExistsHandler } from "../endpoints/tusFileExistsHandler.mjs";
|
|
5
4
|
import { Server } from "@tus/server";
|
|
6
5
|
|
|
7
6
|
//#region src/utils/tus.ts
|
|
8
|
-
const { logError } = useErrorHandler();
|
|
9
7
|
function createTusServer(args) {
|
|
10
8
|
const { s3Store, pluginOptions } = args;
|
|
11
9
|
return new Server({
|
|
12
10
|
datastore: s3Store,
|
|
13
11
|
namingFunction: async (_req, metadata) => {
|
|
14
|
-
|
|
15
|
-
const { client, bucket } = s3Store;
|
|
16
|
-
if (!metadata?.filename) return generateUniqueFilename("");
|
|
17
|
-
if (await client.getObject({
|
|
18
|
-
Key: metadata?.filename ?? "",
|
|
19
|
-
Bucket: bucket
|
|
20
|
-
}).catch(() => logError(MediaCloudErrors.S3_GET_OBJECT_ERROR.message))) return generateUniqueFilename(metadata?.filename ?? "");
|
|
21
|
-
else return metadata.filename;
|
|
22
|
-
} catch (_error) {
|
|
23
|
-
logError(MediaCloudErrors.NAMING_FUNCTION_ERROR.message);
|
|
24
|
-
return metadata?.filename ?? generateUniqueFilename("");
|
|
25
|
-
}
|
|
12
|
+
return metadata?.filename ?? generateUniqueFilename("");
|
|
26
13
|
},
|
|
27
14
|
path: "/api/uploads",
|
|
28
15
|
respectForwardedHeaders: pluginOptions.s3.respectForwardedHeaders ?? true
|
|
@@ -69,6 +56,11 @@ function createTusEndpoints(args) {
|
|
|
69
56
|
method: "delete",
|
|
70
57
|
path: "/uploads/:id"
|
|
71
58
|
},
|
|
59
|
+
{
|
|
60
|
+
handler: getTusFileExistsHandler({ s3Store }),
|
|
61
|
+
method: "get",
|
|
62
|
+
path: "/uploads/:filename/exists"
|
|
63
|
+
},
|
|
72
64
|
{
|
|
73
65
|
handler: getTusPostProcessorHandler({ s3Store }),
|
|
74
66
|
method: "get",
|
package/dist/utils/tus.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tus.mjs","names":["TusServer"],"sources":["../../src/utils/tus.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"tus.mjs","names":["TusServer"],"sources":["../../src/utils/tus.ts"],"sourcesContent":["import { Server as TusServer } from '@tus/server'\n\nimport { getTusPostProcessorHandler } from '../endpoints/tusPostProcessorHandler'\nimport { getTusFileExistsHandler } from '../endpoints/tusFileExistsHandler'\n\nimport { generateUniqueFilename } from './file'\n\nimport type { S3Store } from '../tus/stores/s3/s3-store'\nimport type { PayloadRequest } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types'\n\n/**\n * Creates a TUS server instance with S3 storage\n * @param args - The arguments for creating the TUS server\n * @returns A configured TusServer instance\n */\ninterface CreateTusServerArgs {\n s3Store: S3Store\n pluginOptions: MediaCloudPluginOptions\n}\n\nexport function createTusServer(args: CreateTusServerArgs): TusServer {\n const { s3Store, pluginOptions } = args\n\n return new TusServer({\n datastore: s3Store,\n namingFunction: async (_req, metadata) => {\n return metadata?.filename ?? generateUniqueFilename('')\n },\n path: '/api/uploads',\n respectForwardedHeaders: pluginOptions.s3.respectForwardedHeaders ?? true,\n })\n}\n\n/**\n * Creates TUS upload endpoints for file handling\n * @param tusServer - The TUS server instance\n * @returns An array of endpoint configurations\n */\ninterface CreateTusEndpointsArgs {\n tusServer: TusServer\n s3Store: S3Store\n}\n\nexport function createTusEndpoints(args: CreateTusEndpointsArgs) {\n const { tusServer, s3Store } = args\n\n /**\n * Handles TUS requests through the server\n * @param req - The payload request object\n * @returns The server response\n */\n function tusHandler(req: PayloadRequest) {\n return tusServer.handleWeb(req as Request)\n }\n\n return [\n { handler: tusHandler, method: 'options' as const, path: '/uploads' },\n { handler: tusHandler, method: 'post' as const, path: '/uploads' },\n { handler: tusHandler, method: 'get' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'put' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'patch' as const, path: '/uploads/:id' },\n { handler: tusHandler, method: 'delete' as const, path: '/uploads/:id' },\n {\n handler: getTusFileExistsHandler({ s3Store }),\n method: 'get' as const,\n path: '/uploads/:filename/exists',\n },\n {\n handler: getTusPostProcessorHandler({ s3Store }),\n method: 'get' as const,\n path: '/uploads/:filename/process',\n },\n ]\n}\n"],"mappings":";;;;;;AAqBA,SAAgB,gBAAgB,MAAsC;CACpE,MAAM,EAAE,SAAS,kBAAkB;AAEnC,QAAO,IAAIA,OAAU;EACnB,WAAW;EACX,gBAAgB,OAAO,MAAM,aAAa;AACxC,UAAO,UAAU,YAAY,uBAAuB,GAAG;;EAEzD,MAAM;EACN,yBAAyB,cAAc,GAAG,2BAA2B;EACtE,CAAC;;AAaJ,SAAgB,mBAAmB,MAA8B;CAC/D,MAAM,EAAE,WAAW,YAAY;;;;;;CAO/B,SAAS,WAAW,KAAqB;AACvC,SAAO,UAAU,UAAU,IAAe;;AAG5C,QAAO;EACL;GAAE,SAAS;GAAY,QAAQ;GAAoB,MAAM;GAAY;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAiB,MAAM;GAAY;EAClE;GAAE,SAAS;GAAY,QAAQ;GAAgB,MAAM;GAAgB;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAgB,MAAM;GAAgB;EACrE;GAAE,SAAS;GAAY,QAAQ;GAAkB,MAAM;GAAgB;EACvE;GAAE,SAAS;GAAY,QAAQ;GAAmB,MAAM;GAAgB;EACxE;GACE,SAAS,wBAAwB,EAAE,SAAS,CAAC;GAC7C,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS,2BAA2B,EAAE,SAAS,CAAC;GAChD,QAAQ;GACR,MAAM;GACP;EACF"}
|