From bcdd452aa816e547c21cf0f434229794ee132ed4 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Mon, 23 Sep 2019 22:58:06 -0400 Subject: [PATCH 01/10] Start of the edit profile functions. --- functions/index.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/functions/index.js b/functions/index.js index 9130d2f..27262c6 100644 --- a/functions/index.js +++ b/functions/index.js @@ -75,4 +75,23 @@ app.post('/postUser', (req, res) => { }); }); +// Returns all profile data of the currently logged in user +app.get('getProfileInfo', (req, res) => { + +}); + +// Updates the currently logged in user's profile information +app.post('/updateProfileInfo', FBAuth, (req, res) => { + const profileData = { + handle: req.user.handle, + firstName: req.body.firstName, + lastName: req.body.lastName, + imageUrl: req.body.imageUrl, + }; + + return res.status(200).json(profileData); + + +}); + exports.api = functions.https.onRequest(app); \ No newline at end of file From 5642b685bc8111f51f4dc34624abc269df81427f Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Fri, 27 Sep 2019 16:28:00 -0400 Subject: [PATCH 02/10] Finished functionality of backend edit profile functions --- functions/index.js | 61 +++++++++-- functions/package-lock.json | 211 +++++++++++++++++++++++++----------- functions/package.json | 2 +- 3 files changed, 199 insertions(+), 75 deletions(-) diff --git a/functions/index.js b/functions/index.js index 27262c6..3342c37 100644 --- a/functions/index.js +++ b/functions/index.js @@ -18,12 +18,18 @@ const firebaseConfig = { const firebase = require('firebase'); firebase.initializeApp(firebaseConfig); +const isEmpty = (str) => { + if (str.trim() === '') return true; + else return false; +} + // Acts as a middleman between the client and any function that you use it with // The function will only execute if the user is logged in, or rather, they have // a valid token const FBAuth = (req, resp, next) => { let idToken; + // Checking that the token exists in the header of the request if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) { idToken = req.headers.authorization.split('Bearer ')[1]; } else { @@ -31,6 +37,7 @@ const FBAuth = (req, resp, next) => { return resp.status(403).json({ error: 'Unauthorized' }); } + // Checking that the token is valid in firebase admin.auth().verifyIdToken(idToken) .then(decodedToken => { req.user = decodedToken; @@ -41,7 +48,7 @@ const FBAuth = (req, resp, next) => { .get(); }) .then(data => { - req.user.handle = data.docs[0].data().handle; + req.user.handle = data.docs[0].data().handle; // Save username return next(); }) .catch(err => { @@ -51,7 +58,7 @@ const FBAuth = (req, resp, next) => { } app.get('/getUsers', (req, res) => { - admin.firestore().collection('users').get().then(data => { + db.collection('users').get().then(data => { let users = []; data.forEach(doc => { users.push(doc.data()); @@ -63,35 +70,65 @@ app.post('/postUser', (req, res) => { const newUser = { body: req.body.body }; - admin.firestore().collection('users').add(newUser).then((doc) => { + db.collection('users').add(newUser).then((doc) => { res.json({ message: 'Successfully added!' }); }).catch((err) => { res.status(500).json({ - error: "Error in posting user!" + error: 'Error in posting user!' }); console.error(err); }); }); // Returns all profile data of the currently logged in user -app.get('getProfileInfo', (req, res) => { +app.get('/getProfileInfo', (req, res) => { + // FIXME: Delete this after login is implemented + req.user = {}; + req.user.handle = 'itsjimmy'; + db.collection('users').doc(req.user.handle).get() + .then((data) => { + return res.status(200).json(data.data()); + }); }); // Updates the currently logged in user's profile information -app.post('/updateProfileInfo', FBAuth, (req, res) => { +app.post('/updateProfileInfo', (req, res) => { + // FIXME: Delete this after login is implemented + req.user = {}; + req.user.handle = 'itsjimmy'; + + // TODO: Add functionality for adding/updating profile images + + // ?: Should users be able to change their handles? const profileData = { - handle: req.user.handle, - firstName: req.body.firstName, - lastName: req.body.lastName, - imageUrl: req.body.imageUrl, + firstName: req.body.firstName.trim(), // Can be empty + lastName: req.body.lastName.trim(), // Can be empty + email: req.body.email.trim(), // Cannot be empty + bio: req.body.bio.trim(), // Can be empty }; + + // Data validation + let errors = {} - return res.status(200).json(profileData); - + if (isEmpty(profileData.email)) { + errors.email = "Must not be empty."; + } + // Update the database entry for this user + db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) + .then((data) =>{ + console.log(`${req.user.handle}'s profile info has been updated.`) + return res.status(200).json({general: `${req.user.handle}'s profile info has been updated.`}); + }) + .catch((err) => { + res.status(500).json({ + error: 'Error updating profile data' + }); + console.error(err); + }) }); exports.api = functions.https.onRequest(app); \ No newline at end of file diff --git a/functions/package-lock.json b/functions/package-lock.json index a23d4aa..7d9edfe 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -55,15 +55,30 @@ "integrity": "sha512-foQHhvyB0RR+mb/+wmHXd/VOU+D8fruFEW1k79Q9wzyTPpovMBa1Mcns5fwEWBhUfi8bmoEtaGB8RSAHnTFzTg==" }, "@firebase/database": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.2.tgz", - "integrity": "sha512-LnXKRE1AmjlS+iRF7j8vx+Ni8x85CmLP5u5Pw5rDKhKLn2eTR1tJKD937mUeeGEtDHwR1rrrkLYOqRR2cSG3hQ==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.5.4.tgz", + "integrity": "sha512-Hz1Bi3fzIcNNocE4EhvvwoEQGurG2BGssWD3/6a2bzty+K1e57SLea2Ied8QYNBUU1zt/4McHfa3Y71EQIyn/w==", "requires": { "@firebase/database-types": "0.4.3", - "@firebase/logger": "0.1.24", - "@firebase/util": "0.2.27", + "@firebase/logger": "0.1.25", + "@firebase/util": "0.2.28", "faye-websocket": "0.11.3", "tslib": "1.10.0" + }, + "dependencies": { + "@firebase/logger": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.25.tgz", + "integrity": "sha512-/lRhuepVcCCnQ2jcO5Hr08SYdmZDTQU9fdPdzg+qXJ9k/QnIrD2RbswXQcL6mmae3uPpX7fFXQAoScJ9pzp50w==" + }, + "@firebase/util": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.28.tgz", + "integrity": "sha512-ZQMAWtXj8y5kvB6izs0aTM/jG+WO8HpqhXA/EwD6LckJ+1P5LnAhaLZt1zR4HpuCE+jeP5I32Id5RJ/aifFs6A==", + "requires": { + "tslib": "1.10.0" + } + } } }, "@firebase/database-types": { @@ -208,9 +223,9 @@ "integrity": "sha512-VlTurkvs4v7EVFWESBZGOPghFEokQhU5au5CP9WqA8B2/PcQRDsaaQlQCA6VATuEnW+vtSiSBvTiOc4004f8xg==" }, "@google-cloud/common": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.0.tgz", - "integrity": "sha512-ArSNbbuMOWVhrSasxECEYRcjMzkPgTfXJHQE5gccyDaoBv0oKqG9S2lse2KAgHpRADRna7wKiX9PWOpeB19VvA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-2.2.2.tgz", + "integrity": "sha512-AgMdDgLeYlEG17tXtMCowE7mplm907pcugtfJYYAp06HNe9RDnunUIY5KMnn9yikYl7NXNofARC+hwG77Zsa4g==", "optional": true, "requires": { "@google-cloud/projectify": "^1.0.0", @@ -256,12 +271,13 @@ "@google-cloud/promisify": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-1.0.2.tgz", - "integrity": "sha512-7WfV4R/3YV5T30WRZW0lqmvZy9hE2/p9MvpI34WuKa2Wz62mLu5XplGTFEMK6uTbJCLWUxTcZ4J4IyClKucE5g==" + "integrity": "sha512-7WfV4R/3YV5T30WRZW0lqmvZy9hE2/p9MvpI34WuKa2Wz62mLu5XplGTFEMK6uTbJCLWUxTcZ4J4IyClKucE5g==", + "optional": true }, "@google-cloud/storage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-3.2.1.tgz", - "integrity": "sha512-129EwPGej6bXzY1u5nja2aeMDew6DIHaJn7ZV6nteQ74LQQSNv2jKrqTlyhndBsAwpuwQAxeghPTCoFT/H8Frg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-3.3.0.tgz", + "integrity": "sha512-9jmHJ0ncQTcrZRwq5MRjXEwuCFkIjHenYwVbycV6bbZ4O84Hcgg4Yp33sKcJug5rvZeVgrpCzPbYXqO3B0LzJw==", "optional": true, "requires": { "@google-cloud/common": "^2.1.1", @@ -270,27 +286,50 @@ "arrify": "^2.0.0", "compressible": "^2.0.12", "concat-stream": "^2.0.0", - "date-and-time": "^0.9.0", + "date-and-time": "^0.10.0", "duplexify": "^3.5.0", "extend": "^3.0.2", "gaxios": "^2.0.1", - "gcs-resumable-upload": "^2.0.0", + "gcs-resumable-upload": "^2.2.4", "hash-stream-validation": "^0.2.1", "mime": "^2.2.0", "mime-types": "^2.0.8", "onetime": "^5.1.0", "p-limit": "^2.2.0", "pumpify": "^2.0.0", + "readable-stream": "^3.4.0", "snakeize": "^0.1.0", "stream-events": "^1.0.1", "through2": "^3.0.0", "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "optional": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "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==", + "optional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } } }, "@grpc/grpc-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.5.3.tgz", - "integrity": "sha512-doDzxjdN0IJihQJvjDkZun9bZp/TW2EKO5E4fNvw8634kU1eNqPnFtAmiEiIYptqJ9StC+zRo1mwrazhqI0k5A==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-0.5.4.tgz", + "integrity": "sha512-aY4fTCz7jq7oKFmfAeZVqGzMCR5I9NLdY9E2fJ70QtGXwlJnTaN6cnbRmCk23/aKPx9UHqOtk2lyjpN6LbAaxw==", "optional": true, "requires": { "semver": "^6.2.0" @@ -443,6 +482,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, "requires": { "event-target-shim": "^5.0.0" } @@ -472,6 +512,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "optional": true, "requires": { "es6-promisify": "^5.0.0" } @@ -526,7 +567,8 @@ "arrify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true }, "ascli": { "version": "1.0.1", @@ -552,12 +594,14 @@ "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "optional": true }, "bignumber.js": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", - "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==", + "optional": true }, "body-parser": { "version": "1.19.0", @@ -853,7 +897,8 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "optional": true }, "cors": { "version": "2.8.5", @@ -892,15 +937,16 @@ "optional": true }, "date-and-time": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.9.0.tgz", - "integrity": "sha512-4JybB6PbR+EebpFx/KyR5Ybl+TcdXMLIJkyYsCx3P4M4CWGMuDyFF19yh6TyasMAIF5lrsgIxiSHBXh2FFc7Fg==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-0.10.0.tgz", + "integrity": "sha512-IbIzxtvK80JZOVsWF6+NOjunTaoFVYxkAQoyzmflJyuRCJAJebehy48mPiCAedcGp4P7/UO3QYRWa0fe6INftg==", "optional": true }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "optional": true, "requires": { "ms": "^2.1.1" } @@ -984,6 +1030,7 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "optional": true, "requires": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", @@ -994,12 +1041,14 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1013,12 +1062,14 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -1058,9 +1109,10 @@ } }, "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.3.tgz", + "integrity": "sha512-cbNhPFS6MlYlWTGncSiDYbdqKhwWFy7kNeb1YSOG6K65i/wPTkLVCJQj0hXA4j0m5Da+hBWnqopEnu1FFelisQ==", + "optional": true, "requires": { "once": "^1.4.0" } @@ -1074,12 +1126,14 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "optional": true }, "es6-promisify": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "optional": true, "requires": { "es6-promise": "^4.0.3" } @@ -1242,7 +1296,8 @@ "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true }, "express": { "version": "4.17.1", @@ -1304,7 +1359,8 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "optional": true }, "external-editor": { "version": "3.1.0", @@ -1338,7 +1394,8 @@ "fast-text-encoding": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.0.tgz", - "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==" + "integrity": "sha512-R9bHCvweUxxwkDwhjav5vxpFvdPGlVngtqmx4pIZfSUhM/Q4NiIUHB456BAf+Q1Nwu3HEZYONtu+Rya+af4jiQ==", + "optional": true }, "faye-websocket": { "version": "0.11.3", @@ -1429,9 +1486,9 @@ } }, "firebase-admin": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.5.0.tgz", - "integrity": "sha512-rvgCj5Z1iFOT6K6uW37VRl4PKNpAcBFu/FIQ4Nl5bFnqbHSxf+QxzsqdsUtIxdqZU1yh2DTs2t+s5qORx/T9+g==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-8.6.0.tgz", + "integrity": "sha512-+JqOinU5bYUkg434LqEBXrHMrIBhL/+HwWEgbZpS1sBKHQRJK7LlcBrayqxvQKwJzgh5xs/JTInTmkozXk7h1w==", "requires": { "@firebase/database": "^0.5.1", "@google-cloud/firestore": "^2.0.0", @@ -1536,6 +1593,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.0.1.tgz", "integrity": "sha512-c1NXovTxkgRJTIgB2FrFmOFg4YIV6N/bAa4f/FZ4jIw13Ql9ya/82x69CswvotJhbV3DiGnlTZwoq2NVXk2Irg==", + "optional": true, "requires": { "abort-controller": "^3.0.0", "extend": "^3.0.2", @@ -1544,9 +1602,10 @@ } }, "gcp-metadata": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-2.0.2.tgz", - "integrity": "sha512-dxPXBvjyfz5qFEBXzEwNmuZXwsGYfuASGYeg3CKZDaQRXdiWti9J3/Ezmtyon1OrCNpDO2YekyoSjEqMtsrcXw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.0.0.tgz", + "integrity": "sha512-WP5/TZWri9TrD41jNr8ukY9dKYLL+8jwQVwbtUbmprjWuyybdnJNkbXbwqD2sdbXIVXD1WCqzfj7QftSLB6K8Q==", + "optional": true, "requires": { "gaxios": "^2.0.1", "json-bigint": "^0.3.0" @@ -1587,24 +1646,25 @@ "dev": true }, "google-auth-library": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.2.1.tgz", - "integrity": "sha512-p9vO6UcRIK/zD3PxoMijaUfFYu6tvzaQwvag1K/82O42NBeAnmllyQUgqaBhcAh9FzFAVlN4bQIaO8+prpE7Vg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.2.2.tgz", + "integrity": "sha512-0vzniXbjD5SE9aenAMqhjVR99wvqLpyd5Fw6zC3WxJ15GIMGx96tq+Cu1WRviqsnQqhrmnad6T69kv6qkj/w2Q==", + "optional": true, "requires": { "arrify": "^2.0.0", "base64-js": "^1.3.0", "fast-text-encoding": "^1.0.0", "gaxios": "^2.0.0", - "gcp-metadata": "^2.0.0", + "gcp-metadata": "^3.0.0", "gtoken": "^4.0.0", "jws": "^3.1.5", "lru-cache": "^5.0.0" } }, "google-gax": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.5.2.tgz", - "integrity": "sha512-NceyDzlw4mQz6qH3bDIuRtfDAZKehM96QpnPPJ3Hur7FA/gPzpzboUYwhfP6q5obSP4LuSSDhI/76Fu51/ljtg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-1.6.1.tgz", + "integrity": "sha512-5/6uaUA9qAqRKVe2sjvMgsnU/HbfQisQTM5EZ5DfNGOYVBoTsPBdOhR2ZqEWPyqHe7YkdzVHev3FH9W3YWcORw==", "optional": true, "requires": { "@grpc/grpc-js": "^0.5.2", @@ -1626,6 +1686,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.2.tgz", "integrity": "sha512-UfnEARfJKI6pbmC1hfFFm+UAcZxeIwTiEcHfqKe/drMsXD/ilnVjF7zgOGpHXyhuvX6jNJK3S8A0hOQjwtFxEw==", + "optional": true, "requires": { "node-forge": "^0.9.0" }, @@ -1633,7 +1694,8 @@ "node-forge": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==" + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", + "optional": true } } }, @@ -2073,9 +2135,10 @@ } }, "gtoken": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.0.0.tgz", - "integrity": "sha512-XaRCfHJxhj06LmnWNBzVTAr85NfAErq0W1oabkdqwbq3uL/QTB1kyvGog361Uu2FMG/8e3115sIy/97Rnd4GjQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.0.tgz", + "integrity": "sha512-wqyn2gf5buzEZN4QNmmiiW2i2JkEdZnL7Z/9p44RtZqgt4077m4khRgAYNuu8cBwHWCc6MsP6eDUn/KkF6jFIw==", + "optional": true, "requires": { "gaxios": "^2.0.0", "google-p12-pem": "^2.0.0", @@ -2131,7 +2194,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "string_decoder": { "version": "1.1.1", @@ -2209,6 +2273,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "optional": true, "requires": { "agent-base": "^4.3.0", "debug": "^3.1.0" @@ -2364,7 +2429,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true }, "isarray": { "version": "0.0.1", @@ -2418,6 +2484,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", + "optional": true, "requires": { "bignumber.js": "^7.0.0" } @@ -2559,6 +2626,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "optional": true, "requires": { "yallist": "^3.0.2" } @@ -2590,7 +2658,8 @@ "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "optional": true }, "mime-db": { "version": "1.41.0", @@ -2679,7 +2748,8 @@ "node-fetch": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "optional": true }, "node-forge": { "version": "0.7.4", @@ -2705,7 +2775,8 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true }, "on-finished": { "version": "2.3.0", @@ -2826,7 +2897,8 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "optional": true }, "progress": { "version": "2.0.3", @@ -2879,6 +2951,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "optional": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -2888,6 +2961,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.0.tgz", "integrity": "sha512-ieN9HmpFPt4J4U4qnjN4BxrnqpPPXJyp3qFErxfwBtFOec6ewpIHdS2eu3TkmGW6S+RzFGEOGpm5ih/X/onRPQ==", + "optional": true, "requires": { "duplexify": "^4.1.1", "inherits": "^2.0.3", @@ -2898,6 +2972,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.1.tgz", "integrity": "sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA==", + "optional": true, "requires": { "end-of-stream": "^1.4.1", "inherits": "^2.0.3", @@ -2909,6 +2984,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2919,6 +2995,7 @@ "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==", + "optional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3073,7 +3150,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "optional": true }, "send": { "version": "0.17.1", @@ -3190,6 +3268,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "optional": true, "requires": { "stubs": "^3.0.0" } @@ -3197,7 +3276,8 @@ "stream-shift": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "optional": true }, "streamsearch": { "version": "0.1.2", @@ -3238,7 +3318,8 @@ "stubs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", - "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=" + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", + "optional": true }, "supports-color": { "version": "5.5.0", @@ -3318,6 +3399,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "optional": true, "requires": { "readable-stream": "2 || 3" }, @@ -3326,6 +3408,7 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "optional": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3336,6 +3419,7 @@ "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==", + "optional": true, "requires": { "safe-buffer": "~5.2.0" } @@ -3420,7 +3504,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "optional": true }, "utils-merge": { "version": "1.0.1", @@ -3555,7 +3640,8 @@ "xdg-basedir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "optional": true }, "xmlhttprequest": { "version": "1.8.0", @@ -3575,7 +3661,8 @@ "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "optional": true }, "yargs": { "version": "3.32.0", diff --git a/functions/package.json b/functions/package.json index 200b47a..845347b 100644 --- a/functions/package.json +++ b/functions/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "firebase": "^6.6.1", - "firebase-admin": "^8.0.0", + "firebase-admin": "^8.6.0", "firebase-functions": "^3.1.0" }, "devDependencies": { From 262e59df655151cbf3730648a1295aa38b5b6c3a Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sat, 28 Sep 2019 22:42:26 -0400 Subject: [PATCH 03/10] Disabled the feed route rewrite and small fixes. --- firebase.json | 6 +----- functions/index.js | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/firebase.json b/firebase.json index 896c687..ad362e4 100644 --- a/firebase.json +++ b/firebase.json @@ -17,11 +17,7 @@ "firebase.json", "**/.*", "**/node_modules/**" - ], - "rewrites": [{ - "source": "/feed", - "destination": "/feed.html" - }] + ] }, "storage": { "rules": "storage.rules" diff --git a/functions/index.js b/functions/index.js index 3342c37..da1d19f 100644 --- a/functions/index.js +++ b/functions/index.js @@ -119,15 +119,15 @@ app.post('/updateProfileInfo', (req, res) => { // Update the database entry for this user db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) - .then((data) =>{ + .then(() => { console.log(`${req.user.handle}'s profile info has been updated.`) return res.status(200).json({general: `${req.user.handle}'s profile info has been updated.`}); }) .catch((err) => { - res.status(500).json({ + console.error(err); + return res.status(500).json({ error: 'Error updating profile data' }); - console.error(err); }) }); From 15f976398a6ac6447be40493ed58998d624c6c41 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 01:35:06 -0400 Subject: [PATCH 04/10] Code refactoring --- functions/handlers/users.js | 46 +++++++++++++ functions/index.js | 130 +++--------------------------------- functions/util/FBAuth.js | 35 ++++++++++ functions/util/admin.js | 7 ++ functions/util/config.js | 9 +++ functions/util/validator.js | 17 +++++ 6 files changed, 123 insertions(+), 121 deletions(-) create mode 100644 functions/handlers/users.js create mode 100644 functions/util/FBAuth.js create mode 100644 functions/util/admin.js create mode 100644 functions/util/config.js create mode 100644 functions/util/validator.js diff --git a/functions/handlers/users.js b/functions/handlers/users.js new file mode 100644 index 0000000..39e1e83 --- /dev/null +++ b/functions/handlers/users.js @@ -0,0 +1,46 @@ +const {db} = require('../util/admin'); +const {validateUpdateProfileInfo} = require('../util/validator'); + +exports.getProfileInfo = (req, res) => { + // FIXME: Delete this after login is implemented + req.user = {}; + req.user.handle = 'itsjimmy'; + + db.collection('users').doc(req.user.handle).get() + .then((data) => { + return res.status(200).json(data.data()); + }); +}; + +exports.updateProfileInfo = (req, res) => { + // FIXME: Delete this after login is implemented + req.user = {}; + req.user.handle = 'itsjimmy'; + + // TODO: Add functionality for adding/updating profile images + + // ?: Should users be able to change their handles? + const profileData = { + firstName: req.body.firstName.trim(), // Can be empty + lastName: req.body.lastName.trim(), // Can be empty + email: req.body.email.trim(), // Cannot be empty + bio: req.body.bio.trim(), // Can be empty + }; + + // Data validation + const {valid, errors} = validateUpdateProfileInfo(profileData); + if (!valid) return res.status(400).json(errors); + + // Update the database entry for this user + db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) + .then(() => { + console.log(`${req.user.handle}'s profile info has been updated.`) + return res.status(201).json({general: `${req.user.handle}'s profile info has been updated.`}); + }) + .catch((err) => { + console.error(err); + return res.status(500).json({ + error: 'Error updating profile data' + }); + }) +}; \ No newline at end of file diff --git a/functions/index.js b/functions/index.js index da1d19f..aba5607 100644 --- a/functions/index.js +++ b/functions/index.js @@ -1,134 +1,22 @@ /* eslint-disable promise/always-return */ const functions = require('firebase-functions'); -const admin = require('firebase-admin'); const app = require('express')(); -admin.initializeApp(); -const db = admin.firestore(); -const firebaseConfig = { - apiKey: "AIzaSyCvsWetg4qFdsPGfJ3LCw_QaaYzoan7Q34", - authDomain: "twistter-e4649.firebaseapp.com", - databaseURL: "https://twistter-e4649.firebaseio.com", - projectId: "twistter-e4649", - storageBucket: "twistter-e4649.appspot.com", - messagingSenderId: "20131817365", - appId: "1:20131817365:web:633c95fb08b16d4526b89c" -}; +const FBAuth = require('./util/FBAuth'); -const firebase = require('firebase'); -firebase.initializeApp(firebaseConfig); +/*------------------------------------------------------------------* + * users.js * + *------------------------------------------------------------------*/ -const isEmpty = (str) => { - if (str.trim() === '') return true; - else return false; -} - -// Acts as a middleman between the client and any function that you use it with -// The function will only execute if the user is logged in, or rather, they have -// a valid token -const FBAuth = (req, resp, next) => { - let idToken; - - // Checking that the token exists in the header of the request - if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) { - idToken = req.headers.authorization.split('Bearer ')[1]; - } else { - console.error('No token found'); - return resp.status(403).json({ error: 'Unauthorized' }); - } - - // Checking that the token is valid in firebase - admin.auth().verifyIdToken(idToken) - .then(decodedToken => { - req.user = decodedToken; - console.log(decodedToken); - return db.collection('users') - .where('userId', '==', req.user.uid) - .limit(1) - .get(); - }) - .then(data => { - req.user.handle = data.docs[0].data().handle; // Save username - return next(); - }) - .catch(err => { - console.error('Error verifying token', err); - return res.status(403).json(err); - }) -} - -app.get('/getUsers', (req, res) => { - db.collection('users').get().then(data => { - let users = []; - data.forEach(doc => { - users.push(doc.data()); - }); return res.json(users); - }).catch((err) => console.error(err)); -}); - -app.post('/postUser', (req, res) => { - const newUser = { - body: req.body.body - }; - db.collection('users').add(newUser).then((doc) => { - res.json({ - message: 'Successfully added!' - }); - }).catch((err) => { - res.status(500).json({ - error: 'Error in posting user!' - }); - console.error(err); - }); -}); +const {getProfileInfo, updateProfileInfo} = require('./handlers/users'); // Returns all profile data of the currently logged in user -app.get('/getProfileInfo', (req, res) => { - // FIXME: Delete this after login is implemented - req.user = {}; - req.user.handle = 'itsjimmy'; - - db.collection('users').doc(req.user.handle).get() - .then((data) => { - return res.status(200).json(data.data()); - }); -}); +// TODO: Add FBAuth +app.get('/getProfileInfo', getProfileInfo); // Updates the currently logged in user's profile information -app.post('/updateProfileInfo', (req, res) => { - // FIXME: Delete this after login is implemented - req.user = {}; - req.user.handle = 'itsjimmy'; +// TODO: Add FBAuth +app.post('/updateProfileInfo', updateProfileInfo); - // TODO: Add functionality for adding/updating profile images - - // ?: Should users be able to change their handles? - const profileData = { - firstName: req.body.firstName.trim(), // Can be empty - lastName: req.body.lastName.trim(), // Can be empty - email: req.body.email.trim(), // Cannot be empty - bio: req.body.bio.trim(), // Can be empty - }; - - // Data validation - let errors = {} - - if (isEmpty(profileData.email)) { - errors.email = "Must not be empty."; - } - - // Update the database entry for this user - db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) - .then(() => { - console.log(`${req.user.handle}'s profile info has been updated.`) - return res.status(200).json({general: `${req.user.handle}'s profile info has been updated.`}); - }) - .catch((err) => { - console.error(err); - return res.status(500).json({ - error: 'Error updating profile data' - }); - }) -}); exports.api = functions.https.onRequest(app); \ No newline at end of file diff --git a/functions/util/FBAuth.js b/functions/util/FBAuth.js new file mode 100644 index 0000000..e9ebf97 --- /dev/null +++ b/functions/util/FBAuth.js @@ -0,0 +1,35 @@ +const {admin, db} = require('./admin'); + +// Acts as a middleman between the client and any function that you use it with +// The function will only execute if the user is logged in, or rather, they have +// a valid token +module.exports = (req, resp, next) => { + let idToken; + + // Checking that the token exists in the header of the request + if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) { + idToken = req.headers.authorization.split('Bearer ')[1]; + } else { + console.error('No token found'); + return resp.status(403).json({ error: 'Unauthorized' }); + } + + // Checking that the token is valid in firebase + admin.auth().verifyIdToken(idToken) + .then(decodedToken => { + req.user = decodedToken; + console.log(decodedToken); + return db.collection('users') + .where('userId', '==', req.user.uid) + .limit(1) + .get(); + }) + .then(data => { + req.user.handle = data.docs[0].data().handle; // Save username + return next(); + }) + .catch(err => { + console.error('Error verifying token', err); + return res.status(403).json(err); + }) +}; \ No newline at end of file diff --git a/functions/util/admin.js b/functions/util/admin.js new file mode 100644 index 0000000..7710d59 --- /dev/null +++ b/functions/util/admin.js @@ -0,0 +1,7 @@ +const admin = require('firebase-admin'); + +admin.initializeApp(); + +const db = admin.firestore(); + +module.exports = {admin, db}; \ No newline at end of file diff --git a/functions/util/config.js b/functions/util/config.js new file mode 100644 index 0000000..903fed5 --- /dev/null +++ b/functions/util/config.js @@ -0,0 +1,9 @@ +module.exports = { + apiKey: "AIzaSyCvsWetg4qFdsPGfJ3LCw_QaaYzoan7Q34", + authDomain: "twistter-e4649.firebaseapp.com", + databaseURL: "https://twistter-e4649.firebaseio.com", + projectId: "twistter-e4649", + storageBucket: "twistter-e4649.appspot.com", + messagingSenderId: "20131817365", + appId: "1:20131817365:web:633c95fb08b16d4526b89c" +}; \ No newline at end of file diff --git a/functions/util/validator.js b/functions/util/validator.js new file mode 100644 index 0000000..b7c18d8 --- /dev/null +++ b/functions/util/validator.js @@ -0,0 +1,17 @@ +const isEmpty = (str) => { + if (str.trim() === '') return true; + else return false; +}; + +exports.validateUpdateProfileInfo = (profileData) => { + let errors = {} + + if (isEmpty(profileData.email)) { + errors.email = "Must not be empty."; + } + + return { + errors, + valid: Object.keys(errors).length === 0 ? true : false + } +}; \ No newline at end of file From d05f87c7a7372f52d07aa6898856300863b5a890 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 21:01:06 -0400 Subject: [PATCH 05/10] Made edit profile data validation more robust. --- functions/handlers/users.js | 10 ++-------- functions/index.js | 1 - functions/util/validator.js | 27 +++++++++++++++++++++++---- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 39e1e83..6a4a42c 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -19,17 +19,11 @@ exports.updateProfileInfo = (req, res) => { // TODO: Add functionality for adding/updating profile images - // ?: Should users be able to change their handles? - const profileData = { - firstName: req.body.firstName.trim(), // Can be empty - lastName: req.body.lastName.trim(), // Can be empty - email: req.body.email.trim(), // Cannot be empty - bio: req.body.bio.trim(), // Can be empty - }; // Data validation - const {valid, errors} = validateUpdateProfileInfo(profileData); + const {valid, errors, profileData} = validateUpdateProfileInfo(req.body); if (!valid) return res.status(400).json(errors); + // Update the database entry for this user db.collection('users').doc(req.user.handle).set(profileData, {merge: true}) diff --git a/functions/index.js b/functions/index.js index aba5607..8803e0b 100644 --- a/functions/index.js +++ b/functions/index.js @@ -7,7 +7,6 @@ const FBAuth = require('./util/FBAuth'); /*------------------------------------------------------------------* * users.js * *------------------------------------------------------------------*/ - const {getProfileInfo, updateProfileInfo} = require('./handlers/users'); // Returns all profile data of the currently logged in user diff --git a/functions/util/validator.js b/functions/util/validator.js index b7c18d8..ca29c6c 100644 --- a/functions/util/validator.js +++ b/functions/util/validator.js @@ -3,15 +3,34 @@ const isEmpty = (str) => { else return false; }; -exports.validateUpdateProfileInfo = (profileData) => { - let errors = {} +const isEmail = (str) => { + const emailRegEx = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + if (str.match(emailRegEx)) return true; + else return false; +} - if (isEmpty(profileData.email)) { +exports.validateUpdateProfileInfo = (data) => { + let errors = {}; + let profileData = {}; + + // ?: Should users be able to change their handles and emails? + + // Only adds the key to the DB if the values are not empty + if (!isEmpty(data.firstName)) profileData.firstName = data.firstName.trim(); + if (!isEmpty(data.lastName)) profileData.lastName = data.lastName.trim(); + if (!isEmpty(data.bio)) profileData.bio = data.bio.trim(); + + if (isEmpty(data.email)) { errors.email = "Must not be empty."; + } else if (!isEmail(data.email)) { + errors.email = "Must be a valid email." + } else { + profileData.email = data.email; } return { errors, - valid: Object.keys(errors).length === 0 ? true : false + valid: Object.keys(errors).length === 0 ? true : false, + profileData } }; \ No newline at end of file From ce8ee3635472455b6c57714fcd3c22658fda8404 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 21:46:18 -0400 Subject: [PATCH 06/10] Fixed missing bracket --- functions/handlers/users.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 3637574..240bfdd 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -37,6 +37,7 @@ exports.updateProfileInfo = (req, res) => { error: 'Error updating profile data' }); }) +}; exports.getUserDetails = (req, res) => { let userData = {}; @@ -70,4 +71,4 @@ exports.getUserDetails = (req, res) => { console.error(err); return res.status(500).json({ error: err.code}); }); -}; \ No newline at end of file +}; From f4ef1ee8e1b38cb204d414d8e3cd0fcd67b06b95 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 21:52:25 -0400 Subject: [PATCH 07/10] Wrong filename --- functions/util/FBAuth.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 functions/util/FBAuth.js diff --git a/functions/util/FBAuth.js b/functions/util/FBAuth.js deleted file mode 100644 index e9ebf97..0000000 --- a/functions/util/FBAuth.js +++ /dev/null @@ -1,35 +0,0 @@ -const {admin, db} = require('./admin'); - -// Acts as a middleman between the client and any function that you use it with -// The function will only execute if the user is logged in, or rather, they have -// a valid token -module.exports = (req, resp, next) => { - let idToken; - - // Checking that the token exists in the header of the request - if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) { - idToken = req.headers.authorization.split('Bearer ')[1]; - } else { - console.error('No token found'); - return resp.status(403).json({ error: 'Unauthorized' }); - } - - // Checking that the token is valid in firebase - admin.auth().verifyIdToken(idToken) - .then(decodedToken => { - req.user = decodedToken; - console.log(decodedToken); - return db.collection('users') - .where('userId', '==', req.user.uid) - .limit(1) - .get(); - }) - .then(data => { - req.user.handle = data.docs[0].data().handle; // Save username - return next(); - }) - .catch(err => { - console.error('Error verifying token', err); - return res.status(403).json(err); - }) -}; \ No newline at end of file From dc498a3a1d2959acbbcf1685f3f6b1142a13250c Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 21:56:59 -0400 Subject: [PATCH 08/10] Add comments to fbAuth.js --- functions/util/fbAuth.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/functions/util/fbAuth.js b/functions/util/fbAuth.js index 24369a6..04d71ed 100644 --- a/functions/util/fbAuth.js +++ b/functions/util/fbAuth.js @@ -1,7 +1,12 @@ const { admin, db } = require('./admin'); +// Acts as a middleman between the client and any function that you use it with +// The function will only execute if the user is logged in, or rather, they have +// a valid token module.exports = (req, res, next) => { let idToken; + + // Checking that the token exists in the header of the request if (req.headers.authorization) { idToken = req.headers.authorization; } else { @@ -9,6 +14,7 @@ module.exports = (req, res, next) => { return res.status(403).json({ error: 'Unauthorized'}); } + // Checking that the token is valid in firebase admin.auth().verifyIdToken(idToken) .then((decodedToken) => { req.user = decodedToken; @@ -17,7 +23,7 @@ module.exports = (req, res, next) => { .get(); }) .then((data) => { - req.user.handle = data.docs[0].data().handle; + req.user.handle = data.docs[0].data().handle; // Save username req.user.imageUrl = data.docs[0].data().imageUrl; return next(); }) @@ -25,4 +31,4 @@ module.exports = (req, res, next) => { console.error('Error while verifying token ', err); return res.status(403).json(err); }); -}; \ No newline at end of file +}; From 929216c05e75ef2aeac37ba081b215a06ba0557d Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 22:06:51 -0400 Subject: [PATCH 09/10] Changed naming from FBAuth to fbAuth --- functions/index.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/functions/index.js b/functions/index.js index e5a9052..e764338 100644 --- a/functions/index.js +++ b/functions/index.js @@ -4,22 +4,21 @@ const app = require('express')(); const cors = require('cors'); app.use(cors()); -const FBAuth = require('./util/FBAuth'); +const fbAuth = require('./util/fbAuth'); /*------------------------------------------------------------------* * handlers/users.js * *------------------------------------------------------------------*/ const {getUserDetails, getProfileInfo, updateProfileInfo} = require('./handlers/users'); -// Returns all data in the users collection app.get('/getUser/:handle', getUserDetails); // Returns all profile data of the currently logged in user -// TODO: Add FBAuth +// TODO: Add fbAuth app.get('/getProfileInfo', getProfileInfo); // Updates the currently logged in user's profile information -// TODO: Add FBAuth +// TODO: Add fbAuth app.post('/updateProfileInfo', updateProfileInfo); /*------------------------------------------------------------------* @@ -28,7 +27,7 @@ app.post('/updateProfileInfo', updateProfileInfo); const {putPost} = require('./handlers/post'); // Adds one post to the database -app.post('/putPost', FBAuth, putPost); +app.post('/putPost', fbAuth, putPost); exports.api = functions.https.onRequest(app); \ No newline at end of file From 90442fe3cd885b5b7e48fc2ba699f3c4642ab3b3 Mon Sep 17 00:00:00 2001 From: Clayton Wilson Date: Sun, 29 Sep 2019 23:59:36 -0400 Subject: [PATCH 10/10] Fixing 'firebase deploy' errors and warnings --- functions/handlers/post.js | 4 ++-- functions/handlers/users.js | 6 +++++- functions/util/validator.js | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/functions/handlers/post.js b/functions/handlers/post.js index 71b97eb..e2d195b 100644 --- a/functions/handlers/post.js +++ b/functions/handlers/post.js @@ -17,11 +17,11 @@ exports.putPost = (req, res) => { .then((doc) => { const resPost = newPost; resPost.postId = doc.id; - res.json(resPost); + return res.status(200).json(resPost); }) .catch((err) => { - res.status(500).json({ error: 'something is wrong'}); console.error(err); + return res.status(500).json({ error: 'something is wrong'}); }); }; diff --git a/functions/handlers/users.js b/functions/handlers/users.js index 240bfdd..c165ae0 100644 --- a/functions/handlers/users.js +++ b/functions/handlers/users.js @@ -9,6 +9,10 @@ exports.getProfileInfo = (req, res) => { db.collection('users').doc(req.user.handle).get() .then((data) => { return res.status(200).json(data.data()); + }) + .catch((err) => { + console.error(err); + return res.status(500).json(err); }); }; @@ -41,7 +45,7 @@ exports.updateProfileInfo = (req, res) => { exports.getUserDetails = (req, res) => { let userData = {}; - db.doc('/users/${req.params.handle}').get().then((doc) => { + db.doc(`/users/${req.params.handle}`).get().then((doc) => { if (doc.exists) { userData.user = doc.data(); return db.collection('post').where('userHandle', '==', req.params.handle) diff --git a/functions/util/validator.js b/functions/util/validator.js index ca29c6c..3ab6226 100644 --- a/functions/util/validator.js +++ b/functions/util/validator.js @@ -4,7 +4,7 @@ const isEmpty = (str) => { }; const isEmail = (str) => { - const emailRegEx = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + const emailRegEx = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (str.match(emailRegEx)) return true; else return false; }