@edgedev/firebase 2.0.30 → 2.0.33

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
@@ -48,19 +48,14 @@ import {
48
48
  OAuthProvider,
49
49
  browserPopupRedirectResolver,
50
50
  signInWithPopup,
51
- signInWithPhoneNumber,
52
51
  sendSignInLinkToEmail,
53
- updateEmail,
54
52
  verifyBeforeUpdateEmail,
55
- RecaptchaVerifier,
56
- ConfirmationResult,
57
- PhoneAuthProvider,
58
53
  signInWithCustomToken,
59
54
  updateProfile,
60
55
  } from "firebase/auth";
61
56
 
62
- import { getStorage, ref as storageRef, uploadBytes, listAll, getDownloadURL, deleteObject } from "firebase/storage";
63
57
 
58
+ import { getStorage, ref as storageRef, uploadBytes, getDownloadURL, connectStorageEmulator} from "firebase/storage";
64
59
 
65
60
  import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions";
66
61
 
@@ -186,6 +181,7 @@ interface firebaseConfig {
186
181
  measurementId?: string;
187
182
  emulatorFirestore?: string;
188
183
  emulatorFunctions?: string;
184
+ emulatorStorage?: string;
189
185
  }
190
186
 
191
187
  interface actionResponse {
@@ -211,7 +207,8 @@ export const EdgeFirebase = class {
211
207
  measurementId: "",
212
208
  emulatorAuth: "",
213
209
  emulatorFirestore: "",
214
- emulatorFunctions: ""
210
+ emulatorFunctions: "",
211
+ emulatorStorage: "",
215
212
  },
216
213
  isPersistant: false,
217
214
  enablePopupRedirect: false,
@@ -239,17 +236,17 @@ export const EdgeFirebase = class {
239
236
 
240
237
  if (this.firebaseConfig.measurementId) {
241
238
  this.anaytics = getAnalytics(this.app);
242
- }
243
-
244
- if (this.firebaseConfig.storageBucket) {
239
+ }
240
+ if (this.firebaseConfig.storageBucket) {
245
241
  this.storage = getStorage(this.app);
246
242
  }
247
-
248
243
  this.functions = getFunctions(this.app);
249
244
  if (this.firebaseConfig.emulatorFunctions) {
250
245
  connectFunctionsEmulator(this.functions, "127.0.0.1", this.firebaseConfig.emulatorFunctions)
251
246
  }
252
- this.setOnAuthStateChanged();
247
+ if (this.firebaseConfig.emulatorStorage) {
248
+ connectStorageEmulator(this.storage, "127.0.0.1", this.firebaseConfig.emulatorStorage)
249
+ }
253
250
  }
254
251
 
255
252
  private firebaseConfig = null;
@@ -258,7 +255,6 @@ export const EdgeFirebase = class {
258
255
  public auth = null;
259
256
  public db = null;
260
257
  public storage = null;
261
-
262
258
  private anaytics = null;
263
259
 
264
260
  private functions = null;
@@ -2127,48 +2123,70 @@ export const EdgeFirebase = class {
2127
2123
  this.unsubscribe[collectionPath] = null;
2128
2124
  }
2129
2125
  };
2130
- // Firestore Storage
2131
- // Upload a file
2132
- async uploadFileToStorage(filePath, file) {
2133
- const canWrite = await this.permissionCheck("write", filePath);
2134
- if (!canWrite) {
2135
- return { success: false, message: "Permission denied" };
2136
- }
2137
- const storagePath = storageRef(this.storage, filePath);
2138
- await uploadBytes(storagePath, file);
2139
- return { success: true, message: "File uploaded successfully" };
2140
- }
2141
2126
 
2142
- // List all files in a directory
2143
- async listFilesInStorage(directoryPath) {
2144
- const canRead = await this.permissionCheck("read", directoryPath);
2145
- if (!canRead) {
2146
- return { success: false, message: "Permission denied" };
2147
- }
2148
- const dirRef = storageRef(this.storage, directoryPath);
2149
- const fileList = await listAll(dirRef);
2150
- return { success: true, files: fileList.items.map(item => item.fullPath) };
2151
- }
2152
2127
 
2153
- // Download a file
2154
- async downloadFileFromStorage(filePath) {
2155
- const canRead = await this.permissionCheck("read", filePath);
2156
- if (!canRead) {
2157
- return { success: false, message: "Permission denied" };
2158
- }
2159
- const fileRef = storageRef(this.storage, filePath);
2160
- const downloadUrl = await getDownloadURL(fileRef);
2161
- return { success: true, url: downloadUrl };
2162
- }
2128
+ // File functions
2129
+ public uploadFileToStorage = async (filePath: string, file: Blob): Promise<actionResponse> => {
2130
+ try {
2131
+ // Validate if file is provided
2132
+ if (!file) {
2133
+ return this.sendResponse({
2134
+ success: false,
2135
+ message: "No file provided for upload.",
2136
+ meta: {}
2137
+ });
2138
+ }
2163
2139
 
2164
- // Delete a fileå
2165
- async deleteFileFromStorage(filePath) {
2166
- const canDelete = await this.permissionCheck("delete", filePath);
2167
- if (!canDelete) {
2168
- return { success: false, message: "Permission denied" };
2140
+ // Check if the user has write permission to the filePath
2141
+ const hasWritePermission = await this.permissionCheck("write", filePath);
2142
+ if (!hasWritePermission) {
2143
+ return this.sendResponse({
2144
+ success: false,
2145
+ message: "You do not have permission to upload files to this path.",
2146
+ meta: {}
2147
+ });
2148
+ }
2149
+
2150
+ // Initialize Firebase Storage
2151
+
2152
+ // Define a temporary path for the file upload
2153
+ // You might want to include some unique identifier in the temporary path
2154
+ const tempFilePath = `temp/${this.user.uid}/${filePath.replaceAll('/', '-|-')}`;
2155
+
2156
+ // Create a reference to the temporary file location
2157
+ const fileRef = storageRef(this.storage, tempFilePath);
2158
+
2159
+ // Upload the file to the temporary location
2160
+ await uploadBytes(fileRef, file);
2161
+
2162
+ // Prepare data for the callable function
2163
+ const data = {
2164
+ uid: this.user.uid,
2165
+ filePath: filePath, // The final desired path for the file
2166
+ };
2167
+
2168
+ // Call the Firebase Function to handle the file move and any additional processing
2169
+ const callable = httpsCallable(this.functions, 'edgeFirebase-uploadFile');
2170
+ const functionResult = await callable(data);
2171
+
2172
+ const resultData = functionResult.data as { success: boolean; message: string; finalDownloadURL?: string };
2173
+
2174
+ if (!resultData.success) {
2175
+ throw new Error(resultData.message);
2176
+ }
2177
+ // Return success response
2178
+ return this.sendResponse({
2179
+ success: true,
2180
+ message: "File processed successfully.",
2181
+ meta: {}
2182
+ });
2183
+ } catch (error) {
2184
+ console.error(error);
2185
+ return this.sendResponse({
2186
+ success: false,
2187
+ message: "An error occurred during file processing.",
2188
+ meta: {}
2189
+ });
2169
2190
  }
2170
- const fileRef = storageRef(this.storage, filePath);
2171
- await deleteObject(fileRef);
2172
- return { success: true, message: "File deleted successfully" };
2173
- }
2191
+ };
2174
2192
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "2.0.30",
3
+ "version": "2.0.33",
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": {
package/src/.env.dev CHANGED
@@ -4,4 +4,5 @@ STRIPE_RETURN_URL=
4
4
  OPENAI_API_KEY=
5
5
  TWILIO_SID=
6
6
  TWILIO_AUTH_TOKEN=
7
- TWILIO_SYSTEM_NUMBER=
7
+ TWILIO_SYSTEM_NUMBER=
8
+ STORAGE_BUCKET=
@@ -7,4 +7,5 @@ VITE_FIREBASE_APP_ID=
7
7
  VITE_FIREBASE_MEASUREMENT_ID=
8
8
  VITE_FIREBASE_EMULATOR_AUTH=9099
9
9
  VITE_FIREBASE_EMULATOR_FIRESTORE=8080
10
- VITE_FIREBASE_EMULATOR_FUNCTIONS=5001
10
+ VITE_FIREBASE_EMULATOR_FUNCTIONS=5001
11
+ VITE_FIREBASE_EMULATOR_STORAGE=9199
package/src/.env.prod CHANGED
@@ -4,4 +4,5 @@ STRIPE_RETURN_URL=
4
4
  OPENAI_API_KEY=
5
5
  TWILIO_SID=
6
6
  TWILIO_AUTH_TOKEN=
7
- TWILIO_SYSTEM_NUMBER=
7
+ TWILIO_SYSTEM_NUMBER=
8
+ STORAGE_BUCKET=
@@ -7,4 +7,5 @@ VITE_FIREBASE_APP_ID=
7
7
  VITE_FIREBASE_MEASUREMENT_ID=
8
8
  VITE_FIREBASE_EMULATOR_AUTH=
9
9
  VITE_FIREBASE_EMULATOR_FIRESTORE=
10
- VITE_FIREBASE_EMULATOR_FUNCTIONS=
10
+ VITE_FIREBASE_EMULATOR_FUNCTIONS=
11
+ VITE_FIREBASE_EMULATOR_STORAGE=
package/src/config.js CHANGED
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ /* eslint-disable no-undef */
1
3
  const functions = require('firebase-functions')
2
4
  const { PubSub } = require('@google-cloud/pubsub')
3
5
  const admin = require('firebase-admin')
@@ -10,6 +12,7 @@ const { onMessagePublished } = require('firebase-functions/v2/pubsub')
10
12
 
11
13
  const { onCall, HttpsError, onRequest } = require('firebase-functions/v2/https')
12
14
  const { onSchedule } = require('firebase-functions/v2/scheduler')
15
+ const { Storage } = require('@google-cloud/storage')
13
16
  const {
14
17
  onDocumentWritten,
15
18
  onDocumentCreated,
@@ -42,5 +45,6 @@ module.exports = {
42
45
  admin,
43
46
  twilio,
44
47
  db,
48
+ Storage,
45
49
  }
46
50
 
@@ -1,9 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ /* eslint-disable no-undef */
1
3
  const { onCall, HttpsError, logger, getFirestore, functions, admin, twilio, db, onSchedule, onDocumentUpdated, pubsub } = require('./config.js')
2
4
 
3
5
  const authToken = process.env.TWILIO_AUTH_TOKEN
4
6
  const accountSid = process.env.TWILIO_SID
5
7
  const systemNumber = process.env.TWILIO_SYSTEM_NUMBER
6
8
 
9
+
7
10
  function formatPhoneNumber(phone) {
8
11
  // Remove non-numeric characters from the phone number
9
12
  const numericPhone = phone.replace(/\D/g, '')
@@ -11,6 +14,120 @@ function formatPhoneNumber(phone) {
11
14
  return `+1${numericPhone}`
12
15
  }
13
16
 
17
+
18
+
19
+ // File functions:
20
+
21
+ //TODO: NEED TO WRITE WRAPPERS FOR THESE IN THE edgeFirebase.js file... UPLOAD has to do alot more... upload needs to acutall upload the file to firestorage... get the path, then pass it to the uploadFile function...
22
+ //TODO: the uploadFile funntion needs to delete the file if they don't have permission to write to the path...
23
+
24
+ const bucketName = process.env.BUCKET_NAME
25
+ const storage = new Storage();
26
+ const bucket = storage.bucket(bucketName)
27
+
28
+ exports.uploadFile = onCall(async (request) => {
29
+ const auth = request.auth
30
+ if (data.uid !== auth.uid) {
31
+ throw new functions.https.HttpsError('permission-denied', 'You do not have permission to upload files for this user.');
32
+ }
33
+ const tempFilesPath = `temp/${auth.uid}/`;
34
+ const [files] = await bucket.getFiles({ prefix: tempFilesPath });
35
+ for (const file of files) {
36
+ const originalFilePath = file.name.replace(/-\|-/g, '/');
37
+ const hasWritePermission = await permissionCheck(auth.uid, "write", originalFilePath);
38
+ if (hasWritePermission) {
39
+ // Move file to the new path
40
+ await bucket.file(file.name).move(originalFilePath);
41
+ } else {
42
+ // Delete the file if no write permission
43
+ await bucket.file(file.name).delete();
44
+ }
45
+ }
46
+ });
47
+
48
+ exports.downloadFile = onCall(async (request) => {
49
+ const data = request.data;
50
+ const auth = request.auth
51
+ if (data.uid !== auth.uid) {
52
+ throw new functions.https.HttpsError('permission-denied', 'You do not have permission to upload files for this user.');
53
+ }
54
+ // Permission check for downloading the specified file
55
+ const canRead = await permissionCheck(auth.uid, "read", data.filePath);
56
+ if (!canRead) {
57
+ throw new HttpsError('permission-denied', 'You do not have permission to download this file.');
58
+ }
59
+
60
+ const options = {
61
+ version: 'v4',
62
+ action: 'read',
63
+ expires: Date.now() + 5 * 60 * 1000, // 5 minutes
64
+ };
65
+
66
+ try {
67
+ const [url] = await bucket.file(data.filePath).getSignedUrl(options);
68
+ return { success: true, url };
69
+ } catch (error) {
70
+ logger.error(error);
71
+ throw new HttpsError('internal', 'Unable to generate download URL.');
72
+ }
73
+ });
74
+
75
+ exports.listFiles = onCall(async (request) => {
76
+ // Validate user authentication
77
+ const data = request.data
78
+ const auth = request.auth
79
+ if (data.uid !== auth.uid) {
80
+ throw new functions.https.HttpsError('permission-denied', 'You do not have permission to upload files for this user.');
81
+ }
82
+
83
+ // Permission check for reading the specified directory
84
+ const canRead = await permissionCheck(auth.uid, "read", data.directoryPath);
85
+ if (!canRead) {
86
+ throw new HttpsError('permission-denied', 'You do not have permission to list files in this directory.');
87
+ }
88
+
89
+ try {
90
+ const [files] = await bucket.getFiles({ prefix: data.directoryPath });
91
+ const fileList = files.map(file => file.name);
92
+ return { success: true, fileList };
93
+ } catch (error) {
94
+ logger.error(error);
95
+ throw new HttpsError('internal', 'Unable to list files.');
96
+ }
97
+ });
98
+
99
+
100
+ exports.deleteFile = onCall(async (request) => {
101
+ // Validate user authentication
102
+ const data = request.data
103
+ const auth = request.auth
104
+ if (data.uid !== auth.uid) {
105
+ throw new functions.https.HttpsError('permission-denied', 'You do not have permission to upload files for this user.');
106
+ }
107
+
108
+ // Extract filePath from the request data
109
+ const filePath = data.filePath;
110
+
111
+ // Perform permission check for deleting the specified file
112
+ const canDelete = await permissionCheck(auth.uid, "delete", filePath);
113
+ if (!canDelete) {
114
+ throw new functions.https.HttpsError('permission-denied', 'You do not have permission to delete this file.');
115
+ }
116
+
117
+ try {
118
+ // Specify your bucket name
119
+ await storage.bucket(bucketName).file(filePath).delete();
120
+
121
+ return { success: true, message: "File successfully deleted." };
122
+ } catch (error) {
123
+ console.error("Error deleting file:", error);
124
+ throw new functions.https.HttpsError('internal', 'Failed to delete file.');
125
+ }
126
+ });
127
+
128
+ //end file functions
129
+
130
+
14
131
  exports.topicQueue = onSchedule({ schedule: 'every 1 minutes', timeoutSeconds: 180 }, async (event) => {
15
132
  const queuedTopicsRef = db.collection('topic-queue')
16
133
  const snapshot = await queuedTopicsRef.get()
@@ -22,6 +22,25 @@ awk '/\/\/ #EDGE FIREBASE RULES START/,/\/\/ #EDGE FIREBASE RULES END/' ./src/fi
22
22
  sed -e '1s/^/\/\/ #EDGE FIREBASE RULES START\n/' -e '$s/$/\n\/\/ #EDGE FIREBASE RULES END/' \
23
23
  >> "$project_root/firestore.rules";
24
24
 
25
+
26
+ # Check if the destination file exists
27
+ if [ ! -f "$project_root/storage.rules" ]; then
28
+ # If the file does not exist, create it and add the rules_version line
29
+ echo "rules_version = '2';" > "$project_root/storage.rules";
30
+ fi
31
+
32
+ # Ensure there's a newline at the end of the file
33
+ [ "$(tail -c1 $project_root/storage.rules)" != "" ] && echo "" >> "$project_root/storage.rules"
34
+
35
+ # Remove the existing block of rules between the markers
36
+ sed -i.backup '/\/\/ #EDGE FIREBASE RULES START/,/\/\/ #EDGE FIREBASE RULES END/d' "$project_root/storage.rules"
37
+
38
+ # Extract the code block from the source file and append it to the destination file
39
+ awk '/\/\/ #EDGE FIREBASE RULES START/,/\/\/ #EDGE FIREBASE RULES END/' ./src/storage.rules | \
40
+ sed '1d;$d' | \
41
+ sed -e '1s/^/\/\/ #EDGE FIREBASE RULES START\n/' -e '$s/$/\n\/\/ #EDGE FIREBASE RULES END/' \
42
+ >> "$project_root/storage.rules";
43
+
25
44
  if [ ! -d "$project_root/functions" ]; then
26
45
  mkdir "$project_root/functions"
27
46
  fi
@@ -41,12 +60,12 @@ if [ ! -f "$project_root/functions/.env.prod" ]; then
41
60
  cp ./src/.env.prod "$project_root/functions/.env.prod"
42
61
  fi
43
62
 
44
- if [ ! -f "$project_root/.env.development" ]; then
45
- cp ./src/.env.development "$project_root/.env.development"
63
+ if [ ! -f "$project_root/.env.dev" ]; then
64
+ cp ./src/.env.development "$project_root/.env.dev"
46
65
  fi
47
66
 
48
- if [ ! -f "$project_root/.env.production" ]; then
49
- cp ./src/.env.production "$project_root/.env.production"
67
+ if [ ! -f "$project_root/.env" ]; then
68
+ cp ./src/.env.production "$project_root/.env"
50
69
  fi
51
70
 
52
71
  if [ ! -f "$project_root/functions/package.json" ]; then
@@ -0,0 +1,19 @@
1
+ rules_version = '2';
2
+ // #EDGE FIREBASE RULES START
3
+ service firebase.storage {
4
+ match /b/{bucket}/o {
5
+ // Deny read access to all paths
6
+ match /{allPaths=**} {
7
+ allow read: if false;
8
+ }
9
+ // Allow write access only to paths matching /temp/{userId}/{anyPath=**}
10
+ // {userId} is a placeholder for the actual user ID
11
+ // {anyPath=**} matches any file path under /temp/{userId}/
12
+ match /temp/{userId}/{anyPath=**} {
13
+ allow write: if request.auth != null && request.auth.uid == userId;
14
+ // Keep read access denied
15
+ allow read: if false;
16
+ }
17
+ }
18
+ }
19
+ // #EDGE FIREBASE RULES END