@kumix/storage 0.1.0 → 0.1.2
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/cloudinary.d.ts +7 -6
- package/dist/cloudinary.d.ts.map +1 -1
- package/dist/cloudinary.js +243 -181
- package/dist/cloudinary.js.map +1 -1
- package/dist/{config-t-NVJICl.js → config-By5ib98s.js} +17 -9
- package/dist/config-By5ib98s.js.map +1 -0
- package/dist/{config-J6AdDh_c.d.ts → config-DGzEH-Zd.d.ts} +13 -10
- package/dist/config-DGzEH-Zd.d.ts.map +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +84 -11
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/s3.d.ts +2 -1
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +162 -95
- package/dist/s3.js.map +1 -1
- package/package.json +7 -12
- package/dist/config-J6AdDh_c.d.ts.map +0 -1
- package/dist/config-t-NVJICl.js.map +0 -1
package/dist/cloudinary.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, H as UploadResult, I as ListResult, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, U as CloudinaryConfig, V as UploadOptions, _ as DeleteFolderOptions, b as ListFoldersOptions, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, j as DownloadResult, k as DeleteResult, m as CopyFolderResult, n as EnvRecord, p as CopyFolderOptions, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-
|
|
1
|
+
import { A as DownloadOptions, B as PresignedUrlResult, C as RenameFolderResult, D as CopyResult, E as CopyOptions, F as ListOptions, H as UploadResult, I as ListResult, L as MoveOptions, M as DuplicateOptions, N as DuplicateResult, O as DeleteOptions, P as ExistsResult, R as MoveResult, S as RenameFolderOptions, T as BatchDeleteResult, U as CloudinaryConfig, V as UploadOptions, _ as DeleteFolderOptions, b as ListFoldersOptions, f as StorageInterface, g as CreateFolderResult, h as CreateFolderOptions, j as DownloadResult, k as DeleteResult, m as CopyFolderResult, n as EnvRecord, p as CopyFolderOptions, v as DeleteFolderResult, w as BatchDeleteOptions, x as ListFoldersResult, y as FolderExistsResult, z as PresignedUrlOptions } from "./config-DGzEH-Zd.js";
|
|
2
2
|
|
|
3
3
|
//#region src/services/cloudinary.d.ts
|
|
4
4
|
/**
|
|
@@ -30,16 +30,16 @@ declare class CloudinaryService implements StorageInterface {
|
|
|
30
30
|
folderExists(path: string): Promise<FolderExistsResult>;
|
|
31
31
|
renameFolder(options: RenameFolderOptions): Promise<RenameFolderResult>;
|
|
32
32
|
copyFolder(options: CopyFolderOptions): Promise<CopyFolderResult>;
|
|
33
|
-
uploadFile(key: string, file: Buffer | Uint8Array | string,
|
|
33
|
+
uploadFile(key: string, file: Buffer | Uint8Array | string, options?: Partial<UploadOptions>): Promise<UploadResult>;
|
|
34
34
|
downloadFile(key: string): Promise<DownloadResult>;
|
|
35
35
|
deleteFile(key: string): Promise<DeleteResult>;
|
|
36
36
|
deleteFiles(keys: string[]): Promise<BatchDeleteResult>;
|
|
37
37
|
listFiles(prefix?: string, maxKeys?: number): Promise<ListResult>;
|
|
38
38
|
fileExists(key: string): Promise<boolean>;
|
|
39
|
-
copyFile(sourceKey: string, destinationKey: string,
|
|
40
|
-
moveFile(sourceKey: string, destinationKey: string,
|
|
41
|
-
duplicateFile(sourceKey: string, destinationKey: string,
|
|
42
|
-
renameFile(sourceKey: string, destinationKey: string,
|
|
39
|
+
copyFile(sourceKey: string, destinationKey: string, options?: Partial<CopyOptions>): Promise<CopyResult>;
|
|
40
|
+
moveFile(sourceKey: string, destinationKey: string, options?: Partial<MoveOptions>): Promise<MoveResult>;
|
|
41
|
+
duplicateFile(sourceKey: string, destinationKey: string, options?: Partial<DuplicateOptions>): Promise<DuplicateResult>;
|
|
42
|
+
renameFile(sourceKey: string, destinationKey: string, options?: Partial<MoveOptions>): Promise<MoveResult>;
|
|
43
43
|
getDownloadUrl(key: string, expiresIn?: number): Promise<PresignedUrlResult>;
|
|
44
44
|
getUploadUrl(key: string, contentType?: string, expiresIn?: number): Promise<PresignedUrlResult>;
|
|
45
45
|
createFolderPath(path: string): Promise<CreateFolderResult>;
|
|
@@ -88,6 +88,7 @@ declare class CloudinaryService implements StorageInterface {
|
|
|
88
88
|
declare function createCloudinary(): CloudinaryService;
|
|
89
89
|
declare function createCloudinary(config: CloudinaryConfig): CloudinaryService;
|
|
90
90
|
declare function createCloudinary(config: CloudinaryConfig, env: EnvRecord): CloudinaryService;
|
|
91
|
+
declare function createCloudinary(config: undefined, env: EnvRecord): CloudinaryService;
|
|
91
92
|
//#endregion
|
|
92
93
|
export { CloudinaryService, createCloudinary };
|
|
93
94
|
//# sourceMappingURL=cloudinary.d.ts.map
|
package/dist/cloudinary.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudinary.d.ts","names":[],"sources":["../src/services/cloudinary.ts","../src/cloudinary.ts"],"mappings":";;;;;;;;cA+Ca,iBAAA,YAA6B,gBAAA;EAAA,QAChC,QAAA;EAAA,QACA,MAAA;;cAGI,MAAA,EAAQ,gBAAA;EAOpB,SAAA,IAAa,gBAAA;EAIb,WAAA;EAIA,YAAA,CAAa,GAAA;EAKP,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;EAIxC,QAAA,CAAS,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,cAAA;EAI5C,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA,CAAQ,YAAA;EAIxC,WAAA,CAAY,OAAA,EAAS,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;EAIlD,IAAA,CAAK,OAAA,GAAU,WAAA,GAAc,OAAA,CAAQ,UAAA;EAIrC,MAAA,CAAO,GAAA,WAAc,OAAA,CAAQ,YAAA;EAI7B,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;EAIpC,IAAA,CAAK,OAAA,EAAS,WAAA,GAAc,OAAA,CAAQ,UAAA;EAIpC,SAAA,CAAU,OAAA,EAAS,gBAAA,GAAmB,OAAA,CAAQ,eAAA;EAI9C,eAAA,CAAgB,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAKvD,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAIpD,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAIpD,WAAA,CAAY,OAAA,GAAU,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;EAInD,YAAA,CAAa,IAAA,WAAe,OAAA,CAAQ,kBAAA;EAIpC,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,OAAA,CAAQ,kBAAA;EAIpD,UAAA,CAAW,OAAA,EAAS,iBAAA,GAAoB,OAAA,CAAQ,gBAAA;EAKhD,UAAA,CACJ,GAAA,UACA,IAAA,EAAM,MAAA,GAAS,UAAA,WACf,
|
|
1
|
+
{"version":3,"file":"cloudinary.d.ts","names":[],"sources":["../src/services/cloudinary.ts","../src/cloudinary.ts"],"mappings":"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"}
|
package/dist/cloudinary.js
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
|
-
import { o as loadCloudinaryConfig } from "./config-
|
|
2
|
-
import { v2 } from "cloudinary";
|
|
1
|
+
import { o as loadCloudinaryConfig } from "./config-By5ib98s.js";
|
|
3
2
|
//#region src/providers/cloudinary.ts
|
|
4
|
-
/**
|
|
5
|
-
* Cloudinary storage provider implementation
|
|
6
|
-
* Provides cloud storage operations using Cloudinary's API for media management
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Cloudinary storage provider
|
|
10
|
-
* Implements StorageInterface for Cloudinary cloud storage
|
|
11
|
-
* @internal
|
|
12
|
-
*/
|
|
13
3
|
var CloudinaryProvider = class {
|
|
14
4
|
config;
|
|
5
|
+
_client = null;
|
|
15
6
|
constructor(config) {
|
|
16
7
|
this.config = config;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Lazily load the Cloudinary SDK and re-apply this instance's config to the
|
|
11
|
+
* module-global v2 client. The SDK is dynamically imported so the optional
|
|
12
|
+
* `cloudinary` peer dep isn't required at module load time — consumers who
|
|
13
|
+
* only use the S3 provider can import the package without installing it.
|
|
14
|
+
*
|
|
15
|
+
* NOTE: the Cloudinary SDK keeps a single global config, so creating multiple
|
|
16
|
+
* `CloudinaryProvider` instances with different credentials in the same
|
|
17
|
+
* process is not supported — the most recently-configured instance wins.
|
|
18
|
+
* `getClient()` re-applies config before each API call to narrow the race
|
|
19
|
+
* window when instances are interleaved.
|
|
20
|
+
*/
|
|
21
|
+
async getClient() {
|
|
22
|
+
if (!this._client) try {
|
|
23
|
+
const mod = await import("cloudinary");
|
|
24
|
+
this._client = mod.v2 ?? mod.default?.v2;
|
|
25
|
+
} catch {
|
|
26
|
+
throw new Error("cloudinary is not available in this runtime. Install the `cloudinary` package to use the Cloudinary storage provider.");
|
|
27
|
+
}
|
|
28
|
+
this._client.config({
|
|
29
|
+
cloud_name: this.config.cloudName,
|
|
30
|
+
api_key: this.config.apiKey,
|
|
31
|
+
api_secret: this.config.apiSecret,
|
|
32
|
+
secure: this.config.secure !== false
|
|
22
33
|
});
|
|
34
|
+
return this._client;
|
|
23
35
|
}
|
|
24
36
|
async upload(options) {
|
|
25
37
|
try {
|
|
38
|
+
const cloudinary = await this.getClient();
|
|
26
39
|
let fileData;
|
|
27
40
|
if (typeof options.file === "string") fileData = options.file;
|
|
28
41
|
else fileData = `data:${options.contentType || "application/octet-stream"};base64,${Buffer.from(options.file).toString("base64")}`;
|
|
@@ -33,19 +46,23 @@ var CloudinaryProvider = class {
|
|
|
33
46
|
let folder = "";
|
|
34
47
|
let publicId = keyParts[keyParts.length - 1];
|
|
35
48
|
if (keyParts.length > 1) folder = keyParts.slice(0, -1).join("/");
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
if (resourceType !== "raw") {
|
|
50
|
+
const dotIndex = publicId.lastIndexOf(".");
|
|
51
|
+
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
52
|
+
}
|
|
38
53
|
const uploadOptions = {
|
|
39
54
|
public_id: publicId,
|
|
40
55
|
resource_type: resourceType,
|
|
41
56
|
overwrite: true,
|
|
42
57
|
invalidate: true,
|
|
43
|
-
...options.metadata
|
|
58
|
+
...options.metadata ? { context: options.metadata } : {}
|
|
44
59
|
};
|
|
45
|
-
|
|
46
|
-
if (this.config.folder)
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
const folderParts = [];
|
|
61
|
+
if (this.config.folder) folderParts.push(this.config.folder);
|
|
62
|
+
if (folder) folderParts.push(folder);
|
|
63
|
+
if (folderParts.length > 0) uploadOptions.folder = folderParts.join("/");
|
|
64
|
+
const result = await cloudinary.uploader.upload(fileData, uploadOptions);
|
|
65
|
+
const publicUrl = this.getPublicUrl(result.public_id, result.format, resourceType);
|
|
49
66
|
return {
|
|
50
67
|
success: true,
|
|
51
68
|
key: result.public_id,
|
|
@@ -63,7 +80,11 @@ var CloudinaryProvider = class {
|
|
|
63
80
|
}
|
|
64
81
|
async download(options) {
|
|
65
82
|
try {
|
|
66
|
-
const
|
|
83
|
+
const dotIndex = options.key.lastIndexOf(".");
|
|
84
|
+
const hasExt = dotIndex > 0 && dotIndex < options.key.length - 1;
|
|
85
|
+
const baseKey = hasExt ? options.key.slice(0, dotIndex) : options.key;
|
|
86
|
+
const extension = hasExt ? options.key.slice(dotIndex + 1) : void 0;
|
|
87
|
+
const publicUrl = this.getPublicUrl(baseKey, extension);
|
|
67
88
|
const response = await fetch(publicUrl);
|
|
68
89
|
if (!response.ok) return {
|
|
69
90
|
success: false,
|
|
@@ -88,25 +109,24 @@ var CloudinaryProvider = class {
|
|
|
88
109
|
}
|
|
89
110
|
async delete(options) {
|
|
90
111
|
try {
|
|
91
|
-
const
|
|
112
|
+
const cloudinary = await this.getClient();
|
|
92
113
|
let publicId = options.key;
|
|
93
|
-
if (keyParts.length > 1) publicId = `${keyParts.slice(0, -1).join("/")}/${keyParts[keyParts.length - 1]}`;
|
|
94
114
|
const dotIndex = publicId.lastIndexOf(".");
|
|
95
115
|
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
96
116
|
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
97
117
|
let resourceType = "raw";
|
|
98
118
|
try {
|
|
99
|
-
await
|
|
119
|
+
await cloudinary.api.resource(publicId, { resource_type: "image" });
|
|
100
120
|
resourceType = "image";
|
|
101
121
|
} catch {
|
|
102
122
|
try {
|
|
103
|
-
await
|
|
123
|
+
await cloudinary.api.resource(publicId, { resource_type: "video" });
|
|
104
124
|
resourceType = "video";
|
|
105
125
|
} catch {
|
|
106
126
|
resourceType = "raw";
|
|
107
127
|
}
|
|
108
128
|
}
|
|
109
|
-
await
|
|
129
|
+
await cloudinary.uploader.destroy(publicId, {
|
|
110
130
|
resource_type: resourceType,
|
|
111
131
|
invalidate: true
|
|
112
132
|
});
|
|
@@ -120,12 +140,30 @@ var CloudinaryProvider = class {
|
|
|
120
140
|
}
|
|
121
141
|
async batchDelete(options) {
|
|
122
142
|
try {
|
|
143
|
+
const cloudinary = await this.getClient();
|
|
144
|
+
const publicIds = options.keys.map((key) => {
|
|
145
|
+
let publicId = key;
|
|
146
|
+
const dotIndex = publicId.lastIndexOf(".");
|
|
147
|
+
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
148
|
+
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
149
|
+
return publicId;
|
|
150
|
+
});
|
|
151
|
+
const result = await cloudinary.api.delete_resources(publicIds);
|
|
152
|
+
const deleted = [];
|
|
153
|
+
const errors = [];
|
|
154
|
+
for (const [publicId, info] of Object.entries(result)) {
|
|
155
|
+
const originalKey = options.keys[publicIds.indexOf(publicId)] || publicId;
|
|
156
|
+
const status = info?.status;
|
|
157
|
+
if (status === "deleted" || status === "not_found") deleted.push(originalKey);
|
|
158
|
+
else errors.push({
|
|
159
|
+
key: originalKey,
|
|
160
|
+
error: `Delete status: ${String(status)}`
|
|
161
|
+
});
|
|
162
|
+
}
|
|
123
163
|
return {
|
|
124
|
-
success:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
error: "Cloudinary batch delete not implemented yet"
|
|
128
|
-
}))
|
|
164
|
+
success: errors.length === 0,
|
|
165
|
+
deleted,
|
|
166
|
+
errors
|
|
129
167
|
};
|
|
130
168
|
} catch (error) {
|
|
131
169
|
return {
|
|
@@ -139,55 +177,61 @@ var CloudinaryProvider = class {
|
|
|
139
177
|
}
|
|
140
178
|
async list(options = {}) {
|
|
141
179
|
try {
|
|
180
|
+
const cloudinary = await this.getClient();
|
|
142
181
|
const prefix = options.prefix || "";
|
|
143
182
|
const maxResults = options.maxKeys || 50;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
const TYPES = [
|
|
184
|
+
"image",
|
|
185
|
+
"video",
|
|
186
|
+
"raw"
|
|
187
|
+
];
|
|
188
|
+
let type = "image";
|
|
189
|
+
let cursor;
|
|
190
|
+
if (options.continuationToken) {
|
|
191
|
+
const sep = options.continuationToken.indexOf(":");
|
|
192
|
+
if (sep > 0) {
|
|
193
|
+
const t = options.continuationToken.slice(0, sep);
|
|
194
|
+
const c = options.continuationToken.slice(sep + 1);
|
|
195
|
+
if (TYPES.includes(t)) {
|
|
196
|
+
type = t;
|
|
197
|
+
cursor = c || void 0;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const fetchPage = (rt, c) => cloudinary.api.resources({
|
|
202
|
+
type: "upload",
|
|
203
|
+
resource_type: rt,
|
|
204
|
+
prefix,
|
|
205
|
+
max_results: maxResults,
|
|
206
|
+
...c ? { next_cursor: c } : {}
|
|
207
|
+
}).catch(() => ({
|
|
208
|
+
resources: [],
|
|
209
|
+
next_cursor: void 0
|
|
210
|
+
}));
|
|
211
|
+
let page = await fetchPage(type, cursor);
|
|
212
|
+
let resources = page.resources || [];
|
|
213
|
+
let nextCursor = page.next_cursor;
|
|
214
|
+
let typeIdx = TYPES.indexOf(type);
|
|
215
|
+
while (!nextCursor && resources.length === 0 && typeIdx < TYPES.length - 1) {
|
|
216
|
+
typeIdx++;
|
|
217
|
+
type = TYPES[typeIdx];
|
|
218
|
+
page = await fetchPage(type);
|
|
219
|
+
resources = page.resources || [];
|
|
220
|
+
nextCursor = page.next_cursor;
|
|
221
|
+
}
|
|
222
|
+
const files = resources.map((resource) => ({
|
|
223
|
+
key: String(resource.public_id || ""),
|
|
224
|
+
size: Number(resource.bytes) || 0,
|
|
225
|
+
lastModified: new Date(String(resource.created_at || /* @__PURE__ */ new Date())),
|
|
226
|
+
etag: String(resource.etag || ""),
|
|
227
|
+
contentType: this.formatToMime(String(resource.resource_type || "raw"), resource.format ? String(resource.format) : void 0)
|
|
228
|
+
}));
|
|
229
|
+
const nextContinuationToken = nextCursor ? `${type}:${nextCursor}` : void 0;
|
|
176
230
|
return {
|
|
177
231
|
success: true,
|
|
178
|
-
files
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
...rawFiles.resources || []
|
|
182
|
-
].map((resource) => ({
|
|
183
|
-
key: String(resource.public_id || ""),
|
|
184
|
-
size: Number(resource.bytes) || 0,
|
|
185
|
-
lastModified: new Date(String(resource.created_at || /* @__PURE__ */ new Date())),
|
|
186
|
-
etag: String(resource.etag || ""),
|
|
187
|
-
contentType: resource.format ? `${resource.resource_type}/${resource.format}` : "application/octet-stream"
|
|
188
|
-
})),
|
|
189
|
-
isTruncated: !!(images.next_cursor || videos.next_cursor || rawFiles.next_cursor),
|
|
190
|
-
nextContinuationToken: images.next_cursor || videos.next_cursor || rawFiles.next_cursor
|
|
232
|
+
files,
|
|
233
|
+
isTruncated: !!nextContinuationToken,
|
|
234
|
+
nextContinuationToken
|
|
191
235
|
};
|
|
192
236
|
} catch (error) {
|
|
193
237
|
return {
|
|
@@ -196,12 +240,22 @@ var CloudinaryProvider = class {
|
|
|
196
240
|
};
|
|
197
241
|
}
|
|
198
242
|
}
|
|
199
|
-
async exists(
|
|
243
|
+
async exists(key) {
|
|
200
244
|
try {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
245
|
+
const cloudinary = await this.getClient();
|
|
246
|
+
let publicId = key;
|
|
247
|
+
const dotIndex = publicId.lastIndexOf(".");
|
|
248
|
+
if (dotIndex > 0) publicId = publicId.slice(0, dotIndex);
|
|
249
|
+
if (this.config.folder) publicId = `${this.config.folder}/${publicId}`;
|
|
250
|
+
for (const resourceType of [
|
|
251
|
+
"image",
|
|
252
|
+
"video",
|
|
253
|
+
"raw"
|
|
254
|
+
]) try {
|
|
255
|
+
await cloudinary.api.resource(publicId, { resource_type: resourceType });
|
|
256
|
+
return { exists: true };
|
|
257
|
+
} catch {}
|
|
258
|
+
return { exists: false };
|
|
205
259
|
} catch (error) {
|
|
206
260
|
return {
|
|
207
261
|
exists: false,
|
|
@@ -210,68 +264,98 @@ var CloudinaryProvider = class {
|
|
|
210
264
|
}
|
|
211
265
|
}
|
|
212
266
|
async copy(_options) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
};
|
|
218
|
-
} catch (error) {
|
|
219
|
-
return {
|
|
220
|
-
success: false,
|
|
221
|
-
error: error instanceof Error ? error.message : "Copy failed"
|
|
222
|
-
};
|
|
223
|
-
}
|
|
267
|
+
return {
|
|
268
|
+
success: false,
|
|
269
|
+
error: "Cloudinary copy not implemented yet"
|
|
270
|
+
};
|
|
224
271
|
}
|
|
225
272
|
async move(_options) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
};
|
|
231
|
-
} catch (error) {
|
|
232
|
-
return {
|
|
233
|
-
success: false,
|
|
234
|
-
error: error instanceof Error ? error.message : "Move failed"
|
|
235
|
-
};
|
|
236
|
-
}
|
|
273
|
+
return {
|
|
274
|
+
success: false,
|
|
275
|
+
error: "Cloudinary move not implemented yet"
|
|
276
|
+
};
|
|
237
277
|
}
|
|
238
278
|
async duplicate(options) {
|
|
239
279
|
return this.copy(options);
|
|
240
280
|
}
|
|
241
281
|
async getPresignedUrl(_options) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: "Cloudinary presigned URL not implemented yet"
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
getPublicUrl(key, extension, resourceType) {
|
|
288
|
+
const baseUrl = `${this.config.secure !== false ? "https" : "http"}://res.cloudinary.com/${this.config.cloudName}`;
|
|
289
|
+
let publicId = key;
|
|
290
|
+
if (this.config.folder && !publicId.startsWith(`${this.config.folder}/`)) publicId = `${this.config.folder}/${publicId}`;
|
|
291
|
+
return `${baseUrl}/${resourceType || this.inferResourceType(extension || key)}/upload/${publicId}${extension ? `.${extension}` : ""}`;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Build a valid MIME type from a Cloudinary `resource_type` + `format`.
|
|
295
|
+
* Naive `${resource_type}/${format}` produces invalid types like `raw/pdf`
|
|
296
|
+
* or `image/jpg`, so map known formats to their real MIME types and fall back
|
|
297
|
+
* to `application/octet-stream` for raw/unknown assets.
|
|
298
|
+
*/
|
|
299
|
+
formatToMime(resourceType, format) {
|
|
300
|
+
if (!format) return "application/octet-stream";
|
|
301
|
+
const fmt = format.toLowerCase();
|
|
302
|
+
const overrides = {
|
|
303
|
+
jpg: "image/jpeg",
|
|
304
|
+
jpeg: "image/jpeg",
|
|
305
|
+
svg: "image/svg+xml",
|
|
306
|
+
pdf: "application/pdf",
|
|
307
|
+
mov: "video/quicktime",
|
|
308
|
+
mp3: "audio/mpeg"
|
|
309
|
+
};
|
|
310
|
+
if (overrides[fmt]) return overrides[fmt];
|
|
311
|
+
if (resourceType === "image" || resourceType === "video") return `${resourceType}/${fmt}`;
|
|
312
|
+
return "application/octet-stream";
|
|
313
|
+
}
|
|
314
|
+
/** Infer Cloudinary resource type from a filename or extension */
|
|
315
|
+
inferResourceType(nameOrExt) {
|
|
316
|
+
const ext = nameOrExt.includes(".") ? nameOrExt.split(".").pop()?.toLowerCase() || "" : nameOrExt.toLowerCase();
|
|
317
|
+
const imageExts = [
|
|
318
|
+
"jpg",
|
|
319
|
+
"jpeg",
|
|
320
|
+
"png",
|
|
321
|
+
"gif",
|
|
322
|
+
"webp",
|
|
323
|
+
"svg",
|
|
324
|
+
"bmp",
|
|
325
|
+
"ico",
|
|
326
|
+
"avif",
|
|
327
|
+
"tiff"
|
|
328
|
+
];
|
|
329
|
+
const videoExts = [
|
|
330
|
+
"mp4",
|
|
331
|
+
"webm",
|
|
332
|
+
"mov",
|
|
333
|
+
"avi",
|
|
334
|
+
"mkv",
|
|
335
|
+
"flv",
|
|
336
|
+
"wmv",
|
|
337
|
+
"m4v",
|
|
338
|
+
"ogv"
|
|
339
|
+
];
|
|
340
|
+
if (imageExts.includes(ext)) return "image";
|
|
341
|
+
if (videoExts.includes(ext)) return "video";
|
|
342
|
+
return "raw";
|
|
256
343
|
}
|
|
257
344
|
async createFolder(options) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
};
|
|
263
|
-
} catch (error) {
|
|
264
|
-
return {
|
|
265
|
-
success: false,
|
|
266
|
-
error: error instanceof Error ? error.message : "Create folder failed"
|
|
267
|
-
};
|
|
268
|
-
}
|
|
345
|
+
return {
|
|
346
|
+
success: true,
|
|
347
|
+
path: options.path
|
|
348
|
+
};
|
|
269
349
|
}
|
|
270
350
|
async deleteFolder(options) {
|
|
271
351
|
try {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
352
|
+
const cloudinary = await this.getClient();
|
|
353
|
+
const normalizedPath = options.path.replace(/\/$/, "");
|
|
354
|
+
const scopedPath = this.config.folder ? `${this.config.folder}/${normalizedPath}` : normalizedPath;
|
|
355
|
+
const folderPath = `${scopedPath}/`;
|
|
356
|
+
const folderApiPath = scopedPath;
|
|
357
|
+
if (options.recursive) await cloudinary.api.delete_resources_by_prefix(folderPath);
|
|
358
|
+
await cloudinary.api.delete_folder(folderApiPath);
|
|
275
359
|
return { success: true };
|
|
276
360
|
} catch (error) {
|
|
277
361
|
return {
|
|
@@ -284,9 +368,9 @@ var CloudinaryProvider = class {
|
|
|
284
368
|
try {
|
|
285
369
|
return {
|
|
286
370
|
success: true,
|
|
287
|
-
folders: ((await
|
|
288
|
-
name: folder.name,
|
|
289
|
-
path: folder.path
|
|
371
|
+
folders: ((await (await this.getClient()).api.root_folders()).folders || []).map((folder) => ({
|
|
372
|
+
name: String(folder.name ?? ""),
|
|
373
|
+
path: String(folder.path ?? "")
|
|
290
374
|
})),
|
|
291
375
|
isTruncated: false,
|
|
292
376
|
nextContinuationToken: void 0
|
|
@@ -299,43 +383,22 @@ var CloudinaryProvider = class {
|
|
|
299
383
|
}
|
|
300
384
|
}
|
|
301
385
|
async folderExists(_path) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
};
|
|
307
|
-
} catch (error) {
|
|
308
|
-
return {
|
|
309
|
-
exists: false,
|
|
310
|
-
error: error instanceof Error ? error.message : "Check folder existence failed"
|
|
311
|
-
};
|
|
312
|
-
}
|
|
386
|
+
return {
|
|
387
|
+
exists: false,
|
|
388
|
+
error: "Cloudinary folder existence is not implemented yet — this result does NOT mean the folder is absent."
|
|
389
|
+
};
|
|
313
390
|
}
|
|
314
391
|
async renameFolder(_options) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
};
|
|
320
|
-
} catch (error) {
|
|
321
|
-
return {
|
|
322
|
-
success: false,
|
|
323
|
-
error: error instanceof Error ? error.message : "Rename folder failed"
|
|
324
|
-
};
|
|
325
|
-
}
|
|
392
|
+
return {
|
|
393
|
+
success: false,
|
|
394
|
+
error: "Cloudinary rename folder not implemented yet"
|
|
395
|
+
};
|
|
326
396
|
}
|
|
327
397
|
async copyFolder(_options) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
};
|
|
333
|
-
} catch (error) {
|
|
334
|
-
return {
|
|
335
|
-
success: false,
|
|
336
|
-
error: error instanceof Error ? error.message : "Copy folder failed"
|
|
337
|
-
};
|
|
338
|
-
}
|
|
398
|
+
return {
|
|
399
|
+
success: false,
|
|
400
|
+
error: "Cloudinary copy folder not implemented yet"
|
|
401
|
+
};
|
|
339
402
|
}
|
|
340
403
|
};
|
|
341
404
|
//#endregion
|
|
@@ -413,12 +476,11 @@ var CloudinaryService = class {
|
|
|
413
476
|
async copyFolder(options) {
|
|
414
477
|
return this.provider.copyFolder(options);
|
|
415
478
|
}
|
|
416
|
-
async uploadFile(key, file,
|
|
479
|
+
async uploadFile(key, file, options) {
|
|
417
480
|
return this.upload({
|
|
418
481
|
key,
|
|
419
482
|
file,
|
|
420
|
-
|
|
421
|
-
metadata
|
|
483
|
+
...options
|
|
422
484
|
});
|
|
423
485
|
}
|
|
424
486
|
async downloadFile(key) {
|
|
@@ -439,29 +501,29 @@ var CloudinaryService = class {
|
|
|
439
501
|
async fileExists(key) {
|
|
440
502
|
return (await this.exists(key)).exists;
|
|
441
503
|
}
|
|
442
|
-
async copyFile(sourceKey, destinationKey,
|
|
504
|
+
async copyFile(sourceKey, destinationKey, options) {
|
|
443
505
|
return this.copy({
|
|
444
506
|
sourceKey,
|
|
445
507
|
destinationKey,
|
|
446
|
-
|
|
508
|
+
...options
|
|
447
509
|
});
|
|
448
510
|
}
|
|
449
|
-
async moveFile(sourceKey, destinationKey,
|
|
511
|
+
async moveFile(sourceKey, destinationKey, options) {
|
|
450
512
|
return this.move({
|
|
451
513
|
sourceKey,
|
|
452
514
|
destinationKey,
|
|
453
|
-
|
|
515
|
+
...options
|
|
454
516
|
});
|
|
455
517
|
}
|
|
456
|
-
async duplicateFile(sourceKey, destinationKey,
|
|
518
|
+
async duplicateFile(sourceKey, destinationKey, options) {
|
|
457
519
|
return this.duplicate({
|
|
458
520
|
sourceKey,
|
|
459
521
|
destinationKey,
|
|
460
|
-
|
|
522
|
+
...options
|
|
461
523
|
});
|
|
462
524
|
}
|
|
463
|
-
async renameFile(sourceKey, destinationKey,
|
|
464
|
-
return this.moveFile(sourceKey, destinationKey,
|
|
525
|
+
async renameFile(sourceKey, destinationKey, options) {
|
|
526
|
+
return this.moveFile(sourceKey, destinationKey, options);
|
|
465
527
|
}
|
|
466
528
|
async getDownloadUrl(key, expiresIn) {
|
|
467
529
|
return this.getPresignedUrl({
|