@edgedev/firebase 2.0.29 → 2.0.32

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,17 +48,15 @@ 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
 
57
+
58
+ import { getStorage, ref as storageRef, uploadBytes, getDownloadURL} from "firebase/storage";
59
+
62
60
  import { getFunctions, httpsCallable, connectFunctionsEmulator } from "firebase/functions";
63
61
 
64
62
  import { getAnalytics, logEvent } from "firebase/analytics";
@@ -236,8 +234,10 @@ export const EdgeFirebase = class {
236
234
 
237
235
  if (this.firebaseConfig.measurementId) {
238
236
  this.anaytics = getAnalytics(this.app);
237
+ }
238
+ if (this.firebaseConfig.storageBucket) {
239
+ this.storage = getStorage(this.app);
239
240
  }
240
-
241
241
  this.functions = getFunctions(this.app);
242
242
  if (this.firebaseConfig.emulatorFunctions) {
243
243
  connectFunctionsEmulator(this.functions, "127.0.0.1", this.firebaseConfig.emulatorFunctions)
@@ -250,7 +250,7 @@ export const EdgeFirebase = class {
250
250
  public app = null;
251
251
  public auth = null;
252
252
  public db = null;
253
-
253
+ public storage = null;
254
254
  private anaytics = null;
255
255
 
256
256
  private functions = null;
@@ -2119,4 +2119,70 @@ export const EdgeFirebase = class {
2119
2119
  this.unsubscribe[collectionPath] = null;
2120
2120
  }
2121
2121
  };
2122
+
2123
+
2124
+ // File functions
2125
+ public uploadFileToStorage = async (filePath: string, file: Blob): Promise<actionResponse> => {
2126
+ try {
2127
+ // Validate if file is provided
2128
+ if (!file) {
2129
+ return this.sendResponse({
2130
+ success: false,
2131
+ message: "No file provided for upload.",
2132
+ meta: {}
2133
+ });
2134
+ }
2135
+
2136
+ // Check if the user has write permission to the filePath
2137
+ const hasWritePermission = await this.permissionCheck("write", filePath);
2138
+ if (!hasWritePermission) {
2139
+ return this.sendResponse({
2140
+ success: false,
2141
+ message: "You do not have permission to upload files to this path.",
2142
+ meta: {}
2143
+ });
2144
+ }
2145
+
2146
+ // Initialize Firebase Storage
2147
+
2148
+ // Define a temporary path for the file upload
2149
+ // You might want to include some unique identifier in the temporary path
2150
+ const tempFilePath = `temp/${this.user.uid}/${filePath.replaceAll('/', '-|-')}`;
2151
+
2152
+ // Create a reference to the temporary file location
2153
+ const fileRef = storageRef(this.storage, tempFilePath);
2154
+
2155
+ // Upload the file to the temporary location
2156
+ await uploadBytes(fileRef, file);
2157
+
2158
+ // Prepare data for the callable function
2159
+ const data = {
2160
+ uid: this.user.uid,
2161
+ filePath: filePath, // The final desired path for the file
2162
+ };
2163
+
2164
+ // Call the Firebase Function to handle the file move and any additional processing
2165
+ const callable = httpsCallable(this.functions, 'edgeFirebase-uploadFile');
2166
+ const functionResult = await callable(data);
2167
+
2168
+ const resultData = functionResult.data as { success: boolean; message: string; finalDownloadURL?: string };
2169
+
2170
+ if (!resultData.success) {
2171
+ throw new Error(resultData.message);
2172
+ }
2173
+ // Return success response
2174
+ return this.sendResponse({
2175
+ success: true,
2176
+ message: "File processed successfully.",
2177
+ meta: {}
2178
+ });
2179
+ } catch (error) {
2180
+ console.error(error);
2181
+ return this.sendResponse({
2182
+ success: false,
2183
+ message: "An error occurred during file processing.",
2184
+ meta: {}
2185
+ });
2186
+ }
2187
+ };
2122
2188
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgedev/firebase",
3
- "version": "2.0.29",
3
+ "version": "2.0.32",
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=
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=
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
@@ -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