@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 CHANGED
@@ -601,8 +601,17 @@ export const EdgeFirebase = class {
601
601
  }
602
602
  }
603
603
  }
604
- const userRef = doc(this.db, "staged-users", userRegister.registrationCode);
605
- const userSnap = await getDoc(userRef);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "2.1.58",
3
+ "version": "2.1.60",
4
4
  "description": "Vue 3 / Nuxt 3 Plugin or Nuxt 3 plugin for firebase authentication and firestore.",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -1,6 +1,8 @@
1
1
  const AWS = require('aws-sdk')
2
- const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub, Storage, permissionCheck, onObjectFinalized, onObjectDeleted, onDocumentDeleted } = require('./config.js')
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
- const updatedMetadata = {
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
+ }