@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 +73 -7
- package/package.json +1 -1
- package/src/.env.dev +2 -1
- package/src/.env.prod +2 -1
- package/src/config.js +4 -0
- package/src/edgeFirebase.js +117 -0
- package/src/postinstall.sh +19 -0
- package/src/storage.rules +19 -0
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
package/src/.env.dev
CHANGED
package/src/.env.prod
CHANGED
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
|
|
package/src/edgeFirebase.js
CHANGED
|
@@ -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()
|
package/src/postinstall.sh
CHANGED
|
@@ -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
|