Firebase - Error: There was an error deploying functions - node.js

Ok, y'all I am in a pickle here. I've been working on all my firebase functions for a few days and thought I was making really good progress until I went to deploy after several days of testing with the firebase server and postman... It won't deploy T_T
The error I am getting in the terminal when I use firebase --debug deploy is:
2022-01-25T17:23:50.575Z] Total Function Deployment time: 57866
[2022-01-25T17:23:50.575Z] 1 Functions Deployed
[2022-01-25T17:23:50.575Z] 1 Functions Errored
[2022-01-25T17:23:50.575Z] 0 Function Deployments Aborted
[2022-01-25T17:23:50.575Z] Average Function Deployment time: 57864
Functions deploy had errors with the following functions:
api(us-central1)
[2022-01-25T17:23:50.713Z] Missing URI for HTTPS function in printTriggerUrls. This shouldn't happen
in functions: cleaning up build files...
[2022-01-25T17:23:50.719Z] >>> [apiv2][query] DELETE https://artifactregistry.googleapis.com/v1beta2/projects/lxai-mentor-matching/locations/us-central1/repositories/gcf-artifacts/packages/api [none]
[2022-01-25T17:23:50.722Z] >>> [apiv2][query] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/tags/list [none]
[2022-01-25T17:23:50.918Z] <<< [apiv2][status] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/tags/list 200
[2022-01-25T17:23:50.918Z] <<< [apiv2][body] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/tags/list {"child":["5fbdb989-6e6d-42bc-9a25-3073c3098d76"],"manifest":{},"name":"lxai-mentor-matching/gcf/us-central1","tags":[]}
[2022-01-25T17:23:50.918Z] >>> [apiv2][query] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/tags/list [none]
[2022-01-25T17:23:50.931Z] <<< [apiv2][status] DELETE https://artifactregistry.googleapis.com/v1beta2/projects/lxai-mentor-matching/locations/us-central1/repositories/gcf-artifacts/packages/api 404
[2022-01-25T17:23:50.933Z] <<< [apiv2][body] DELETE https://artifactregistry.googleapis.com/v1beta2/projects/lxai-mentor-matching/locations/us-central1/repositories/gcf-artifacts/packages/api {"error":{"code":404,"message":"Repository does not exist: \"projects/lxai-mentor-matching/locations/us-central1/repositories/gcf-artifacts\"","status":"NOT_FOUND"}}
[2022-01-25T17:23:51.061Z] <<< [apiv2][status] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/tags/list 200
[2022-01-25T17:23:51.063Z] <<< [apiv2][body] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/tags/list {"child":["cache"],"manifest":{"sha256:efea96d0dec08db58767f472cc16a3c17c277e41d844a61bbdb745e49c7cd8e6":{"imageSizeBytes":"421918451","layerId":"","mediaType":"application/vnd.docker.distribution.manifest.v2+json","tag":["api_version-15","latest"],"timeCreatedMs":"315532801000","timeUploadedMs":"1643131418106"}},"name":"lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76","tags":["api_version-15","latest"]}
[2022-01-25T17:23:51.064Z] >>> [apiv2][query] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/tags/list [none]
[2022-01-25T17:23:51.069Z] >>> [apiv2][query] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/api_version-15 [none]
[2022-01-25T17:23:51.230Z] <<< [apiv2][status] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/latest 202
[2022-01-25T17:23:51.231Z] <<< [apiv2][body] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/latest {"errors":[]}
[2022-01-25T17:23:51.250Z] <<< [apiv2][status] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/tags/list 200
[2022-01-25T17:23:51.250Z] <<< [apiv2][body] GET https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/tags/list {"child":[],"manifest":{"sha256:75f8940b6524c90f2b653b387e0cab4a9ae9f3dbebb2e5f4dfb03fc3345ead6f":{"imageSizeBytes":"13412478","layerId":"","mediaType":"application/vnd.docker.distribution.manifest.v2+json","tag":["latest"],"timeCreatedMs":"315532801000","timeUploadedMs":"1643131418618"}},"name":"lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache","tags":["latest"]}
[2022-01-25T17:23:51.251Z] >>> [apiv2][query] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/latest [none]
[2022-01-25T17:23:51.256Z] <<< [apiv2][status] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/api_version-15 202
[2022-01-25T17:23:51.256Z] <<< [apiv2][body] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/api_version-15 {"errors":[]}
[2022-01-25T17:23:51.257Z] >>> [apiv2][query] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/sha256:efea96d0dec08db58767f472cc16a3c17c277e41d844a61bbdb745e49c7cd8e6 [none]
[2022-01-25T17:23:51.386Z] <<< [apiv2][status] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/latest 202
[2022-01-25T17:23:51.386Z] <<< [apiv2][body] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/latest {"errors":[]}
[2022-01-25T17:23:51.387Z] >>> [apiv2][query] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/sha256:75f8940b6524c90f2b653b387e0cab4a9ae9f3dbebb2e5f4dfb03fc3345ead6f [none]
[2022-01-25T17:23:51.683Z] <<< [apiv2][status] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/sha256:efea96d0dec08db58767f472cc16a3c17c277e41d844a61bbdb745e49c7cd8e6 202
[2022-01-25T17:23:51.683Z] <<< [apiv2][body] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/manifests/sha256:efea96d0dec08db58767f472cc16a3c17c277e41d844a61bbdb745e49c7cd8e6 {"errors":[]}
[2022-01-25T17:23:51.728Z] <<< [apiv2][status] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/sha256:75f8940b6524c90f2b653b387e0cab4a9ae9f3dbebb2e5f4dfb03fc3345ead6f 202
[2022-01-25T17:23:51.729Z] <<< [apiv2][body] DELETE https://us.gcr.io/v2/lxai-mentor-matching/gcf/us-central1/5fbdb989-6e6d-42bc-9a25-3073c3098d76/cache/manifests/sha256:75f8940b6524c90f2b653b387e0cab4a9ae9f3dbebb2e5f4dfb03fc3345ead6f {"errors":[]}
[2022-01-25T17:23:51.852Z] Error: Failed to update function api in region us-central1
at /Users/tmac/.nvm/versions/node/v17.3.0/lib/node_modules/firebase-tools/lib/deploy/functions/release/fabricator.js:38:11
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Fabricator.updateV1Function (/Users/tmac/.nvm/versions/node/v17.3.0/lib/node_modules/firebase-tools/lib/deploy/functions/release/fabricator.js:255:32)
at async Fabricator.updateEndpoint (/Users/tmac/.nvm/versions/node/v17.3.0/lib/node_modules/firebase-tools/lib/deploy/functions/release/fabricator.js:136:13)
at async handle (/Users/tmac/.nvm/versions/node/v17.3.0/lib/node_modules/firebase-tools/lib/deploy/functions/release/fabricator.js:75:17)
Error: There was an error deploying functions
Having read a few other posts here I am super confused as to what the error is and where I can hunt it down. Everything has been working locally with firebase serve. When I check the log files I get this:
11:23:52.550 AM
api
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":
{"code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause: https://cloud.google.com/functions/docs/monitoring/logging#viewing_logs. Additional troubleshooting documentation can be found at https://cloud.google.com/functions/docs/troubleshooting#logging. Please visit https://cloud.google.com/functions/docs/troubleshooting for in-depth troubleshooting documentation."},"authenticationInfo":{"principalEmail":"timmcmackenjr#gmail.com"},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","resourceName":"projects/lxai-mentor-matching/locations/us-central1/functions/api"}
One post here was about space in a file name, but that's not my issue, another was about package.json so I'm posting mine here but not sure what I am looking for. Any idea on how to dig deeper or what to look for so I can get deploy working again?
package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"serve": "firebase emulators:start --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "16"
},
"main": "index.js",
"dependencies": {
"firebase-admin": "^9.8.0",
"firebase-functions": "^3.14.1"
},
"devDependencies": {
"firebase-functions-test": "^0.2.0"
},
"private": true
}
index.js
const functions = require('firebase-functions');
const app = require('express')();
const { postOneScream } = require('./handlers/screams');
const {
signup,
login,
uploadImage,
addUserDetails,
} = require('./handlers/users');
const { addMentee, getMentees, updateMentee } = require('./handlers/mentees');
const { addMentor, getMentors, updateMentor } = require('./handlers/mentors');
const { FBAuth } = require('./util/fbAuth');
//**POST ROUTES**
// Scream routes - Testing post functionality for social media feed posts
app.post('/screams', FBAuth, postOneScream);
// Signup route
app.post('/signup', signup);
// Sign In route
app.post('/login', login);
// Upload an image route
app.post('/users/image', FBAuth, uploadImage);
// Add details to user profile
app.post('/users', FBAuth, addUserDetails);
// Mentee Signup / Update
app.post('/mentees', FBAuth, addMentee);
app.post('/mentees', FBAuth, updateMentee);
// Mentor Signup
app.post('/mentors', FBAuth, addMentor);
app.post('/mentors', FBAuth, updateMentor);
//**GET ROUTES**
// Get all mentees
app.get('/mentees', getMentees);
// Get all mentors
app.get('/mentors', getMentors);
//**Export API**
// export api allows us to use express for our function formating
exports.api = functions.https.onRequest(app);
users.js
const { admin, db } = require('../util/admin');
const firebase = require('firebase');
const config = require('../util/config');
firebase.initializeApp(config);
const {
validateSignupData,
validateLoginData,
reduceUserDetails,
} = require('../util/validators');
exports.signup = async (req, res) => {
const newUser = {
email: req.body.email,
password: req.body.password,
confirmPassword: req.body.confirmPassword,
handle: req.body.handle,
};
// Validating the fields for user signup
const { valid, errors } = validateSignupData(newUser);
if (!valid) return res.status(400).json(errors);
const noImg = 'no-img.png';
const userDoc = await db.doc(`/users/${newUser.handle}`).get();
if (userDoc.exists) {
return res.status(400).json({ handle: 'This handle already taken' });
} else {
// Create user
let userId;
await firebase
.auth()
.createUserWithEmailAndPassword(newUser.email, newUser.password)
.then((data) => {
userId = data.user.uid;
return data.user.getIdToken();
})
.then((userToken) => {
// Add User to Users Collection
const uToken = userToken;
const userCredentials = {
handle: newUser.handle,
email: newUser.email,
createdAt: new Date().toISOString(),
imgURL: `https://firebasetorage.googleapis.com/v0/b/${config.storageBucket}/o/${noImg}?alt=media`,
userId,
};
db.doc(`/users/${newUser.handle}`).set(userCredentials);
return userToken;
})
.then((userToken) => {
return res
.status(201)
.json({ message: 'User created successfully', token: userToken });
})
.catch((err) => {
console.error(err);
if (err.code === 'auth/email-already-in-use') {
return res.status(400).json({ email: 'Email is already in use' });
} else {
return res.status(500).json({ error: err.code });
}
});
}
};
exports.login = (req, res) => {
const user = {
email: req.body.email,
password: req.body.password,
};
// Validating the fields for user login
const { valid, errors } = validateLoginData(user);
if (!valid) return res.status(400).json(errors);
// Log the user in and get a token
firebase
.auth()
.signInWithEmailAndPassword(user.email, user.password)
.then((data) => {
return data.user.getIdToken();
})
.then((token) => {
res.json({ token });
})
.catch((err) => {
console.error(err);
if (err.code === 'auth/wrong-password') {
return res.status(403).json({ general: 'Wrong credentials' });
} else {
return res.status(500).json({ error: err.code });
}
});
};
// Upload an image for user profile page
exports.uploadImage = (req, res) => {
const BusBoy = require('busboy');
const path = require('path');
const os = require('os');
const fs = require('fs');
const busboy = BusBoy({ headers: req.headers });
let imageFileName;
let imageToBeUploaded = {};
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
if (
mimetype !== 'image/jpeg' &&
mimetype !== 'image/png' &&
mimetype !== 'image/jpg'
) {
return res
.status(400)
.json({ error: 'Wrong file type, please use JPG/JPEG/PNG' });
}
const fileName = filename.filename + '';
const imageExtention = fileName.split('.')[fileName.split('.').length - 1];
// Not sure why we need to change file name but this is the tutorial recommendation
imageFileName = `${Math.round(
Math.random() * 10000000000,
)}.${imageExtention}`;
console.log(imageFileName);
const filePath = path.join(os.tmpdir(), imageFileName);
imageToBeUploaded = { filePath, mimetype };
file.pipe(fs.createWriteStream(filePath));
});
busboy.on('finish', () => {
admin
.storage()
.bucket()
.upload(imageToBeUploaded.filePath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype,
},
},
})
.then(() => {
const imgURL = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
return db.doc(`/users/${req.user.handle}`).update({ imgURL });
})
.then(() => {
return res.json({ message: 'Image uploaded successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
});
busboy.end(req.rawBody);
};
// Add user details to user collection in db / user profile in react
exports.addUserDetails = (req, res) => {
let userDetails = reduceUserDetails(req.body);
db.doc(`/users/${req.user.handle}`)
.update(userDetails)
.then(() => {
return res.status(200).json({ message: 'Details added successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
screams.js
const { db } = require('../util/admin');
exports.postOneScream = (req, res) => {
const newScream = {
body: req.body.body,
userHandle: req.user.handle,
createdAt: new Date().toISOString(),
};
db.collection('screams')
.add(newScream)
.then((doc) => {
res.json({ message: `document ${doc.id} created successfully` });
})
.catch((err) => {
res.status(500).json({ error: 'something went wrong' });
console.error(err);
});
};
mentees.js
const { db } = require('../util/admin');
const { reduceMenteeDetails } = require('../util/validators');
// Add a mentee to the mentees collection
exports.addMentee = (req, res) => {
let menteeDetails = reduceMenteeDetails(req);
db.doc(`/mentees/${req.user.handle}`).set(menteeDetails)
.then(() => {
return res.status(200).json({ message: 'Details added successfully' });
}).catch(err => {
console.error(err);
return res.status(500).json({ error: err.code });
})
}
// Get all Mentees from the mentee collection
exports.getMentees = (req, res) => {
db.collection('mentees').get()
.then((data) => {
let mentees = [];
data.forEach((doc) => {
mentees.push(doc.data())
});
return res.json(mentees);
})
.catch(err => {
console.error(err);
return res.status(500).json({ error: err.code })
})
}
// Update an existing mentee
exports.updateMentee = (req, res) => {
let menteeDetails = reduceMenteeDetails(req);
db.doc(`/mentees/${req.user.handle}`).update(menteeDetails)
.then(() => {
return res.status(200).json({ message: 'Details added successfully' });
}).catch(err => {
console.error(err);
return res.status(500).json({ error: err.code });
})
}
mentors.js
const { db } = require('../util/admin');
const { reduceMentorDetails } = require('../util/validators');
// Add a mentor to the mentors collection
exports.addMentor = (req, res) => {
let mentorDetails = reduceMentorDetails(req);
db.doc(`/mentors/${req.user.handle}`)
.set(mentorDetails)
.then(() => {
return res.status(200).json({ message: 'Details added successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
// Get all mentors from the mentor collection
exports.getMentors = (req, res) => {
db.collection('mentors')
.get()
.then((data) => {
let mentors = [];
data.forEach((doc) => {
mentors.push(doc.data());
});
return res.json(mentors);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
// Update an existing mentor
exports.updateMentor = (req, res) => {
let mentorDetails = reduceMentorDetails(req);
db.doc(`/mentors/${req.user.handle}`)
.update(mentorDetails)
.then(() => {
return res.status(200).json({ message: 'Details added successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
validators.js
// Helper functions for validation of signup form
// Check if a field is empty
const isEmpty = (string) => {
if (string.trim() === '') return true;
else return false;
};
// Check if an email is valid in format
const isEmail = (email) => {
const regEx =
/^(([^<>()[\]\.,;:\s#\"]+(\.[^<>()[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()[\]\.,;:\s#\"]+\.)+[^<>()[\]\.,;:\s#\"]{2,})$/i;
if (email.match(regEx)) return true;
else return false;
};
//Validate signup credential entered
exports.validateSignupData = (data) => {
let errors = {};
if (isEmpty(data.email)) {
errors.email = 'Must not be empty';
} else if (!isEmail(data.email)) {
errors.email = 'Must be a valid email';
}
if (isEmpty(data.password)) errors.password = 'Must not be empty';
if (data.password !== data.confirmPassword)
errors.confirmPassword = 'Passwords do not match';
if (isEmpty(data.handle)) errors.handle = 'Must not be empty';
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false,
};
};
// Validate Login Credentials entered
exports.validateLoginData = (data) => {
let errors = {};
if (isEmpty(data.email)) errors.email = 'Must not be empty';
if (isEmpty(data.password)) errors.password = 'Must not be empty';
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false,
};
};
// User Details Updater - Checks for non-answers/blanks and return dict
exports.reduceUserDetails = (data) => {
let userDetails = {};
if (!isEmpty(data.fullName.trim())) userDetails.fullName = data.fullName;
if (!isEmpty(data.jobTitle.trim())) userDetails.jobTitle = data.jobTitle;
if (!isEmpty(data.affiliation.trim()))
userDetails.affiliation = data.affiliation;
if (!isEmpty(data.homeLocation.trim()))
userDetails.homeLocation = data.homeLocation;
if (!isEmpty(data.isLatinX.trim())) userDetails.isLatinX = data.isLatinX;
if (!isEmpty(data.bio.trim())) userDetails.bio = data.bio;
if (!isEmpty(data.website.trim())) {
// If user doesn't include the http://
if (data.website.trim().substring(0, 4) !== 'http') {
userDetails.website = `http://${data.website.trim()}`;
} else userDetails.website = data.website;
}
if (!isEmpty(data.publicProfile.trim()))
userDetails.publicProfile = data.publicProfile;
return userDetails;
};
// Mentee Details Updater - Checks for non-answers/blanks and return dict
exports.reduceMenteeDetails = (data) => {
const answers = data.body;
let menteeDetails = {};
// Things we can pull from the data.user or just geneate
menteeDetails.createdAt = new Date().toISOString();
menteeDetails.userId = data.user.uid;
menteeDetails.email = data.user.email;
menteeDetails.handle = data.user.handle;
//Things we can pull from the user's info in the user collection
menteeDetails.fullName = answers.fullName;
menteeDetails.isLatinX = answers.isLatinX;
menteeDetails.currentLocation = answers.currentLocation;
// Questions we need to ask in the mentee sign up flow
if (!isEmpty(answers.gender.trim())) menteeDetails.gender = answers.gender;
if (!isEmpty(answers.countryOrigin.trim()))
menteeDetails.countryOrigin = answers.countryOrigin;
if (!isEmpty(answers.affiliation.trim()))
menteeDetails.affiliation = answers.affiliation;
if (!isEmpty(answers.position.trim()))
menteeDetails.position = answers.position;
if (!isEmpty(answers.scholarOrWebsite.trim())) {
// If user doesn't include the http://
if (answers.scholarOrWebsite.trim().substring(0, 4) !== 'http') {
menteeDetails.scholarOrWebsite = `http://${answers.scholarOrWebsite.trim()}`;
} else menteeDetails.scholarOrWebsite = answers.scholarOrWebsite;
}
if (answers.languages.length > 0) menteeDetails.languages = answers.languages;
if (!isEmpty(answers.timezone.trim()))
menteeDetails.timezone = answers.timezone;
if (answers.mentorshipArea.length > 0)
menteeDetails.mentorshipArea = answers.mentorshipArea;
if (!isEmpty(answers.motivationStatement.trim()))
menteeDetails.motivationStatement = answers.motivationStatement;
if (answers.prefferedOutcomes.length > 0)
menteeDetails.prefferedOutcomes = answers.prefferedOutcomes;
if (!isEmpty(answers.discussAfter.trim()))
menteeDetails.discussAfter = answers.discussAfter;
if (!isEmpty(answers.careerGoals.trim()))
menteeDetails.careerGoals = answers.careerGoals;
if (answers.skillMentorship.length > 0)
menteeDetails.skillMentorship = answers.skillMentorship;
if (answers.researchAreas.length > 0)
menteeDetails.researchAreas = answers.researchAreas;
if (answers.careerAdvice.length > 0)
menteeDetails.careerAdvice = answers.careerAdvice;
if (!isEmpty(answers.workshopReviewer))
menteeDetails.workshopReviewer = answers.workshopReviewer;
if (!isEmpty(answers.peerReviewedPubs.trim()))
menteeDetails.peerReviewedPubs = answers.peerReviewedPubs;
if (!isEmpty(answers.topTierReviewer.trim()))
menteeDetails.topTierReviewer = answers.topTierReviewer;
if (!isEmpty(answers.topTierPub.trim()))
menteeDetails.topTierPub = answers.topTierPub;
if (!isEmpty(answers.highImpactReviewer.trim()))
menteeDetails.highImpactReviewer = answers.highImpactReviewer;
if (answers.conferencePreference.length > 0)
menteeDetails.conferencePreference = answers.conferencePreference;
if (!isEmpty(answers.otherConferences.trim())) {
let conferences = answers.otherConferences.split(',');
menteeDetails.conferencePreference =
menteeDetails.conferencePreference.concat(conferences);
}
if (!isEmpty(answers.peerReviewedHighImpact))
menteeDetails.peerReviewedHighImpact = answers.peerReviewedHighImpact;
return menteeDetails;
};
// Mentee Details Updater - Checks for non-answers/blanks and return dict
exports.reduceMentorDetails = (data) => {
const answers = data.body;
let mentorDetails = {};
// Things we can pull from the data.user or just geneate
mentorDetails.createdAt = new Date().toISOString();
mentorDetails.userId = data.user.uid;
mentorDetails.email = data.user.email;
mentorDetails.handle = data.user.handle;
//Things we can pull from the user's info in the user collection
mentorDetails.fullName = answers.fullName;
mentorDetails.isLatinX = answers.isLatinX;
mentorDetails.currentLocation = answers.currentLocation;
// Questions we need to ask in the mentor sign up flow
if (!isEmpty(answers.gender.trim())) mentorDetails.gender = answers.gender;
if (!isEmpty(answers.countryOrigin.trim()))
mentorDetails.countryOrigin = answers.countryOrigin;
if (!isEmpty(answers.affiliation.trim()))
mentorDetails.affiliation = answers.affiliation;
if (!isEmpty(answers.position.trim()))
mentorDetails.position = answers.position;
if (!isEmpty(answers.scholarOrWebsite.trim())) {
// If user doesn't include the http://
if (answers.scholarOrWebsite.trim().substring(0, 4) !== 'http') {
mentorDetails.scholarOrWebsite = `http://${answers.scholarOrWebsite.trim()}`;
} else mentorDetails.scholarOrWebsite = answers.scholarOrWebsite;
}
if (answers.languages.length > 0) mentorDetails.languages = answers.languages;
if (!isEmpty(answers.timezone.trim()))
mentorDetails.timezone = answers.timezone;
if (!isEmpty(answers.previousMentor.trim()))
mentorDetails.previousMentor = answers.previousMentor;
if (answers.mentorshipArea.length > 0)
mentorDetails.mentorshipArea = answers.mentorshipArea;
if (!isEmpty(answers.hoursAvailable.trim()))
mentorDetails.hoursAvailable = answers.hoursAvailable;
if (answers.menteePref.length > 0)
mentorDetails.menteePref = answers.menteePref;
if (!isEmpty(answers.otherPref.trim())) {
let menteePrefs;
let newPref;
if (mentorDetails.menteePref.length !== 0)
menteePrefs = mentorDetails.menteePref;
else menteePrefs = [];
newPref = answers.otherPref.split(',');
menteePrefs = menteePrefs.concat(newPref);
mentorDetails.menteePref = menteePrefs;
}
if (answers.prefferedOutcomes.length > 0)
mentorDetails.prefferedOutcomes = answers.prefferedOutcomes;
if (!isEmpty(answers.otherOutcomes.trim())) {
let outcomes;
let newPref;
if (mentorDetails.prefferedOutcomes.length !== 0)
outcomes = mentorDetails.prefferedOutcomes;
else outcomes = [];
newPref = answers.otherPref.split(',');
outcomes = outcomes.concat(newPref);
mentorDetails.prefferedOutcomes = outcomes;
}
if (!isEmpty(answers.discussAfter.trim()))
mentorDetails.discussAfter = answers.discussAfter;
if (answers.skillMentorship.length > 0)
mentorDetails.skillMentorship = answers.skillMentorship;
if (answers.researchAreas.length > 0)
mentorDetails.researchAreas = answers.researchAreas;
if (answers.careerAdvice.length > 0)
mentorDetails.careerAdvice = answers.careerAdvice;
if (!isEmpty(answers.workshopReviewer))
mentorDetails.workshopReviewer = answers.workshopReviewer;
if (!isEmpty(answers.peerReviewedPubs.trim()))
mentorDetails.peerReviewedPubs = answers.peerReviewedPubs;
if (!isEmpty(answers.topTierReviewer.trim()))
mentorDetails.topTierReviewer = answers.topTierReviewer;
if (!isEmpty(answers.topTierPub.trim()))
mentorDetails.topTierPub = answers.topTierPub;
if (!isEmpty(answers.highImpactReviewer.trim()))
mentorDetails.highImpactReviewer = answers.highImpactReviewer;
if (answers.conferencePreference.length > 0)
mentorDetails.conferencePreference = answers.conferencePreference;
if (!isEmpty(answers.otherConferences.trim())) {
let otherconferences = answers.otherConferences.split(',');
let conferences = mentorDetails.conferencePreference;
conferences = conferences.concat(otherconferences);
mentorDetails.conferencePreference = conferences;
}
if (!isEmpty(answers.peerReviewedHighImpact))
mentorDetails.peerReviewedHighImpact = answers.peerReviewedHighImpact;
return mentorDetails;
};

I had added a package I needed in functions/index.js to my frontend with
/projectroot $ npm install -s [package]
, in stead of going into the /functions folder with the terminal and add it there to the /functions/package.json for node.js
/projectroot/functions $ npm install -s [package]
Why it causes so much trouble is because things work fine in emulator developer modus, but when you deploy it only sais it has an error, but doesn't tell you what the problem is.
More info on this: Firebase function failing to deploy

According to the error message, there isn't a whole lot of information about the problem you're presenting.
I know you used firebase –-debug deploy to retrieve the error message, and it returned an error on the user code, but what I would advise is that you look into the issue by viewing the log using firebase functions:log, which would be a lot easier.
There will be a visible representation of the exact issue. It could be something as simple as a missing package in some cases.
You can also use the following:
firebase functions:log --only <FUNCTION_NAME>
To have a better understanding of this command, you can access the Write and view logs.

Your firebase.json must include the following JSON code and it has to be similar to this in your case:
"hosting": {
"public": "public",
"rewrites" :[{
"source" : "**",
"function" : "api"
}],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}

Related

"Cannot set headers after they are sent to the client" - node.js, express.js

I am trying to implement a reset password flow in my application. In this post request, I am first checking if the database has the "linkPass" for the passed email which stores the expiration of the link. If not first check that the email is associated with a user account. If yes it generates a link and sends it to their email. If not (and that is where the problem occurs) it sends the client back to the form and displays a message.
So as you can see every condition redirects you to the same page, but if the user does not exist I get the cannot set headers error. I know what is the problem just don't know how to resolve it. Tried to return the redirections but didn't help...
app.post(
"/requireNewPassword",
tryCatch(async (req, res) => {
const { email, modulName, continueUrl } = req.body;
const ipAddress = await axios
.get("https://api.ipify.org")
.then((res) => {
return res.data;
})
.catch((err) => console.log(err));
const [linkPass] = await getLinkPasses(email, "last");
let linkAvailable = false;
//check if linkPass is available and expired
if (!linkPass || linkPass.expiration < Date.now()) {
linkAvailable = false;
const user = await admin
.auth()
.getUserByEmail(email)
.then(async (user) => {
return user;
})
.catch((err) => {
//if user does not exist
console.log(err.message);
if (err.code === "auth/user-not-found") {
linkAvailable = true;
console.log(`${email} does not exist!`);
return res.redirect(
`/?email=${email}&linkAvailable=${linkAvailable}`
);
}
});
//if user exits
if (user) {
const link_id = await generateLinkPass(email, 5, true);
//get firebase oobCode
const pwdResetLink = await admin
.auth()
.generatePasswordResetLink(email)
.then((link) => {
const queryParams = new URLSearchParams(link);
const oobCode = queryParams.get("oobCode");
const apiKey = queryParams.get("apiKey");
const recoveryLink = `${URL_PWD}/resetPassword/? link_id=${link_id}&email=${email}&apiKey=${apiKey}&oobCode=${oobCode}&continueUrl=${continueUrl}`;
console.log("Password recovery link has been created");
return recoveryLink;
})
.catch((err) => {
//if cannot generate pwdResetLink
console.log(
JSON.parse(
err.errorInfo.message.match(/Raw server response: "(.*)"/)[1]
).error.message
);
});
if (pwdResetLink) {
axios
.post(`${URL_EFDS}/sendEmailTemplate`, {
recipient: {
name: user.displayName || "John Doe",
email: email,
ip_address: ipAddress,
},
recoveryLink: pwdResetLink,
moduleName: modulName,
apiKey: EFDS_APIKEY,
templateType: "forgottenPassword",
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.data));
console.log(`Password recovery email has been sent to ${email}`);
return res.redirect(
`/?email=${email}&linkAvailable=${linkAvailable}`
);
} else {
//if pwdResetLink does not exsist
console.log("pwdResetLink does not exsist");
return res.redirect("partials/error", { err });
}
} else {
console.log("user does not exist");
const link_id = await generateLinkPass(email, 0, false);
return res.redirect(`/?email=${email}&linkAvailable=${linkAvailable}`); //this line causing the error
}
} else {
console.log("Valid link is already available");
linkAvailable = true;
//hiaba allitom true-ra a linkAvailablet, a get requestnel figyeli hogy az emailhez letrajott e linkPass, de nem jott letre, igy nem mutat uzenetet
return res.redirect(`/?email=${email}&linkAvailable=${linkAvailable}`);
}
})
);

one of my friend is trying to automate a process in which the bot will post instagram stories as a video from a specific folder

below is the working code in which it can post images but is there any way i can also share videos as instagram story?
the error i get when i try to post video instead of image are:**
error image
PS D:\Softwares\programming\Insta Bot\story> node index.js
18:45:11 - info: Dry Run Activated
18:45:11 - info: Post() called! ======================
18:45:11 - debug: 1 files found in ./images/
18:45:11 - warn: Record file not found, saying yes to D:\Softwares\programming\Insta Bot\story\images\meme.mp4
18:45:11 - debug: Read File Success
18:45:11 - error: undefined
(MAIN CODE)
index.js
const logger = require("./logger.js")
const { random, sleep } = require('./utils')
require('dotenv').config();
const { IgApiClient, IgLoginTwoFactorRequiredError } = require("instagram-private-api");
const ig = new IgApiClient();
const Bluebird = require('bluebird');
const inquirer = require('inquirer');
const { CronJob } = require('cron');
const path = require("path");
const fs = require("fs");
const fsp = fs.promises;
const sharp = require("sharp");
//==================================================================================
const statePath = "./etc/state.conf";
const recordPath = "./etc/usedfiles.jsonl";
const imgFolderPath = "./images/";
const dryrun = true;
const runOnStart = true;
//==================================================================================
(async () => { // FOR AWAIT
// LOGIN TO INSTAGRAM
if (!dryrun) {
await login();
logger.info("Log In Successful");
} else {
logger.info("Dry Run Activated");
}
// SCHEDULER
// logger.silly("I'm a schedule, and I'm running!! :)");
const job = new CronJob('38 43 * * * *', post, null, true); //https://crontab.guru/
if (!runOnStart) logger.info(`Next few posts scheduled for: \n${job.nextDates(3).join("\n")}\n`);
else post();
// MAIN POST COMMAND
async function post() {
logger.info("Post() called! ======================");
let postPromise = fsp.readdir(imgFolderPath)
.then(filenames => {
if (filenames.length < 1) throw new Error(`Folder ${imgFolderPath} is empty...`)
logger.debug(`${filenames.length} files found in ${imgFolderPath}`);
return filenames;
})
.then(filenames => filenames.map(file => path.resolve(imgFolderPath + file)))
.then(filenames => pickUnusedFileFrom(filenames, filenames.length))
.then(filename => {
if (!dryrun) registerFileUsed(filename)
return filename
})
.then(fsp.readFile)
.then(async buffer => {
logger.debug("Read File Success "); //TODO move this to previous then?
return sharp(buffer).jpeg().toBuffer()
.then(file => {
logger.debug("Sharp JPEG Success");
return file
})
})
.then(async file => {
if (!dryrun) {
// await sleep(random(1000, 60000)) //TODO is this necessary?
return ig.publish.story({ file })
.then(fb => logger.info("Posting successful!?"))
}
else return logger.info("Data not sent, dryrun = true")
})
.then(() => logger.info(`Next post scheduled for ${job.nextDates()}\n`))
.catch(logger.error)
}
})();
//=================================================================================
async function login() {
ig.state.generateDevice(process.env.IG_USERNAME);
// ig.state.proxyUrl = process.env.IG_PROXY;
//register callback?
ig.request.end$.subscribe(async () => {
const serialized = await ig.state.serialize();
delete serialized.constants; // this deletes the version info, so you'll always use the version provided by the library
await stateSave(serialized);
});
if (await stateExists()) {
// import state accepts both a string as well as an object
// the string should be a JSON object
const stateObj = await stateLoad();
await ig.state.deserialize(stateObj)
.catch(err => logger.debug("deserialize: " + err));
} else {
let standardLogin = async function() {
// login like normal
await ig.simulate.preLoginFlow();
logger.debug("preLoginFlow finished");
await ig.account.login(process.env.IG_USERNAME, process.env.IG_PASSWORD);
logger.info("Logged in as " + process.env.IG_USERNAME);
process.nextTick(async () => await ig.simulate.postLoginFlow());
logger.debug("postLoginFlow finished");
}
// Perform usual login
// If 2FA is enabled, IgLoginTwoFactorRequiredError will be thrown
return Bluebird.try(standardLogin)
.catch(
IgLoginTwoFactorRequiredError,
async err => {
logger.info("Two Factor Auth Required");
const {username, totp_two_factor_on, two_factor_identifier} = err.response.body.two_factor_info;
// decide which method to use
const verificationMethod = totp_two_factor_on ? '0' : '1'; // default to 1 for SMS
// At this point a code should have been sent
// Get the code
const { code } = await inquirer.prompt([
{
type: 'input',
name: 'code',
message: `Enter code received via ${verificationMethod === '1' ? 'SMS' : 'TOTP'}`,
},
]);
// Use the code to finish the login process
return ig.account.twoFactorLogin({
username,
verificationCode: code,
twoFactorIdentifier: two_factor_identifier,
verificationMethod, // '1' = SMS (default), '0' = TOTP (google auth for example)
trustThisDevice: '1', // Can be omitted as '1' is used by default
});
},
)
.catch(e => logger.error('An error occurred while processing two factor auth', e, e.stack));
}
return
//================================================================================
async function stateSave(data) {
// here you would save it to a file/database etc.
await fsp.mkdir(path.dirname(statePath), { recursive: true }).catch(logger.error);
return fsp.writeFile(statePath, JSON.stringify(data))
// .then(() => logger.info('state saved, daddy-o'))
.catch(err => logger.error("Write error" + err));
}
async function stateExists() {
return fsp.access(statePath, fs.constants.F_OK)
.then(() => {
logger.debug('Can access state info')
return true
})
.catch(() => {
logger.warn('Cannot access state info')
return false
});
}
async function stateLoad() {
// here you would load the data
return fsp.readFile(statePath, 'utf-8')
.then(data => JSON.parse(data))
.then(data => {
logger.info("State load successful");
return data
})
.catch(logger.error)
}
}
async function registerFileUsed( filepath ) {
let data = JSON.stringify({
path: filepath,
time: new Date().toISOString()
}) + '\n';
return fsp.appendFile(recordPath, data, { encoding: 'utf8', flag: 'a+' } )
.then(() => {
logger.debug("Writing filename to record file");
return filepath
})
}
function pickUnusedFileFrom( filenames, iMax = 1000) {
return new Promise((resolve, reject) => {
let checkFileUsed = async function ( filepath ) {
return fsp.readFile(recordPath, 'utf8')
.then(data => data.split('\n'))
.then(arr => arr.filter(Boolean))
.then(arr => arr.map(JSON.parse))
.then(arr => arr.some(entry => entry.path === filepath))
}
let trythis = function( iMax, i = 1) {
let file = random(filenames);
checkFileUsed(file)
.then(async used => {
if (!used) {
logger.info(`Unused file found! ${file}`);
resolve(file);
} else if (i < iMax) {
logger.debug(`Try #${i}: File ${file} used already`);
await sleep(50);
trythis(iMax, ++i)
} else {
reject(`I tried ${iMax} times and all the files I tried were previously used`)
}
})
.catch(err => {
logger.warn("Record file not found, saying yes to " + file);
resolve(file);
})
}( iMax );
})
}

Unhandled Rejection (TypeError): dispatch is not a function error when pulling data from backend

I am trying to pull data from my Postgresql tables to display as form fields in my React front-end. I am getting the below error message for each of the five fields (I have only included the example code for one as they are all the same).
The error is flagging in the action page and I can't see where I am going wrong as I have used similar elsewhere in my application and it worked correctly (although I am relatively new to JS / React).
error message
1 of 5 errors on the page
Unhandled Rejection (TypeError): dispatch is not a function
pullCurrentFields/<
sustainable-scuba-web-app/src/actions/formData.action.js:23
Front-end
formData.action.js
import {
diveCurrentFields,
diveRegionFields,
diveSpotFields,
diveTypeFields,
diveVisibilityFields
} from "../services/formFields.service";
import { failed_data_load, set_message, data_load_successful } from "./types.action";
export const pullCurrentFields = (dispatch) => {
return diveCurrentFields().then(
(response) => {
return response;
},
(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: failed_data_load,
});
dispatch({
type: set_message,
payload: message,
});
return Promise.reject();
}
);
};
formFields.service.js
import axios from 'axios';
const API_URL = "http://localhost:5002/api/formfields/";
// posts register details to backend
export const diveCurrentFields = (currentList) => {
return axios.get(API_URL + "divecurrentfields", {
//currentList,
});
};
Backend
formFields.routes.js
const { Router } = require("express");
const controller = require("../../controllers/formFields.controller");
module.exports = function(app) {
// return list fields for dive log form
app.get('/api/formfields/divecurrentfields', controller.allCurrentFields());
};
formFields.controller.js
const db = require("../server/models/");
const currents = db.currentLevel;
const diveRegions = db.diveRegion;
const diveTypes = db.diveType;
const visibilityLevels = db.visibilityLevel;
const diveSpots = db.diveSpot;
exports.allCurrentFields = (req, res) => {
currents.findAll({})
.then((currents) => {
const currentList = [];
for (i = 0; i < currents.length; i++) {
currentList.push(currents[i].dataValues);
}
if (!currents) {
return res.status(404).send({ message: "No current levels stored" });
}
res.status(200).send({
data: currentList,
});
})
.catch((err) => {
res.status(500).send({ message: err.message });
});
};

Bad request while deploying cloud function in firebase. HTTP Error: 400

I'm trying to deploy cloud function to create push notification (chat messaging) on firebase (Firestore).
But when i'm trying to do this - i'm always getting HTTP Error: 400, The request has errors.
Looks like path of collections is good.
exports.notifyNewMessage = functions.firestore
.document('/chat/{toUserId}/chatRoom/{fromUserId}/chatItems')
.onCreate((docSnapshot, context) => {
const message = docSnapshot.data();
const recipientId = context.params.toUserId; // получатель сообщения
const senderId = context.params.fromUserId; // отправитель сообщения
const senderName = message['username'];
if (recipientId === senderId) {
} else {
return admin.forestore().doc('tokens/' + recipientId).get().then(userDoc => {
const tokens = userDoc.get('tokens')
const notificationBody = (message['type'] === "TEXT") ? message['textMessage'] : "New message with Image"
const payload = {
notification: {
title: senderName + " sent you a message",
body: notificationBody,
clickAction: "ChatActivity" // возможно, это только для андроида
},
data: {
USER_NAME: senderName,
USER_ID: message['senderId']
}
}
return admin.messaging().sendToDevice(tokens, payload).then( response => {
const stillRegisteredTokens = tokens
response.results.forEach((result, index) => {
const error = result.error
if (error) {
const failedRegistrationToken = tokens[index]
console.error('failed token', failedRegistrationToken, error)
if (error.code === 'messaging/invalid-registration-token' || error.code == 'messaging/registration-token-not-registred') {
const failedIndex = stillRegisteredTokens.indexOf(failedRegistrationToken)
if (failedIndex > -1) {
stillRegisteredTokens.splice(failedIndex, 1)
}
}
}
})
return admin.firestore().doc("tokens" + recipientId).update({
tokens: stillRegisteredTokens
})
})
})
}
})
also i would ask about line "clickAction: "ChatActivity" it only for android? how can i do same to ios?
Thx a lot!
Try to delete the function from firebase console and redeploy
or
try changing Internet Service Provider and deploy

How to consume a RESTful API in Node.js

I'm new to Node.js and I'm creating a simple pagination page. The REST API works fine, but consuming it has left me in limbo.
Here is the REST API (other parts have been taken out for brevity)
const data = req.query.pageNo;
const pageNo =
(typeof data === 'undefined' || data < 1) ? 1 : parseInt(req.query.pageNo);
let query = {};
const total = 10;
query.skip = (total * pageNo) - total;
query.limit = total;
try {
const totalCount = await Users.countDocuments();
const pageTotal = Math.ceil(totalCount / total);
const users = await Users.find({}, {}, query);
return res.status(200).json(users);
} catch (error) {
console.log('Error ', error);
return res.status(400).send(error)
};
};
When I return the json with just the 'users' object, like so return res.status(200).json(users); the page renders correctly, but when I pass in other objects like what I have in the code, it fails. This is how I'm consuming the API:
const renderHomepage = (req, res, responseBody) => {
let message = null;
if (!(responseBody instanceof Array)) {
message = 'API lookup error';
responseBody = [];
} else {
if (!responseBody.length) {
message = 'No users found nearby';
}
}
res.render('users-list', {
title: 'Home Page',
users: responseBody,
message: message
});
}
const homelist = (req, res) => {
const path = '/api/users';
const requestOptions = {
url: `${apiOptions.server}${path}`,
method: 'GET',
json: true,
};
request(
requestOptions,
(err, {statusCode}, body) => {
if (err) {
console.log('Ther was an error ', err);
} else if (statusCode === 200 && body.length) {
renderHomepage(req, res, body);
} else if (statusCode !== 200 && !body.length) {
console.log('error ',statusCode);
}
}
);
}
I've searched extensively on both here and other resources but none of the solutions quite answers my question. I hope someone could be of help

Resources