@mohasinac/appkit 3.2.9 → 3.2.10
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.
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* <AsyncFacetSection loadOptions={load} ... />
|
|
11
11
|
*/
|
|
12
12
|
import { apiClient } from "../../../../http/ApiClient";
|
|
13
|
+
import { normalizeError } from "../../../../errors/normalize";
|
|
13
14
|
const PAGE_SIZE = 25;
|
|
14
15
|
function buildUrl(base, q, page, extra) {
|
|
15
16
|
const params = new URLSearchParams({
|
|
@@ -29,7 +30,7 @@ async function fetchPage(url, mapItem) {
|
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
catch (_err) {
|
|
32
|
-
// PaginatedSelect degrades to
|
|
33
|
+
void normalizeError(_err); // PaginatedSelect degrades to empty page; caller can retry by re-typing
|
|
33
34
|
return { items: [], hasMore: false };
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
import { getAdminStorageLite } from "../db-firebase/admin-storage-lite";
|
|
23
|
+
import { withRetry } from "../../http/retry";
|
|
24
|
+
import { normalizeError } from "../../errors/normalize";
|
|
25
|
+
import { serverLogger } from "../../monitoring/server-logger";
|
|
23
26
|
function getBucket() {
|
|
24
27
|
const bucketName = process.env.FIREBASE_ADMIN_STORAGE_BUCKET?.trim() ??
|
|
25
28
|
process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET?.trim();
|
|
@@ -29,75 +32,126 @@ function getBucket() {
|
|
|
29
32
|
}
|
|
30
33
|
export const firebaseStorageProvider = {
|
|
31
34
|
async upload(file, storagePath, options) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
35
|
+
try {
|
|
36
|
+
const bucket = getBucket();
|
|
37
|
+
const fileRef = bucket.file(storagePath);
|
|
38
|
+
await withRetry(() => fileRef.save(file, {
|
|
39
|
+
metadata: {
|
|
40
|
+
contentType: options?.contentType ?? "application/octet-stream",
|
|
41
|
+
cacheControl: options?.cacheControl ?? "public, max-age=31536000",
|
|
42
|
+
metadata: options?.metadata ?? {},
|
|
43
|
+
},
|
|
44
|
+
public: options?.isPublic !== false,
|
|
45
|
+
}), 2, 300);
|
|
46
|
+
const [metadata] = await withRetry(() => fileRef.getMetadata(), 2, 300);
|
|
47
|
+
const url = options?.isPublic !== false
|
|
48
|
+
? `https://storage.googleapis.com/${bucket.name}/${storagePath}`
|
|
49
|
+
: await withRetry(() => fileRef
|
|
50
|
+
.getSignedUrl({
|
|
51
|
+
action: "read",
|
|
52
|
+
expires: Date.now() + 60 * 60 * 1000,
|
|
53
|
+
})
|
|
54
|
+
.then(([u]) => u), 2, 300);
|
|
55
|
+
return {
|
|
56
|
+
path: storagePath,
|
|
57
|
+
url,
|
|
58
|
+
contentType: metadata.contentType,
|
|
59
|
+
size: Number(metadata.size),
|
|
60
|
+
updatedAt: metadata.updated,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
void normalizeError(err);
|
|
65
|
+
serverLogger.error("storage-provider: upload failed", {
|
|
66
|
+
storagePath,
|
|
67
|
+
error: err instanceof Error ? err.message : String(err),
|
|
68
|
+
});
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
58
71
|
},
|
|
59
72
|
async delete(storagePath) {
|
|
60
|
-
|
|
73
|
+
try {
|
|
74
|
+
await withRetry(() => getBucket().file(storagePath).delete({ ignoreNotFound: true }), 2, 300);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
void normalizeError(err);
|
|
78
|
+
serverLogger.error("storage-provider: delete failed", {
|
|
79
|
+
storagePath,
|
|
80
|
+
error: err instanceof Error ? err.message : String(err),
|
|
81
|
+
});
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
61
84
|
},
|
|
62
85
|
getPublicUrl(storagePath) {
|
|
63
86
|
const bucket = getBucket();
|
|
64
87
|
return `https://storage.googleapis.com/${bucket.name}/${storagePath}`;
|
|
65
88
|
},
|
|
66
89
|
async getSignedUrl(storagePath, expiresInSeconds = 3600) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
90
|
+
try {
|
|
91
|
+
const [url] = await withRetry(() => getBucket()
|
|
92
|
+
.file(storagePath)
|
|
93
|
+
.getSignedUrl({
|
|
94
|
+
action: "read",
|
|
95
|
+
expires: Date.now() + expiresInSeconds * 1000,
|
|
96
|
+
}), 2, 300);
|
|
97
|
+
return url;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
void normalizeError(err);
|
|
101
|
+
serverLogger.error("storage-provider: getSignedUrl failed", {
|
|
102
|
+
storagePath,
|
|
103
|
+
error: err instanceof Error ? err.message : String(err),
|
|
104
|
+
});
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
74
107
|
},
|
|
75
108
|
async copy(from, to) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
path: to,
|
|
83
|
-
url: `https://storage.googleapis.com/${bucket.name}/${to}`,
|
|
84
|
-
contentType: metadata.contentType,
|
|
85
|
-
size: Number(metadata.size),
|
|
86
|
-
updatedAt: metadata.updated,
|
|
87
|
-
};
|
|
88
|
-
},
|
|
89
|
-
async list(prefix) {
|
|
90
|
-
const bucket = getBucket();
|
|
91
|
-
const [files] = await bucket.getFiles({ prefix });
|
|
92
|
-
return Promise.all(files.map(async (f) => {
|
|
93
|
-
const [metadata] = await f.getMetadata();
|
|
109
|
+
try {
|
|
110
|
+
const bucket = getBucket();
|
|
111
|
+
const src = bucket.file(from);
|
|
112
|
+
const dest = bucket.file(to);
|
|
113
|
+
await withRetry(() => src.copy(dest), 2, 300);
|
|
114
|
+
const [metadata] = await withRetry(() => dest.getMetadata(), 2, 300);
|
|
94
115
|
return {
|
|
95
|
-
path:
|
|
96
|
-
url: `https://storage.googleapis.com/${bucket.name}/${
|
|
116
|
+
path: to,
|
|
117
|
+
url: `https://storage.googleapis.com/${bucket.name}/${to}`,
|
|
97
118
|
contentType: metadata.contentType,
|
|
98
119
|
size: Number(metadata.size),
|
|
99
120
|
updatedAt: metadata.updated,
|
|
100
121
|
};
|
|
101
|
-
}
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
void normalizeError(err);
|
|
125
|
+
serverLogger.error("storage-provider: copy failed", {
|
|
126
|
+
from,
|
|
127
|
+
to,
|
|
128
|
+
error: err instanceof Error ? err.message : String(err),
|
|
129
|
+
});
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
async list(prefix) {
|
|
134
|
+
try {
|
|
135
|
+
const bucket = getBucket();
|
|
136
|
+
const [files] = await withRetry(() => bucket.getFiles({ prefix }), 2, 300);
|
|
137
|
+
return Promise.all(files.map(async (f) => {
|
|
138
|
+
const [metadata] = await withRetry(() => f.getMetadata(), 2, 300);
|
|
139
|
+
return {
|
|
140
|
+
path: f.name,
|
|
141
|
+
url: `https://storage.googleapis.com/${bucket.name}/${f.name}`,
|
|
142
|
+
contentType: metadata.contentType,
|
|
143
|
+
size: Number(metadata.size),
|
|
144
|
+
updatedAt: metadata.updated,
|
|
145
|
+
};
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
void normalizeError(err);
|
|
150
|
+
serverLogger.error("storage-provider: list failed", {
|
|
151
|
+
prefix,
|
|
152
|
+
error: err instanceof Error ? err.message : String(err),
|
|
153
|
+
});
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
102
156
|
},
|
|
103
157
|
};
|