nestjs-r2-storage 1.4.2 → 1.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 +11 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.js.map +1 -1
- package/dist/r2-storage/cloudflare.service.d.ts +3 -20
- package/dist/r2-storage/cloudflare.service.js +25 -27
- package/dist/r2-storage/cloudflare.service.js.map +1 -1
- package/dist/r2-storage/interfaces/cloudflare-service.interface.d.ts +16 -0
- package/dist/r2-storage/interfaces/cloudflare-service.interface.js +3 -0
- package/dist/r2-storage/interfaces/cloudflare-service.interface.js.map +1 -0
- package/dist/r2-storage/interfaces/photo-manager.interface.d.ts +37 -0
- package/dist/r2-storage/interfaces/photo-manager.interface.js +3 -0
- package/dist/r2-storage/interfaces/photo-manager.interface.js.map +1 -0
- package/dist/r2-storage/interfaces/storage-options.interface.d.ts +23 -23
- package/dist/r2-storage/photo-manager.service.d.ts +23 -54
- package/dist/r2-storage/photo-manager.service.js +382 -333
- package/dist/r2-storage/photo-manager.service.js.map +1 -1
- package/dist/r2-storage/r2-storage.module.d.ts +2 -7
- package/dist/r2-storage/r2-storage.module.js +5 -43
- package/dist/r2-storage/r2-storage.module.js.map +1 -1
- package/dist/r2-storage/utils/validate-options.utils.d.ts +2 -0
- package/dist/r2-storage/utils/validate-options.utils.js +21 -0
- package/dist/r2-storage/utils/validate-options.utils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -406,6 +406,17 @@ R2StorageModule.forRootAsync({
|
|
|
406
406
|
|
|
407
407
|
## Changelog
|
|
408
408
|
|
|
409
|
+
### v1.5.0 (2026-04-25)
|
|
410
|
+
|
|
411
|
+
- **Refactored photo update logic** - Diff-based (state reconciliation) instead of request-driven
|
|
412
|
+
- **No unnecessary uploads** - Files are only uploaded when the value actually changes
|
|
413
|
+
- **No accidental deletions** - Files are only deleted when removed from the payload
|
|
414
|
+
- **Index-based array handling** - Array fields use path-based comparison (`gallery[0].photo`) instead of filename matching
|
|
415
|
+
- **Reusable `extractExistingFileMap()`** - Public method for extracting `fieldPath → fileKey` maps
|
|
416
|
+
- **Optimized traversal** - Single-pass map extraction, O(1) lookups via Map
|
|
417
|
+
- **Size is optional** - Size field does not affect diff logic, only used for storage tracking
|
|
418
|
+
- **Deterministic behavior** - Backend-driven, no assumptions about frontend behavior
|
|
419
|
+
|
|
409
420
|
### v1.2.6 (2025-04-20)
|
|
410
421
|
|
|
411
422
|
- Refactored getNestedValue: access key first, then handle array segments
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { StorageOptions, FileFieldConfig, PhotoFieldConfig, StorageModuleOptions, AccessMode, } from
|
|
2
|
-
export { CloudflareService,
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
1
|
+
export { StorageOptions, FileFieldConfig, PhotoFieldConfig, StorageModuleOptions, AccessMode, } from "./r2-storage/interfaces/storage-options.interface";
|
|
2
|
+
export { CloudflareService, AccessModeError, } from "./r2-storage/cloudflare.service";
|
|
3
|
+
export { UploadUrlResult, DownloadUrlResult, FileInfo, } from "./r2-storage/interfaces/cloudflare-service.interface";
|
|
4
|
+
export { PhotoManagerService } from "./r2-storage/photo-manager.service";
|
|
5
|
+
export { PhotoField, AppendUrlsOptions, PhotoUploadRequest, PhotoUploadResponse, CreatePhotosResult, UpdatePhotosResult, DeletePhotosResult, } from "./r2-storage/interfaces/photo-manager.interface";
|
|
6
|
+
export { R2StorageModule } from "./r2-storage/r2-storage.module";
|
|
7
|
+
export { PhotoFields, UploadUrls, StorageInfo, } from "./r2-storage/decorators/photo-fields.decorator";
|
|
8
|
+
export { parseFieldPath, getNestedValue, setNestedValue, collectNestedValues, isArrayPath, getArrayBasePath, getArrayElementPath, getAllArrayItemPaths, ParsedPath, PathSegment, } from "./r2-storage/utils/nested-value.util";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAQA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAQA,sEAGyC;AAFvC,uHAAA,iBAAiB,OAAA;AACjB,qHAAA,eAAe,OAAA;AASjB,4EAAyE;AAAhE,4HAAA,mBAAmB,OAAA;AAY5B,oEAAiE;AAAxD,oHAAA,eAAe,OAAA;AAExB,yFAIwD;AAHtD,qHAAA,WAAW,OAAA;AACX,oHAAA,UAAU,OAAA;AACV,qHAAA,WAAW,OAAA;AAGb,0EAW8C;AAV5C,mHAAA,cAAc,OAAA;AACd,mHAAA,cAAc,OAAA;AACd,mHAAA,cAAc,OAAA;AACd,wHAAA,mBAAmB,OAAA;AACnB,gHAAA,WAAW,OAAA;AACX,qHAAA,gBAAgB,OAAA;AAChB,wHAAA,mBAAmB,OAAA;AACnB,yHAAA,oBAAoB,OAAA"}
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import { OnModuleInit, OnModuleDestroy, BadRequestException } from
|
|
2
|
-
import { StorageOptions } from
|
|
1
|
+
import { OnModuleInit, OnModuleDestroy, BadRequestException } from "@nestjs/common";
|
|
2
|
+
import { StorageOptions } from "./interfaces/storage-options.interface";
|
|
3
|
+
import { DownloadUrlResult, FileInfo, UploadUrlResult } from "./interfaces/cloudflare-service.interface";
|
|
3
4
|
export declare class AccessModeError extends BadRequestException {
|
|
4
5
|
constructor(message: string);
|
|
5
6
|
}
|
|
6
|
-
export interface UploadUrlResult {
|
|
7
|
-
uploadUrl: string;
|
|
8
|
-
fileKey: string;
|
|
9
|
-
publicUrl: string | null;
|
|
10
|
-
mimeType: string;
|
|
11
|
-
sizeField?: number;
|
|
12
|
-
}
|
|
13
|
-
export interface DownloadUrlResult {
|
|
14
|
-
downloadUrl: string;
|
|
15
|
-
publicUrl: string | null;
|
|
16
|
-
}
|
|
17
|
-
export interface FileInfo {
|
|
18
|
-
size: number;
|
|
19
|
-
lastModified?: Date;
|
|
20
|
-
contentType?: string;
|
|
21
|
-
}
|
|
22
7
|
export declare class CloudflareService implements OnModuleInit, OnModuleDestroy {
|
|
23
8
|
private readonly storageOptions;
|
|
24
9
|
private s3Client;
|
|
@@ -28,11 +13,9 @@ export declare class CloudflareService implements OnModuleInit, OnModuleDestroy
|
|
|
28
13
|
constructor(storageOptions: StorageOptions);
|
|
29
14
|
private get accessMode();
|
|
30
15
|
private isPublicAccessAllowed;
|
|
31
|
-
private ensurePublicAccessAllowed;
|
|
32
16
|
onModuleInit(): void;
|
|
33
17
|
onModuleDestroy(): void;
|
|
34
18
|
private initializeClient;
|
|
35
|
-
setOptions(options: StorageOptions): void;
|
|
36
19
|
getOptions(): StorageOptions;
|
|
37
20
|
private sanitizeFilename;
|
|
38
21
|
private detectMimeType;
|
|
@@ -52,10 +52,11 @@ const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
|
52
52
|
const path = __importStar(require("path"));
|
|
53
53
|
const mime = __importStar(require("mime-types"));
|
|
54
54
|
const constants_1 = require("./constants");
|
|
55
|
+
const validate_options_utils_1 = require("./utils/validate-options.utils");
|
|
55
56
|
class AccessModeError extends common_1.BadRequestException {
|
|
56
57
|
constructor(message) {
|
|
57
58
|
super(message);
|
|
58
|
-
this.name =
|
|
59
|
+
this.name = "AccessModeError";
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
exports.AccessModeError = AccessModeError;
|
|
@@ -63,21 +64,17 @@ let CloudflareService = class CloudflareService {
|
|
|
63
64
|
constructor(storageOptions) {
|
|
64
65
|
this.storageOptions = storageOptions;
|
|
65
66
|
this.defaultExpiry = 3600;
|
|
66
|
-
this.defaultAccessMode =
|
|
67
|
-
this.options = storageOptions;
|
|
67
|
+
this.defaultAccessMode = "hybrid";
|
|
68
|
+
this.options = this.storageOptions;
|
|
68
69
|
}
|
|
69
70
|
get accessMode() {
|
|
70
71
|
return this.options.accessMode || this.defaultAccessMode;
|
|
71
72
|
}
|
|
72
73
|
isPublicAccessAllowed() {
|
|
73
|
-
return this.accessMode ===
|
|
74
|
-
}
|
|
75
|
-
ensurePublicAccessAllowed() {
|
|
76
|
-
if (this.accessMode === 'private') {
|
|
77
|
-
throw new AccessModeError('Public URL generation is not allowed in "private" access mode. Use presigned URLs for file access.');
|
|
78
|
-
}
|
|
74
|
+
return this.accessMode === "public-read" || this.accessMode === "hybrid";
|
|
79
75
|
}
|
|
80
76
|
onModuleInit() {
|
|
77
|
+
(0, validate_options_utils_1.validateOptions)(this.options);
|
|
81
78
|
this.initializeClient();
|
|
82
79
|
}
|
|
83
80
|
onModuleDestroy() {
|
|
@@ -88,37 +85,35 @@ let CloudflareService = class CloudflareService {
|
|
|
88
85
|
initializeClient() {
|
|
89
86
|
this.s3Client = new client_s3_1.S3Client({
|
|
90
87
|
endpoint: this.options.endpoint,
|
|
91
|
-
region: this.options.region ||
|
|
88
|
+
region: this.options.region || "auto",
|
|
92
89
|
credentials: {
|
|
93
90
|
accessKeyId: this.options.accessKeyId,
|
|
94
91
|
secretAccessKey: this.options.secretAccessKey,
|
|
95
92
|
},
|
|
96
93
|
forcePathStyle: true,
|
|
97
|
-
requestChecksumCalculation:
|
|
94
|
+
requestChecksumCalculation: "WHEN_REQUIRED",
|
|
98
95
|
});
|
|
99
96
|
}
|
|
100
|
-
setOptions(options) {
|
|
101
|
-
this.options = options;
|
|
102
|
-
this.initializeClient();
|
|
103
|
-
}
|
|
104
97
|
getOptions() {
|
|
105
98
|
return this.options;
|
|
106
99
|
}
|
|
107
|
-
sanitizeFilename(filename) {
|
|
108
|
-
const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g,
|
|
109
|
-
|
|
100
|
+
sanitizeFilename(filename, unique = true) {
|
|
101
|
+
const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
102
|
+
if (!unique)
|
|
103
|
+
return sanitized;
|
|
104
|
+
const uniqueSuffix = `${Date.now()}`;
|
|
110
105
|
const ext = path.extname(sanitized);
|
|
111
106
|
const basename = path.basename(sanitized, ext);
|
|
112
|
-
return `${basename}_${
|
|
107
|
+
return `${basename}_${uniqueSuffix}${ext}`;
|
|
113
108
|
}
|
|
114
|
-
detectMimeType(filename, fallbackContentType =
|
|
109
|
+
detectMimeType(filename, fallbackContentType = "application/octet-stream") {
|
|
115
110
|
const mimeType = mime.lookup(filename);
|
|
116
111
|
return mimeType || fallbackContentType;
|
|
117
112
|
}
|
|
118
113
|
async getUploadUrl(fileKey, fileSize, customFilename, contentType) {
|
|
119
114
|
const filename = customFilename || path.basename(fileKey);
|
|
120
115
|
const sanitizedFilename = this.sanitizeFilename(filename);
|
|
121
|
-
const finalFileKey = fileKey.includes(
|
|
116
|
+
const finalFileKey = fileKey.includes("/")
|
|
122
117
|
? `${path.dirname(fileKey)}/${sanitizedFilename}`
|
|
123
118
|
: sanitizedFilename;
|
|
124
119
|
const mimeType = contentType || this.detectMimeType(sanitizedFilename);
|
|
@@ -130,7 +125,7 @@ let CloudflareService = class CloudflareService {
|
|
|
130
125
|
const expiry = this.options.signedUrlExpiry || this.defaultExpiry;
|
|
131
126
|
const uploadUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, {
|
|
132
127
|
expiresIn: expiry,
|
|
133
|
-
signableHeaders: new Set([
|
|
128
|
+
signableHeaders: new Set(["host", "content-type"]),
|
|
134
129
|
});
|
|
135
130
|
let publicUrl = null;
|
|
136
131
|
if (this.options.publicUrlBase && this.isPublicAccessAllowed()) {
|
|
@@ -150,7 +145,9 @@ let CloudflareService = class CloudflareService {
|
|
|
150
145
|
Key: fileKey,
|
|
151
146
|
});
|
|
152
147
|
const expiry = this.options.signedUrlExpiry || this.defaultExpiry;
|
|
153
|
-
const downloadUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, {
|
|
148
|
+
const downloadUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, {
|
|
149
|
+
expiresIn: expiry,
|
|
150
|
+
});
|
|
154
151
|
let publicUrl = null;
|
|
155
152
|
if (this.options.publicUrlBase && this.isPublicAccessAllowed()) {
|
|
156
153
|
publicUrl = `${this.options.publicUrlBase}/${fileKey}`;
|
|
@@ -198,7 +195,10 @@ let CloudflareService = class CloudflareService {
|
|
|
198
195
|
};
|
|
199
196
|
}
|
|
200
197
|
catch (error) {
|
|
201
|
-
|
|
198
|
+
if (error?.$metadata?.httpStatusCode === 404) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async fileExists(fileKey) {
|
|
@@ -206,9 +206,7 @@ let CloudflareService = class CloudflareService {
|
|
|
206
206
|
return fileInfo !== null;
|
|
207
207
|
}
|
|
208
208
|
generateFileKey(prefix, filename) {
|
|
209
|
-
|
|
210
|
-
const sanitizedFilename = this.sanitizeFilename(filename);
|
|
211
|
-
return `${prefix}/${timestamp}_${sanitizedFilename}`;
|
|
209
|
+
return `${prefix}/${this.sanitizeFilename(filename)}`;
|
|
212
210
|
}
|
|
213
211
|
};
|
|
214
212
|
exports.CloudflareService = CloudflareService;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare.service.js","sourceRoot":"","sources":["../../src/r2-storage/cloudflare.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"cloudflare.service.js","sourceRoot":"","sources":["../../src/r2-storage/cloudflare.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface UploadUrlResult {
|
|
2
|
+
uploadUrl: string;
|
|
3
|
+
fileKey: string;
|
|
4
|
+
publicUrl: string | null;
|
|
5
|
+
mimeType: string;
|
|
6
|
+
sizeField?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface DownloadUrlResult {
|
|
9
|
+
downloadUrl: string;
|
|
10
|
+
publicUrl: string | null;
|
|
11
|
+
}
|
|
12
|
+
export interface FileInfo {
|
|
13
|
+
size: number;
|
|
14
|
+
lastModified?: Date;
|
|
15
|
+
contentType?: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-service.interface.js","sourceRoot":"","sources":["../../../src/r2-storage/interfaces/cloudflare-service.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface PhotoField {
|
|
2
|
+
field: string;
|
|
3
|
+
urlField?: string;
|
|
4
|
+
sizeField?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PhotoUploadRequest {
|
|
7
|
+
field: string;
|
|
8
|
+
filename: string;
|
|
9
|
+
size: number;
|
|
10
|
+
prefix?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PhotoUploadResponse {
|
|
13
|
+
field: string;
|
|
14
|
+
fileKey: string;
|
|
15
|
+
uploadUrl: string;
|
|
16
|
+
publicUrl: string | null;
|
|
17
|
+
filename?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CreatePhotosResult<T extends Record<string, any>> {
|
|
20
|
+
updatedPayload: T;
|
|
21
|
+
uploadUrls: PhotoUploadResponse[];
|
|
22
|
+
totalStorageUsed: number;
|
|
23
|
+
}
|
|
24
|
+
export interface UpdatePhotosResult<T extends Record<string, any>> {
|
|
25
|
+
updatedPayload: T;
|
|
26
|
+
uploadUrls: PhotoUploadResponse[];
|
|
27
|
+
storageIncrease: number;
|
|
28
|
+
storageDecrease: number;
|
|
29
|
+
deletedFiles: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface DeletePhotosResult {
|
|
32
|
+
deletedFiles: string[];
|
|
33
|
+
totalStorageFreed: number;
|
|
34
|
+
}
|
|
35
|
+
export interface AppendUrlsOptions {
|
|
36
|
+
urlField?: (field: string) => string;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-manager.interface.js","sourceRoot":"","sources":["../../../src/r2-storage/interfaces/photo-manager.interface.ts"],"names":[],"mappings":""}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
export type AccessMode =
|
|
2
|
-
export interface StorageOptions {
|
|
3
|
-
endpoint: string;
|
|
4
|
-
accessKeyId: string;
|
|
5
|
-
secretAccessKey: string;
|
|
6
|
-
bucketName: string;
|
|
7
|
-
region?: string;
|
|
8
|
-
publicUrlBase?: string;
|
|
9
|
-
signedUrlExpiry?: number;
|
|
10
|
-
accessMode?: AccessMode;
|
|
11
|
-
}
|
|
12
|
-
export interface FileFieldConfig {
|
|
13
|
-
field: string;
|
|
14
|
-
sizeField?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface PhotoFieldConfig {
|
|
17
|
-
field: string;
|
|
18
|
-
urlField?: string;
|
|
19
|
-
sizeField?: string;
|
|
20
|
-
}
|
|
21
|
-
export interface StorageModuleOptions {
|
|
22
|
-
isGlobal?: boolean;
|
|
23
|
-
}
|
|
1
|
+
export type AccessMode = "private" | "public-read" | "hybrid";
|
|
2
|
+
export interface StorageOptions {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
accessKeyId: string;
|
|
5
|
+
secretAccessKey: string;
|
|
6
|
+
bucketName: string;
|
|
7
|
+
region?: string;
|
|
8
|
+
publicUrlBase?: string;
|
|
9
|
+
signedUrlExpiry?: number;
|
|
10
|
+
accessMode?: AccessMode;
|
|
11
|
+
}
|
|
12
|
+
export interface FileFieldConfig {
|
|
13
|
+
field: string;
|
|
14
|
+
sizeField?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PhotoFieldConfig {
|
|
17
|
+
field: string;
|
|
18
|
+
urlField?: string;
|
|
19
|
+
sizeField?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface StorageModuleOptions {
|
|
22
|
+
isGlobal?: boolean;
|
|
23
|
+
}
|
|
@@ -1,54 +1,23 @@
|
|
|
1
|
-
import { CloudflareService } from "./cloudflare.service";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
export interface UpdatePhotosResult<T extends Record<string, any>> {
|
|
26
|
-
updatedPayload: T;
|
|
27
|
-
uploadUrls: PhotoUploadResponse[];
|
|
28
|
-
storageIncrease: number;
|
|
29
|
-
storageDecrease: number;
|
|
30
|
-
deletedFiles: string[];
|
|
31
|
-
}
|
|
32
|
-
export interface DeletePhotosResult {
|
|
33
|
-
deletedFiles: string[];
|
|
34
|
-
totalStorageFreed: number;
|
|
35
|
-
}
|
|
36
|
-
export interface AppendUrlsOptions {
|
|
37
|
-
urlField?: (field: string) => string;
|
|
38
|
-
}
|
|
39
|
-
export declare class PhotoManagerService {
|
|
40
|
-
private readonly cloudflareService;
|
|
41
|
-
constructor(cloudflareService: CloudflareService);
|
|
42
|
-
appendPhotoUrls<T extends Record<string, any>>(payload: T[], photoFields: PhotoField[]): Promise<T[]>;
|
|
43
|
-
private handleSingleFieldUrl;
|
|
44
|
-
private handleArrayFieldUrls;
|
|
45
|
-
createObjectWithPhotos<T extends Record<string, any>>(payload: T, photoFields: PhotoField[], filePrefix?: string): Promise<CreatePhotosResult<T>>;
|
|
46
|
-
updateObjectWithPhotos<T extends Record<string, any>>(payload: T, existingObject: T, photoFields: PhotoField[], filePrefix?: string): Promise<UpdatePhotosResult<T>>;
|
|
47
|
-
deletePhotosFromObject<T extends Record<string, any>>(object: T, photoFields: PhotoField[]): Promise<DeletePhotosResult>;
|
|
48
|
-
private extractPhotoUploadRequests;
|
|
49
|
-
private extractArrayFieldUploadRequests;
|
|
50
|
-
private extractExistingFiles;
|
|
51
|
-
private determineFilesToDelete;
|
|
52
|
-
private normalizeFilename;
|
|
53
|
-
private updateArrayFieldWithNewFileKey;
|
|
54
|
-
}
|
|
1
|
+
import { CloudflareService } from "./cloudflare.service";
|
|
2
|
+
import { CreatePhotosResult, DeletePhotosResult, PhotoField, UpdatePhotosResult } from "./interfaces/photo-manager.interface";
|
|
3
|
+
type FileMap = Map<string, string>;
|
|
4
|
+
export declare class PhotoManagerService {
|
|
5
|
+
private readonly cloudflareService;
|
|
6
|
+
constructor(cloudflareService: CloudflareService);
|
|
7
|
+
appendPhotoUrls<T extends Record<string, any>>(payload: T, photoFields: PhotoField[]): Promise<T>;
|
|
8
|
+
appendPhotoUrls<T extends Record<string, any>>(payload: T[], photoFields: PhotoField[]): Promise<T[]>;
|
|
9
|
+
private handleSingleFieldUrl;
|
|
10
|
+
private handleArrayFieldUrls;
|
|
11
|
+
createObjectWithPhotos<T extends Record<string, any>>(payload: T, photoFields: PhotoField[], filePrefix?: string): Promise<CreatePhotosResult<T>>;
|
|
12
|
+
updateObjectWithPhotos<T extends Record<string, any>>(payload: T, existingObject: T, photoFields: PhotoField[], filePrefix?: string): Promise<UpdatePhotosResult<T>>;
|
|
13
|
+
deletePhotosFromObject<T extends Record<string, any>>(object: T, photoFields: PhotoField[]): Promise<DeletePhotosResult>;
|
|
14
|
+
extractExistingFileMap<T extends Record<string, any>>(object: T, photoFields: PhotoField[]): FileMap;
|
|
15
|
+
private extractPayloadFileMap;
|
|
16
|
+
private getArrayFieldPaths;
|
|
17
|
+
private calculatePhotoDiffs;
|
|
18
|
+
private getSizeForField;
|
|
19
|
+
private updateFieldWithFileKey;
|
|
20
|
+
private extractPhotoUploadRequests;
|
|
21
|
+
private extractExistingFiles;
|
|
22
|
+
}
|
|
23
|
+
export {};
|