@edgedev/firebase 2.1.58 → 2.1.60
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/edgeFirebase.ts +13 -4
- package/package.json +1 -1
- package/src/edgeFirebase.js +108 -2
package/edgeFirebase.ts
CHANGED
|
@@ -601,8 +601,17 @@ export const EdgeFirebase = class {
|
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
|
-
|
|
605
|
-
|
|
604
|
+
let userSnap = null;
|
|
605
|
+
try {
|
|
606
|
+
const userRef = doc(this.db, "staged-users", userRegister.registrationCode);
|
|
607
|
+
userSnap = await getDoc(userRef);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
return this.sendResponse({
|
|
610
|
+
success: false,
|
|
611
|
+
message: "Registration code already used.",
|
|
612
|
+
meta: {}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
606
615
|
if (userSnap.exists()) {
|
|
607
616
|
const user = userSnap.data();
|
|
608
617
|
if (user.userId) {
|
|
@@ -2095,7 +2104,7 @@ export const EdgeFirebase = class {
|
|
|
2095
2104
|
|
|
2096
2105
|
// File functions
|
|
2097
2106
|
// TODO... add object to pass merge with customMetadata as var....
|
|
2098
|
-
public uploadFile = async (orgId: string, file: Blob, filePath: string = "", isPublic: boolean = false, toR2: boolean = false): Promise<actionResponse> => {
|
|
2107
|
+
public uploadFile = async (orgId: string, file: Blob, filePath: string = "", isPublic: boolean = false, toR2: boolean = false, extraMeta: object = null): Promise<actionResponse> => {
|
|
2099
2108
|
// Finish toCF function hook.
|
|
2100
2109
|
this.state.currentUploadProgress = 0;
|
|
2101
2110
|
let collectionPath = "organizations/" + orgId;
|
|
@@ -2206,7 +2215,7 @@ export const EdgeFirebase = class {
|
|
|
2206
2215
|
|
|
2207
2216
|
|
|
2208
2217
|
const uploadStatus = await uploadPromise;
|
|
2209
|
-
await this.runFunction("edgeFirebase-addUpdateFileDoc", {orgId, docId: fileDocId, uploadStatus, uploadCompleted: true });
|
|
2218
|
+
await this.runFunction("edgeFirebase-addUpdateFileDoc", {orgId, docId: fileDocId, uploadStatus, uploadCompleted: true, extraMeta });
|
|
2210
2219
|
return uploadStatus;
|
|
2211
2220
|
|
|
2212
2221
|
} catch (error) {
|
package/package.json
CHANGED
package/src/edgeFirebase.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const AWS = require('aws-sdk')
|
|
2
|
-
const
|
|
2
|
+
const FormData = require('form-data')
|
|
3
|
+
const fetch = require('node-fetch')
|
|
3
4
|
|
|
5
|
+
const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub, Storage, permissionCheck, onObjectFinalized, onObjectDeleted, onDocumentDeleted } = require('./config.js')
|
|
4
6
|
const authToken = process.env.TWILIO_AUTH_TOKEN
|
|
5
7
|
const accountSid = process.env.TWILIO_SID
|
|
6
8
|
const systemNumber = process.env.TWILIO_SYSTEM_NUMBER
|
|
@@ -75,7 +77,11 @@ const deleteR2File = async (filePath) => {
|
|
|
75
77
|
exports.fileDeleted = onObjectDeleted({ region: process.env.FIREBASE_STORAGE_BUCKET_REGION }, async (event) => {
|
|
76
78
|
const docId = event.data.metadata?.fileDocId
|
|
77
79
|
const toR2 = event.data.metadata?.toR2
|
|
80
|
+
const cloudflareImageId = event.data.metadata?.cloudflareImageId
|
|
78
81
|
const r2ProcessCompleted = event.data.metadata?.r2ProcessCompleted
|
|
82
|
+
if (cloudflareImageId) {
|
|
83
|
+
await deleteCloudflareImage(cloudflareImageId)
|
|
84
|
+
}
|
|
79
85
|
if (toR2) {
|
|
80
86
|
if (r2ProcessCompleted === 'true') {
|
|
81
87
|
await deleteR2File(event.data.metadata?.r2FilePath)
|
|
@@ -119,6 +125,7 @@ exports.toR2 = onObjectFinalized(
|
|
|
119
125
|
const r2FilePath = `${process.env.FIREBASE_STORAGE_BUCKET}/${event.data.metadata?.filePath}`
|
|
120
126
|
const r2URL = `${process.env.CLOUDFLARE_R2_PUBLIC_URL}/${r2FilePath}`
|
|
121
127
|
const fileName = event.data.metadata?.fileName // File name.
|
|
128
|
+
const fileSize = event.data.metadata?.fileSize
|
|
122
129
|
const contentType = event.data.contentType // File content type.
|
|
123
130
|
|
|
124
131
|
// Download file into memory from bucket.
|
|
@@ -139,6 +146,7 @@ exports.toR2 = onObjectFinalized(
|
|
|
139
146
|
const fileRef = bucket.file(filePath)
|
|
140
147
|
try {
|
|
141
148
|
await r2.upload(params).promise()
|
|
149
|
+
|
|
142
150
|
const fileDocId = event.data.metadata?.fileDocId
|
|
143
151
|
const orgId = event.data.metadata?.orgId
|
|
144
152
|
const docRef = db.collection(`organizations/${orgId}/files`).doc(fileDocId)
|
|
@@ -157,7 +165,7 @@ exports.toR2 = onObjectFinalized(
|
|
|
157
165
|
})
|
|
158
166
|
|
|
159
167
|
// Step 2: Update metadata with additional fields
|
|
160
|
-
|
|
168
|
+
let updatedMetadata = {
|
|
161
169
|
metadata: {
|
|
162
170
|
...event.data.metadata,
|
|
163
171
|
r2FilePath,
|
|
@@ -166,6 +174,36 @@ exports.toR2 = onObjectFinalized(
|
|
|
166
174
|
r2ProcessCompleted: 'true',
|
|
167
175
|
},
|
|
168
176
|
}
|
|
177
|
+
if (contentType.startsWith('image/')) {
|
|
178
|
+
try {
|
|
179
|
+
const cloudflareImage = await uploadToCloudflareImage({
|
|
180
|
+
r2FilePath,
|
|
181
|
+
r2URL,
|
|
182
|
+
fileDocId,
|
|
183
|
+
orgId,
|
|
184
|
+
fileName,
|
|
185
|
+
fileSize,
|
|
186
|
+
})
|
|
187
|
+
console.log(cloudflareImage)
|
|
188
|
+
|
|
189
|
+
updatedMetadata = {
|
|
190
|
+
metadata: {
|
|
191
|
+
...event.data.metadata,
|
|
192
|
+
r2FilePath,
|
|
193
|
+
r2URL,
|
|
194
|
+
uploadCompletedToR2: 'true', // Add custom metadata after file save
|
|
195
|
+
r2ProcessCompleted: 'true',
|
|
196
|
+
cloudflareImageId: cloudflareImage.id,
|
|
197
|
+
cloudflareImageVariants: cloudflareImage.variants,
|
|
198
|
+
cloudflareUploadCompleted: true,
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
await docRef.set({ cloudflareImageId: cloudflareImage.id, cloudflareImageVariants: cloudflareImage.variants, cloudflareUploadCompleted: true }, { merge: true })
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
console.error('Cloudflare Image Upload Failed', e)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
169
207
|
|
|
170
208
|
await fileRef.setMetadata(updatedMetadata)
|
|
171
209
|
console.log(`File uploaded to Cloudflare R2: ${fileName}`)
|
|
@@ -593,3 +631,71 @@ function markProcessed(eventRef) {
|
|
|
593
631
|
return null
|
|
594
632
|
})
|
|
595
633
|
}
|
|
634
|
+
|
|
635
|
+
async function uploadToCloudflareImage({ r2FilePath, r2URL, fileDocId, orgId, fileName, fileSize }) {
|
|
636
|
+
const cleanedr2FilePath = r2FilePath.replaceAll('/', '-').replace('.firebasestorage.app-organizations', '')
|
|
637
|
+
const metadata = {
|
|
638
|
+
orgId,
|
|
639
|
+
fileDocId,
|
|
640
|
+
fileName: cleanedr2FilePath,
|
|
641
|
+
fileSize,
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const API_TOKEN = process.env.CF_IMAGES_TOKEN
|
|
645
|
+
const ACCOUNT_ID = process.env.CF_ACCOUNT_ID
|
|
646
|
+
|
|
647
|
+
const formData = new FormData()
|
|
648
|
+
formData.append('url', r2URL)
|
|
649
|
+
formData.append('metadata', JSON.stringify(metadata))
|
|
650
|
+
formData.append('id', cleanedr2FilePath)
|
|
651
|
+
|
|
652
|
+
const response = await fetch(
|
|
653
|
+
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/images/v1`,
|
|
654
|
+
{
|
|
655
|
+
method: 'POST',
|
|
656
|
+
headers: {
|
|
657
|
+
Authorization: `Bearer ${API_TOKEN}`,
|
|
658
|
+
...formData.getHeaders(),
|
|
659
|
+
},
|
|
660
|
+
body: formData,
|
|
661
|
+
},
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
const result = await response.json()
|
|
665
|
+
|
|
666
|
+
const { result: imageData, success, errors } = result
|
|
667
|
+
|
|
668
|
+
if (!success) {
|
|
669
|
+
const errorMessages = (errors || [])
|
|
670
|
+
.map(error => (error.message ? error.message : 'Unknown error'))
|
|
671
|
+
.join('; ')
|
|
672
|
+
throw new Error(`Cloudflare upload failed: ${errorMessages}`)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return {
|
|
676
|
+
id: imageData.id,
|
|
677
|
+
variants: imageData.variants,
|
|
678
|
+
meta: imageData.meta || {},
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async function deleteCloudflareImage(imageId) {
|
|
683
|
+
const API_TOKEN = process.env.CF_IMAGES_TOKEN
|
|
684
|
+
const ACCOUNT_ID = process.env.CF_ACCOUNT_ID
|
|
685
|
+
|
|
686
|
+
const response = await fetch(
|
|
687
|
+
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/images/v1/${imageId}`,
|
|
688
|
+
{
|
|
689
|
+
method: 'DELETE',
|
|
690
|
+
headers: {
|
|
691
|
+
Authorization: `Bearer ${API_TOKEN}`,
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
if (!response.ok) {
|
|
697
|
+
throw new Error(`Failed to delete Cloudflare image: ${response.statusText}`)
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return true
|
|
701
|
+
}
|