From 67cd8446d5e8610a08b1dcff3b97dbe64270a859 Mon Sep 17 00:00:00 2001 From: Luke C Hartman Date: Wed, 16 Oct 2024 05:12:24 -0700 Subject: [PATCH] eslint format firebase functions & fix slack webhook URL secret --- functions/index.js | 477 +++++++++++++++++++----------------- functions/package-lock.json | 381 ++++++++++++++++------------ functions/package.json | 20 +- 3 files changed, 488 insertions(+), 390 deletions(-) diff --git a/functions/index.js b/functions/index.js index 657fd11..b2fa163 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,211 +1,209 @@ -require('dotenv').config() -const functions = require('firebase-functions') -const admin = require('firebase-admin') +require("dotenv").config(); +const functions = require("firebase-functions"); +const admin = require("firebase-admin"); +const {defineSecret} = require("firebase-functions/params"); // All available logging functions -const { - log, - info, - debug, - warn, - error, - write, -} = require("firebase-functions/logger"); +const {log} = require("firebase-functions/logger"); // The Firebase Admin SDK to access Firestore. const {initializeApp} = require("firebase-admin/app"); initializeApp(); -const axios = require('axios'); // used for sending slack messages from help requests form +const axios = require("axios"); -// Slack Webhook URL from environment variable -const SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/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" +// Define the Slack Webhook URL secret +const slackWebhook = defineSecret("SLACK_WEBHOOK_URL"); // Function to post a message to Slack const postToSlack = async ( - userID, - userName, - userEmail, - userRole, - subject, - messageText, - imageUrl, + userID, + userName, + userEmail, + userRole, + subject, + messageText, + imageUrl, + slackHook, ) => { try { - const payload = { - blocks: [ - { - type: 'section', - text: { - type: 'mrkdwn', - text: `*UserName*: ${userName}, *UserRole*: ${userRole}, *Email*: ${userEmail} - *Subject*: ${subject}`, - }, - }, - { - type: 'section', - block_id: 'section_message', - text: { - type: 'mrkdwn', - text: `Message: ${messageText}`, - }, - accessory: { - type: 'image', - image_url: imageUrl, - alt_text: 'User uploaded image', - }, - }, + const payload = { + blocks: [ { - type: "divider" + type: "section", + text: { + type: "mrkdwn", + text: `*UserName*: ${ userName }, + *UserRole*: ${ userRole }, + *Email*: ${ userEmail } + - *Subject*: ${ subject }`, + }, }, - { - type: 'section', - block_id: 'section_userID', - text: { - type: 'mrkdwn', - text: `UserID: ${userID}`, - } - }, - ], - } - await axios.post(SLACK_WEBHOOK_URL, payload) - } catch (error) { - console.error('Error posting message to Slack:', error.response?.data || error.message) - } -} + { + type: "section", + block_id: "section_message", + text: { + type: "mrkdwn", + text: `Message: ${messageText}`, + }, + accessory: { + type: "image", + image_url: imageUrl, + alt_text: "User uploaded image", + }, + }, + { + type: "divider", + }, + { + type: "section", + block_id: "section_userID", + text: { + type: "mrkdwn", + text: `UserID: ${userID}`, + }, + }, + ], + }; + await axios.post(slackHook, payload); + } catch (error) { + console.error( + "Error posting message to Slack:", + error.response?.data || error.message); + } +}; exports.notifySlackOnNewHelpRequest = functions.firestore - .document('helpRequests/{requestId}') - .onCreate(async (snap) => { - const newRequest = snap.data() - const userID = newRequest.userID || 'unknown user' - let userName = 'Unknown' - let userEmail = 'No email provided' - let userRole = 'No role specified' - - if (userID !== 'unknown user') { - const userRef = admin.firestore().collection('mobileUsers').doc(userID) - const doc = await userRef.get() - if (doc.exists) { - const userData = doc.data() - userName = userData.name || userName - userEmail = userData.email || userEmail - userRole = userData.userRole || userRole - } else { - console.log('User not found') - return // Optionally exit if no user info is available - } - } + .document("helpRequests/{requestId}") + .onCreate( async (snap) => { + const newRequest = snap.data(); + const userID = newRequest.userID || "unknown user"; + let userName = "Unknown"; + let userEmail = "No email provided"; + let userRole = "No role specified"; + + // Fetch user data if the userID is valid + if (userID !== "unknown user") { + const userRef = admin.firestore().collection("mobileUsers").doc(userID); + const doc = await userRef.get(); + if (doc.exists) { + const userData = doc.data(); + userName = userData.name || userName; + userEmail = userData.email || userEmail; + userRole = userData.userRole || userRole; + } else { + console.log("User not found"); + return; // Optionally exit if no user info is available + } + } - const subject = newRequest.subject || 'No Subject' - const messageText = newRequest.message || 'No message provided' - const imageUrl = - newRequest.images && newRequest.images.length > 0 - ? newRequest.images[0] - : 'https://placehold.co/600x400' - - // Post to Slack - return postToSlack( - userID, - userName, - userEmail, - userRole, - subject, - messageText, - imageUrl, - ) - } -) - - -exports.addUserRole = functions.https.onCall((data,context) => { + // Prepare message for Slack + const subject = newRequest.subject || "No Subject"; + const messageText = newRequest.message || "No message provided"; + const imageUrl = + newRequest.images && newRequest.images.length > 0 ? + newRequest.images[0] : + "https://placehold.co/600x400"; + + // Access the secret and use it in the function + const slackHook = slackWebhook.value(); // Fetch the secret value + log(slackHook); + // Post the message to Slack using the Secret Webhook URL + await postToSlack( + userID, + userName, + userEmail, + userRole, + subject, + messageText, + imageUrl, + slackHook, + ); + }); + + +exports.addUserRole = functions.https.onCall((data, context) => { // get user and add custom claim to user - return admin.auth().getUserByEmail(data.email).then(user => { - + return admin.auth().getUserByEmail(data.email).then((user) => { // Once user object is retrieved, updates custom claim - return admin.auth().setCustomUserClaims(user.uid,{}) + return admin.auth().setCustomUserClaims(user.uid, {}); // callback for frontend if success }).then(() => { return { - message: "Success! ${data.email} has been reset to user privileges." - } + message: "Success! ${data.email} has been reset to user privileges.", + }; // callback if there is an error - }).catch(err => { - return err - }) - -}) + }).catch((err) => { + return err; + }); +}); // Adds admin privilege to user based on the email provided -exports.addAdminRole = functions.https.onCall((data,context) => { +exports.addAdminRole = functions.https.onCall((data, context) => { // get user and add custom claim to user - return admin.auth().getUserByEmail(data.email).then(user => { - + return admin.auth().getUserByEmail(data.email).then((user) => { // Once user object is retrieved, updates custom claim - return admin.auth().setCustomUserClaims(user.uid,{ admin: true }) + return admin.auth().setCustomUserClaims(user.uid, {admin: true}); // callback for frontend if success }).then(() => { return { - message: "Success! ${data.email} has been made an admin" - } + message: "Success! ${data.email} has been made an admin", + }; // callback if there is an error - }).catch(err => { - return err - }) - -}) + }).catch((err) => { + return err; + }); +}); // Adds agency privilege to user based on email provided exports.addAgencyRole = functions.https.onCall( - (data,context) => { - // get user and add custom claim to user + (data, context) => { + // get user and add custom claim to user - return admin.auth().getUserByEmail(data.email).then((user) => { + return admin.auth().getUserByEmail(data.email).then((user) => { + // Once user object is retrieved, updates custom claim + return admin.auth().setCustomUserClaims(user.uid, {agency: true}); - // Once user object is retrieved, updates custom claim - return admin.auth().setCustomUserClaims(user.uid,{ agency: true }) + // callback for frontend if success + }).then((result) => { + console.log(result.data.message); // Display the message in the console + // return { + // message: "Success! ${data.email} has been made an agency admin" + // } - // callback for frontend if success - }).then((result) => { - console.log(result.data.message); // Display the message in the console - // return { - // message: "Success! ${data.email} has been made an agency admin" - // } + // callback if there is an error + }).catch((err) => { + return err; + }); + }); - // callback if there is an error - }).catch(err => { - return err - }) - -}) - -exports.viewRole = functions.https.onCall((data,context) => { +exports.viewRole = functions.https.onCall((data, context) => { // get user and add custom claim to user return admin - .auth() - .verifyIdToken(data.id) - .then((decodedToken) => { - - const claims = decodedToken.customClaims - console.log(claims) - if (claims['admin']) { - return { admin: true } - } else if (claims['agency']) { - return { agency: true } - } else { - return { - admin: false, - agency: false + .auth() + .verifyIdToken(data.id) + .then((decodedToken) => { + const claims = decodedToken.customClaims; + console.log(claims); + if (claims["admin"]) { + return {admin: true}; + } else if (claims["agency"]) { + return {agency: true}; + } else { + return { + admin: false, + agency: false, + }; } - } - }) - .catch((error) => { - console.log(error.code,error.message) - }) -}) + }) + .catch((error) => { + console.log(error.code, error.message); + }); +}); // get another user data by uid exports.getUser = functions.https.onCall(async (data, context) => { @@ -225,16 +223,16 @@ exports.getUser = functions.https.onCall(async (data, context) => { const userData = { displayName: userRecord.displayName, email: userRecord.email, - uid: userRecord.uid + uid: userRecord.uid, // Add other fields as needed }; return userData; } catch (error) { - console.error('Error fetching user data:', error); + console.error("Error fetching user data:", error); // If user does not exist - if (error.code === 'auth/user-not-found') { + if (error.code === "auth/user-not-found") { return {}; } @@ -252,96 +250,111 @@ exports.getUserByEmail = functions.https.onCall(async (data, context) => { // } const email = data.email; // Extract email from data object const userRecord = await admin.auth().getUserByEmail(email); - console.log('User Record:',userRecord); - return userRecord - + console.log("User Record:", userRecord); + return userRecord; } catch (error) { - console.error('Error fetching user data:', error); + console.error("Error fetching user data:", error); // If user does not exist - if (error.code === 'auth/user-not-found') { + if (error.code === "auth/user-not-found") { return {}; } // Throw the error for other cases throw error; } -}) +}); // get another user data by uid exports.deleteUser = functions.https.onCall(async (data, context) => { - try { - // Check if the request is authorized (if needed) - // if (!context.auth) { - // return { error: 'Unauthorized' }; - // } - - // Get the UID from the request data - const uid = data.uid - - if (!uid) { - console.log("User data not found") - return { success: false, message: "User data not found" } - } else { - // Perform user deletion operation - await admin.auth().deleteUser(uid) - - console.log("User deleted successfully on server side") - } - - return { success: true, message: "User deleted successfully", userRecord } - } catch (error) { - console.error("Error deleting user:", error) - return { success: false, message: "Error deleting user", error } - } -}) + try { + // Check if the request is authorized (if needed) + // if (!context.auth) { + // return { error: 'Unauthorized' }; + // } + + // Get the UID from the request data + const uid = data.uid; + + if (!uid) { + console.log("User data not found"); + return {success: false, message: "User data not found"}; + } else { + // Perform user deletion operation + await admin.auth().deleteUser(uid); + + console.log("User deleted successfully on server side"); + } + + return {success: true, message: "User deleted successfully", uid}; + } catch (error) { + console.error("Error deleting user:", error); + return {success: false, message: "Error deleting user", error}; + } +}); exports.disableUser = functions.https.onCall((data, context) => { // Ensure the user is authenticated if (!context.auth) { - throw new functions.https.HttpsError('unauthenticated', 'The user must be authenticated to disable their account.'); + throw new functions.https.HttpsError( + "unauthenticated", + "The user must be authenticated to disable their account.", + ); } - const { uid } = data; + const {uid} = data; const callerUid = context.auth.uid; // Check if the request is to disable their own account if (uid !== callerUid) { - throw new functions.https.HttpsError('permission-denied', 'Users can only disable their own accounts.'); + throw new functions.https.HttpsError( + "permission-denied", + "Users can only disable their own accounts.", + ); } return admin.auth().updateUser(uid, { - disabled: true // Set the disabled property to true - }) - .then(() => { - return { message: `Success! User ${uid} has been disabled.` }; + disabled: true, // Set the disabled property to true }) - .catch((error) => { - console.error('Error disabling user:', error); - throw new functions.https.HttpsError('internal', `Error disabling user: ${error.message}`); - }); + .then(() => { + return {message: `Success! User ${uid} has been disabled.`}; + }) + .catch((error) => { + console.error("Error disabling user:", error); + throw new functions.https.HttpsError( + "internal", + `Error disabling user: ${error.message}`, + ); + }); }); exports.getUserRecord = functions.https.onCall(async (data, context) => { - try { - const userRecord = await admin.auth().getUser(data.uid) - console.log('User Record:', userRecord) // Check the output in Firebase logs - return userRecord - } catch (error) { - console.error('Failed to fetch user record:', error) - throw new functions.https.HttpsError('not-found', 'User record not found', error.message); - } -}) - -exports.authGetUserList = functions.https.onCall(async (data,context) => { - // Ensure that the user is authenticated and optionally check if they have the required role/admin privileges + try { + const userRecord = await admin.auth().getUser(data.uid); + console.log("User Record:", userRecord); + return userRecord; + } catch (error) { + console.error("Failed to fetch user record:", error); + throw new functions.https.HttpsError( + "not-found", + "User record not found", + error.message, + ); + } +}); + +exports.authGetUserList = functions.https.onCall(async (data, context) => { + // Ensure that the user is authenticated if (!context.auth) { - throw new functions.https.HttpsError('unauthenticated', 'Request not authenticated.'); + throw new functions.https.HttpsError( + "unauthenticated", + "Request not authenticated.", + ); } - const maxResults = 1000; // You can adjust this value up to a maximum of 1000 + const maxResults = 1000; // maximum of 1000 try { - const listUsersResult = await admin.auth().listUsers(maxResults) + const listUsersResult = await admin.auth().listUsers(maxResults); const users = listUsersResult.users.map((userRecord) => ({ uid: userRecord.uid, email: userRecord.email, @@ -350,16 +363,20 @@ exports.authGetUserList = functions.https.onCall(async (data,context) => { // photoURL: userRecord.photoURL, // emailVerified: userRecord.emailVerified, disabled: userRecord.disabled, - // providerData: userRecord.providerData, // This includes provider-specific identifiers + // providerData: userRecord.providerData, // customClaims: userRecord.customClaims, metadata: { creationTime: userRecord.metadata.creationTime, - lastSignInTime: userRecord.metadata.lastSignInTime - } - })) - return {users} + lastSignInTime: userRecord.metadata.lastSignInTime, + }, + })); + return {users}; } catch (error) { - console.error('Failed to fetch user list: ',error) - throw new functions.https.HttpsError('unknown', 'Failed to fetch user list.', error) + console.error("Failed to fetch user list: ", error); + throw new functions.https.HttpsError( + "unknown", + "Failed to fetch user list.", + error, + ); } -}) +}); diff --git a/functions/package-lock.json b/functions/package-lock.json index 23e2614..39c5665 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -6,14 +6,21 @@ "": { "name": "functions", "dependencies": { + "@google-cloud/firestore": "^7.9.0", + "@google-cloud/storage": "^7.0.1", + "@sendgrid/mail": "^8.1.3", + "axios": "^1.7.3", + "child-process-promise": "^2.2.1", "dotenv": "^16.4.5", - "firebase-admin": "^12.1.0", - "firebase-functions": "^4.3.0" + "firebase-admin": "^12.4.0", + "firebase-functions": "^5.1.0", + "imagemagick": "^0.1.3", + "mkdirp": "^3.0.1" }, "devDependencies": { - "eslint": "^8.15.0", - "eslint-config-google": "^0.14.0", - "firebase-functions-test": "^3.1.0" + "@types/child-process-promise": "^2.2.2", + "eslint": "^8.57.1", + "eslint-config-google": "^0.14.0" }, "engines": { "node": "20" @@ -175,7 +182,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.10.0.tgz", "integrity": "sha512-VFNhdHvfnmqcHHs6YhmSNHHxQqaaD64GwiL0c+e1qz85S8SWZPC2XFRf8p9yHRTF40Kow424s1KBU9f0fdQa+Q==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@opentelemetry/api": "^1.3.0", "fast-deep-equal": "^3.1.1", @@ -192,7 +198,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", "license": "Apache-2.0", - "optional": true, "dependencies": { "arrify": "^2.0.0", "extend": "^3.0.2" @@ -206,7 +211,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=14.0.0" } @@ -216,7 +220,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=14" } @@ -226,7 +229,6 @@ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.13.0.tgz", "integrity": "sha512-Y0rYdwM5ZPW3jw/T26sMxxfPrVQTKm9vGrZG8PRyGuUmUJ8a2xNuQ9W/NNA1prxqv2i54DSydV8SJqxF2oCVgA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@google-cloud/paginator": "^5.0.0", "@google-cloud/projectify": "^4.0.0", @@ -253,7 +255,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "license": "MIT", - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -263,7 +264,6 @@ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.2.tgz", "integrity": "sha512-bgxdZmgTrJZX50OjyVwz3+mNEnCTNkh3cIqGPWVNeW9jX6bn1ZkU80uPd+67/ZpIJIjRQ9qaHCjhavyoWYxumg==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" @@ -277,7 +277,6 @@ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", "license": "Apache-2.0", - "optional": true, "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", @@ -334,7 +333,6 @@ "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", "license": "MIT", - "optional": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/js-sdsl" @@ -383,7 +381,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8.0.0" } @@ -452,12 +449,49 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, + "node_modules/@sendgrid/client": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.3.tgz", + "integrity": "sha512-mRwTticRZIdUTsnyzvlK6dMu3jni9ci9J+dW/6fMMFpGRAJdCJlivFVYQvqk8kRS3RnFzS7sf6BSmhLl1ldDhA==", + "license": "MIT", + "dependencies": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.6.8" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", + "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", + "license": "MIT", + "dependencies": { + "@sendgrid/client": "^8.1.3", + "@sendgrid/helpers": "^8.0.0" + }, + "engines": { + "node": ">=12.*" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "license": "MIT", - "optional": true, "engines": { "node": ">= 10" } @@ -476,8 +510,17 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "license": "MIT" + }, + "node_modules/@types/child-process-promise": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@types/child-process-promise/-/child-process-promise-2.2.6.tgz", + "integrity": "sha512-g0pOHijr6Trug43D2bV0PLSIsSHa/xHEES2HeX5BAlduq1vW0nZcq27Zeud5lgmNB+kPYYVqiMap32EHGTco/w==", + "dev": true, "license": "MIT", - "optional": true + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/connect": { "version": "3.4.38", @@ -535,19 +578,11 @@ "@types/node": "*" } }, - "node_modules/@types/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.5", @@ -581,7 +616,6 @@ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", "license": "MIT", - "optional": true, "dependencies": { "@types/caseless": "*", "@types/node": "*", @@ -614,8 +648,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", @@ -629,7 +662,6 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", - "optional": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -678,7 +710,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "license": "MIT", - "optional": true, "dependencies": { "debug": "^4.3.4" }, @@ -707,7 +738,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -717,7 +747,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "devOptional": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -747,7 +776,6 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "license": "MIT", - "optional": true, "engines": { "node": ">=8" } @@ -757,7 +785,6 @@ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "license": "MIT", - "optional": true, "dependencies": { "retry": "0.13.1" } @@ -766,8 +793,32 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", - "optional": true + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/balanced-match": { "version": "1.0.2", @@ -794,15 +845,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/bignumber.js": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "license": "MIT", - "optional": true, "engines": { "node": "*" } @@ -918,12 +967,60 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/child-process-promise": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/child-process-promise/-/child-process-promise-2.2.1.tgz", + "integrity": "sha512-Fi4aNdqBsr0mv+jgWxcZ/7rAIC2mgihrptyVI4foh/rrjY/3BNjfP9+oaiFx/fzim+1ZyCNBae0DlyfQhSugog==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^4.0.2", + "node-version": "^1.0.0", + "promise-polyfill": "^6.0.1" + } + }, + "node_modules/child-process-promise/node_modules/cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==", + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "node_modules/child-process-promise/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/child-process-promise/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/child-process-promise/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "license": "ISC" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", - "optional": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -937,7 +1034,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -950,7 +1046,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -958,7 +1053,6 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", - "optional": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1061,6 +1155,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -1083,7 +1186,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", - "optional": true, "engines": { "node": ">=0.4.0" } @@ -1137,7 +1239,6 @@ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", "license": "MIT", - "optional": true, "dependencies": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", @@ -1164,8 +1265,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", @@ -1181,7 +1281,6 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "license": "MIT", - "optional": true, "dependencies": { "once": "^1.4.0" } @@ -1212,7 +1311,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "optional": true, "engines": { "node": ">=6" } @@ -1414,7 +1512,6 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", - "optional": true, "engines": { "node": ">=6" } @@ -1480,8 +1577,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/farmhash-modern": { "version": "1.1.0", @@ -1496,7 +1592,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "devOptional": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -1528,7 +1623,6 @@ } ], "license": "MIT", - "optional": true, "dependencies": { "strnum": "^1.0.5" }, @@ -1646,9 +1740,9 @@ } }, "node_modules/firebase-functions": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.9.0.tgz", - "integrity": "sha512-IqxOEsVAWGcRv9KRGzWQR5mOFuNsil3vsfkRPPiaV1U/ATC27/jbahh4z8I4rW8Xqa6cQE5xqnw0ueyMH7i7Ag==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-5.1.1.tgz", + "integrity": "sha512-KkyKZE98Leg/C73oRyuUYox04PQeeBThdygMfeX+7t1cmKWYKa/ZieYa89U8GHgED+0mF7m7wfNZOfbURYxIKg==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.5", @@ -1664,27 +1758,7 @@ "node": ">=14.10.0" }, "peerDependencies": { - "firebase-admin": "^10.0.0 || ^11.0.0 || ^12.0.0" - } - }, - "node_modules/firebase-functions-test": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/firebase-functions-test/-/firebase-functions-test-3.3.0.tgz", - "integrity": "sha512-X+OOA34MGrsTimFXTDnWT0psAqnmBkJ85bGCoLMwjgei5Prfkqh3bv5QASnXC/cmIVBSF2Qw9uW1+mF/t3kFlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/lodash": "^4.14.104", - "lodash": "^4.17.5", - "ts-deepmerge": "^2.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "firebase-admin": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "firebase-functions": ">=4.9.0", - "jest": ">=28.0.0" + "firebase-admin": "^11.10.0 || ^12.0.0" } }, "node_modules/flat-cache": { @@ -1709,12 +1783,31 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/form-data": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", "license": "MIT", - "optional": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -1763,15 +1856,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/gaxios": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "license": "Apache-2.0", - "optional": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", @@ -1792,7 +1883,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -1802,7 +1892,6 @@ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", "license": "Apache-2.0", - "optional": true, "dependencies": { "gaxios": "^6.0.0", "json-bigint": "^1.0.0" @@ -1816,7 +1905,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "optional": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -1896,7 +1984,6 @@ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.14.2.tgz", "integrity": "sha512-R+FRIfk1GBo3RdlRYWPdwk8nmtVUOn6+BkDomAC46KoU8kzXzE1HLmOasSCbWUByMMAGkknVF0G5kQ69Vj7dlA==", "license": "Apache-2.0", - "optional": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", @@ -1914,7 +2001,6 @@ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", "license": "Apache-2.0", - "optional": true, "dependencies": { "@grpc/grpc-js": "^1.10.9", "@grpc/proto-loader": "^0.7.13", @@ -1942,7 +2028,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -1971,7 +2056,6 @@ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "license": "MIT", - "optional": true, "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" @@ -2052,8 +2136,7 @@ "url": "https://patreon.com/mdevils" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.0", @@ -2082,7 +2165,6 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "license": "MIT", - "optional": true, "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -2097,7 +2179,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", - "optional": true, "dependencies": { "debug": "4" }, @@ -2110,7 +2191,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "license": "MIT", - "optional": true, "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -2141,6 +2221,11 @@ "node": ">= 4" } }, + "node_modules/imagemagick": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/imagemagick/-/imagemagick-0.1.3.tgz", + "integrity": "sha512-HIwwW10UdwsWcHETt5BPrnEkaJSFLiW1dYDlPYV0EXyj2zJY28iXimPWlZDsOpxVghmF5EuUjYOvJZhZS4Y10g==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2210,7 +2295,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "optional": true, "engines": { "node": ">=8" } @@ -2243,7 +2327,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", - "optional": true, "engines": { "node": ">=8" }, @@ -2255,7 +2338,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/jose": { @@ -2285,7 +2367,6 @@ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", - "optional": true, "dependencies": { "bignumber.js": "^9.0.0" } @@ -2359,7 +2440,6 @@ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "license": "MIT", - "optional": true, "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -2412,7 +2492,6 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "license": "MIT", - "optional": true, "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -2463,19 +2542,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", @@ -2592,7 +2663,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "license": "MIT", - "optional": true, "bin": { "mime": "cli.js" }, @@ -2634,6 +2704,21 @@ "node": "*" } }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2661,7 +2746,6 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", - "optional": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2686,6 +2770,15 @@ "node": ">= 6.13.0" } }, + "node_modules/node-version": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", + "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2700,7 +2793,6 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", - "optional": true, "engines": { "node": ">= 6" } @@ -2733,7 +2825,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -2761,7 +2852,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "devOptional": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" @@ -2857,12 +2947,17 @@ "node": ">= 0.8.0" } }, + "node_modules/promise-polyfill": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", + "integrity": "sha512-g0LWaH0gFsxovsU7R5LrrhHhWAWiHRnh1GPrhXnPgYsDkIqjRYUYSZEsej/wtleDrz5xVSIDbeKfidztp2XHFQ==", + "license": "MIT" + }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", "license": "Apache-2.0", - "optional": true, "dependencies": { "protobufjs": "^7.2.5" }, @@ -2907,6 +3002,18 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "license": "ISC" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2982,7 +3089,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2997,7 +3103,6 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", - "optional": true, "engines": { "node": ">=0.10.0" } @@ -3017,7 +3122,6 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", - "optional": true, "engines": { "node": ">= 4" } @@ -3027,7 +3131,6 @@ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", "license": "MIT", - "optional": true, "dependencies": { "@types/request": "^2.48.8", "extend": "^3.0.2", @@ -3280,7 +3383,6 @@ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", "license": "MIT", - "optional": true, "dependencies": { "stubs": "^3.0.0" } @@ -3289,15 +3391,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -3307,7 +3407,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", - "optional": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3321,7 +3420,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -3347,15 +3445,13 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", @@ -3375,7 +3471,6 @@ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", "license": "Apache-2.0", - "optional": true, "dependencies": { "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", @@ -3392,7 +3487,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "license": "MIT", - "optional": true, "dependencies": { "debug": "4" }, @@ -3405,7 +3499,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", - "optional": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -3423,7 +3516,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "optional": true, "bin": { "uuid": "dist/bin/uuid" } @@ -3448,15 +3540,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "optional": true - }, - "node_modules/ts-deepmerge": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-2.0.7.tgz", - "integrity": "sha512-3phiGcxPSSR47RBubQxPoZ+pqXsEsozLo4G4AlSrsMKTFg9TA3l+3he5BqpUi9wiuDbaHWXH/amlzQ49uEdXtg==", - "dev": true, - "license": "ISC" + "license": "MIT" }, "node_modules/tslib": { "version": "2.8.0", @@ -3532,8 +3616,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -3570,8 +3653,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "optional": true + "license": "BSD-2-Clause" }, "node_modules/websocket-driver": { "version": "0.7.4", @@ -3601,7 +3683,6 @@ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", - "optional": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -3638,7 +3719,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "optional": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3655,7 +3735,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true, "license": "ISC" }, "node_modules/y18n": { @@ -3663,7 +3742,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "optional": true, "engines": { "node": ">=10" } @@ -3679,7 +3757,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", - "optional": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -3698,7 +3775,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", - "optional": true, "engines": { "node": ">=12" } @@ -3707,7 +3783,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "devOptional": true, "license": "MIT", "engines": { "node": ">=10" diff --git a/functions/package.json b/functions/package.json index 36187eb..addff5f 100644 --- a/functions/package.json +++ b/functions/package.json @@ -2,7 +2,7 @@ "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { - "lint": "eslint .", + "lint": "eslint . --ext .js,.ts", "serve": "firebase emulators:start --only functions", "shell": "firebase functions:shell", "start": "npm run shell", @@ -16,17 +16,23 @@ "dependencies": { "@google-cloud/firestore": "^7.9.0", "@google-cloud/storage": "^7.0.1", + "@sendgrid/mail": "^8.1.3", "axios": "^1.7.3", + "child-process-promise": "^2.2.1", "dotenv": "^16.4.5", - "firebase-admin": "^12.1.0", - "firebase-functions": "^4.3.0", + "firebase-admin": "^12.4.0", + "firebase-functions": "^5.1.0", "imagemagick": "^0.1.3", "mkdirp": "^3.0.1" }, "devDependencies": { - "eslint": "^8.15.0", - "eslint-config-google": "^0.14.0", - "firebase-functions-test": "^3.1.0" + "@types/child-process-promise": "^2.2.2", + "eslint": "^8.57.1", + "eslint-config-google": "^0.14.0" }, - "private": true + "private": true, + "browser": { + "fs": false, + "child_process": false + } }