From d6397626baaed2fbb15ad7782090784bb22974c2 Mon Sep 17 00:00:00 2001 From: Nuno Das Neves Date: Sat, 13 Oct 2018 16:32:00 +1100 Subject: [PATCH] S8 signup flow basic profile (#213) * signup now in two stages; create firebase account, and create profile * signup flow more logical now ... handles most edge cases pretty well * basic profile page - still buggy, non functional update profile button * local default picture * update user backend added... no tests * lint errors fixed * working profile update * fixed textarea stuff, watcher to fix refresh profile problem * profile picture update added * working password reset from login form * nice error message for non-unique username * made hasProfile dependent on isLoggedIn to alleviate some potential bad * removed TODO * fixed post review/question bug when logged in * loading state for nav, split auth and profile get into two bits * reverted some stuff ... working again * better auth code, profile cache, global loading spinner in nav, reroutes * lint * page size fixaroo * I'v messed something up * protecting routes with guards * fixed some PR things * smol fixes * removed preAuth route check - not needed rn * smol fix to login page reroute --- backend/src/controllers/course.js | 20 +- backend/src/controllers/question.js | 8 +- backend/src/controllers/review.js | 8 +- backend/src/controllers/user.js | 27 ++- backend/src/models/constants.js | 3 + backend/src/models/db/tables.js | 9 +- backend/src/models/question.js | 2 +- backend/src/models/user.js | 23 +- backend/src/routes/user.js | 17 +- backend/src/utils/helpers.js | 9 +- backend/tests/models/user.js | 45 ++++ frontend/public/defaultpicture.png | Bin 0 -> 4957 bytes frontend/src/App.vue | 17 ++ frontend/src/components/AppNavBar.vue | 33 ++- .../questions-answers/QuestionForm.vue | 17 -- .../components/reviews-replies/ReviewForm.vue | 17 -- frontend/src/router/index.js | 18 ++ frontend/src/{router.js => router/routes.js} | 53 +++-- frontend/src/store/auth/index.js | 201 ++++++++++++++---- frontend/src/store/root.js | 20 +- frontend/src/utils/api/auth.js | 15 +- frontend/src/views/CreateProfile.vue | 53 +++++ frontend/src/views/ForgotPassword.vue | 25 ++- frontend/src/views/Login.vue | 21 +- frontend/src/views/Onboarding.vue | 4 - frontend/src/views/Profile.vue | 133 +++++++++++- frontend/src/views/SignUp.vue | 19 +- 27 files changed, 622 insertions(+), 195 deletions(-) create mode 100644 backend/tests/models/user.js create mode 100644 frontend/public/defaultpicture.png create mode 100644 frontend/src/router/index.js rename frontend/src/{router.js => router/routes.js} (65%) create mode 100644 frontend/src/views/CreateProfile.vue delete mode 100644 frontend/src/views/Onboarding.vue diff --git a/backend/src/controllers/course.js b/backend/src/controllers/course.js index 047f0938..d90abe89 100644 --- a/backend/src/controllers/course.js +++ b/backend/src/controllers/course.js @@ -1,4 +1,4 @@ -const { ANONYMOUS } = require('../models/constants') +const { ANONYMOUS, PAGE_SIZE } = require('../models/constants') const courseModel = require('../models/course')() const questionModel = require('../models/question')() const reviewModel = require('../models/review')() @@ -19,9 +19,9 @@ exports.getCourse = function ({ params }, res) { /* Get all questions for a course */ exports.getCourseQuestions = function ({ params, query }, res) { - let p = parseInt(query.p) - const pageNumber = p || ANONYMOUS - const pageSize = 10 + const pageNumber = parseInt(query.p) || 1 + // TODO get page size from query + const pageSize = PAGE_SIZE const getCourseQuestions = Promise.all([ questionModel.getQuestions(params.code, pageNumber, pageSize), @@ -45,9 +45,9 @@ exports.getCourseQuestions = function ({ params, query }, res) { /* Get all reviews for a course */ exports.getCourseReviews = function ({ params, query }, res) { - let p = parseInt(query.p) - const pageNumber = p || ANONYMOUS - const pageSize = 10 + const pageNumber = parseInt(query.p) || 1 + // TODO get page size from query + const pageSize = PAGE_SIZE const getCourseReviews = new Promise((resolve, reject) => { Promise.all([ @@ -59,7 +59,7 @@ exports.getCourseReviews = function ({ params, query }, res) { 'meta': { 'curr': pageNumber, 'last': lastPage, - 'pageSize': pageSize + 'pageSize':pageSize }, 'data': values[0] }) @@ -71,14 +71,14 @@ exports.getCourseReviews = function ({ params, query }, res) { } exports.postQuestion = function ({ user, params, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS responseHandler(questionModel.postQuestion(params.code, body), res) .catch(errorHandler(res)) } /* POST new review */ exports.postReview = function ({ user, params, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS responseHandler(reviewModel.postReview(params.code, body), res) .catch(errorHandler(res)) } diff --git a/backend/src/controllers/question.js b/backend/src/controllers/question.js index b7132528..3572feb9 100644 --- a/backend/src/controllers/question.js +++ b/backend/src/controllers/question.js @@ -42,7 +42,7 @@ exports.getQuestionAnswers = function ({ params, query }, res) { /* POST new answer. */ exports.postAnswer = function ({ user, params, query, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS commentModel.postComment({ questionID: params.id }, body) .then(exports.getQuestionAnswers({ params, query }, res)) .catch(errorHandler(res)) @@ -56,7 +56,7 @@ exports.getQuestionLikes = function ({ params }, res) { /* PUT updated question likes value */ exports.putQuestionLikes = function ({ user, params, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS responseHandler(likesModel.putLikes({ type: 'question', ...params, ...body }), res) .catch(errorHandler(res)) } @@ -69,7 +69,7 @@ exports.getAnswerLikes = function ({ params }, res) { /* PUT updated answer likes value */ exports.putAnswerLikes = function ({ user, params, body, query }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS likesModel.putLikes({ type: 'answer', id: params.answerID, ...body }) .then(exports.getQuestionAnswers({ params, query }, res)) -} \ No newline at end of file +} diff --git a/backend/src/controllers/review.js b/backend/src/controllers/review.js index 9e383947..4ebaf132 100644 --- a/backend/src/controllers/review.js +++ b/backend/src/controllers/review.js @@ -41,7 +41,7 @@ exports.getReviewComments = function ({ params, query }, res) { /* POST new comment. */ exports.postComment = function ({ user, params, query, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS commentModel.postComment({ reviewID: params.id }, body) .then(exports.getReviewComments({ params, query }, res)) .catch(errorHandler(res)) @@ -55,7 +55,7 @@ exports.getReviewLikes = function ({ params }, res) { /* PUT updated likes value */ exports.putReviewLikes = function ({ user, params, body }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS responseHandler(likesModel.putLikes({ type: 'review', ...params, ...body }), res) .catch(errorHandler(res)) } @@ -68,7 +68,7 @@ exports.getReplyLikes = function ({ params }, res) { /* PUT updated reply likes value */ exports.putReplyLikes = function ({ user, params, body, query }, res) { - body.userID = user || ANONYMOUS + body.userID = user && user.id || ANONYMOUS likesModel.putLikes({ type: 'reply', id: params.replyID, ...body }) .then(exports.getReviewComments({ params, query }, res)) -} \ No newline at end of file +} diff --git a/backend/src/controllers/user.js b/backend/src/controllers/user.js index 03f6fb3f..f9062976 100644 --- a/backend/src/controllers/user.js +++ b/backend/src/controllers/user.js @@ -3,13 +3,9 @@ const userModel = require('../models/user')() const { responseHandler } = require('../utils/helpers') /* Get data for a specific user */ -exports.getUser = function(_, res) { - return res.json({ - userID: 1, - firstName: 'Walker', - lastName: 'Francis', - email: 'alnuno-das-hinds@gmail.com' - }) +exports.getUser = function({id}, res) { + return responseHandler(userModel.getPublicProfile(id), res) + .catch(errorHandler(res)) } /** @@ -17,15 +13,16 @@ exports.getUser = function(_, res) { * provide frontend with any user specific data */ exports.getSelf = function(req, res) { - const errorResponse = errorHandler(res) - if (!req.user) { - return errorResponse({ message: 'Invalid Credentials' }) - } - // fine for now, should include profile data - return res.json(req.user) + return responseHandler(userModel.getProfile(req.user.id), res) + .catch(errorHandler(res)) +} + +exports.createUser = function({ authorized: { email, uid }, body: { displayName } }, res) { + return responseHandler(userModel.createUser({ email, uid, displayName }), res) + .catch(errorHandler(res)) } -exports.createUser = function({ authorized }, res) { - return responseHandler(userModel.createUser(authorized), res) +exports.updateUser = function({ user: { id }, body: { degree, gradYear, description, picture } }, res) { + return responseHandler(userModel.updateUser(id, { degree, gradYear, description, picture }), res) .catch(errorHandler(res)) } diff --git a/backend/src/models/constants.js b/backend/src/models/constants.js index f38481ba..6862d2b1 100644 --- a/backend/src/models/constants.js +++ b/backend/src/models/constants.js @@ -8,3 +8,6 @@ exports.MIN_ENJOY = 1 exports.MAX_ENJOY = 5 exports.MIN_OPTION = 0 exports.MAX_OPTION = 3 + +// Paging Constants +exports.PAGE_SIZE = 10 diff --git a/backend/src/models/db/tables.js b/backend/src/models/db/tables.js index d7263d33..387e816a 100644 --- a/backend/src/models/db/tables.js +++ b/backend/src/models/db/tables.js @@ -6,14 +6,15 @@ function createUserTable (db) { return new Promise((resolve, reject) => { db.run(`CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, - uid TEXT NOT NULL, - displayName TEXT DEFAULT 'ANON', + uid TEXT UNIQUE NOT NULL, + displayName TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL, joined TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, reputation INTEGER DEFAULT '0', degree TEXT, - gradYear TIMESTAMP DEFAULT '2018', - description TEXT + gradYear TIMESTAMP, + description TEXT, + picture TEXT )`, (err) => err ? reject(err) : resolve('Created User Table')) }) diff --git a/backend/src/models/question.js b/backend/src/models/question.js index 018e3140..8a354e9e 100644 --- a/backend/src/models/question.js +++ b/backend/src/models/question.js @@ -29,7 +29,7 @@ class Question { /** * Gets the total number of questions for a course - * @param {string} code The code of the course + * @param {string} code The code of the course duh * @returns {object} */ getQuestionCount(code) { diff --git a/backend/src/models/user.js b/backend/src/models/user.js index 499dd220..6678eaf8 100644 --- a/backend/src/models/user.js +++ b/backend/src/models/user.js @@ -6,12 +6,12 @@ class User { } /** - * TODO Return specialsed information for auth'd user + * Return specialised information for auth'd user * @param {string} id The id of the auth'd user */ getProfile(id) { return this.db - .query('SELECT * FROM user WHERE id=?', [id]) + .query('SELECT id, email, displayName, degree, gradYear, description, picture, joined FROM user WHERE id=?', [id]) } /** @@ -20,13 +20,13 @@ class User { * @param {number} id Required id param. * @returns {object} */ - getUser(id) { + getPublicProfile(id) { return this.db - .query('SELECT * FROM user WHERE id=?', [id]) + .query('SELECT id, displayName, degree, gradYear, description, picture, joined FROM user WHERE id=?', [id]) } /** - * Alternative getter to get the relevant user's details + * Get all users details by UID. Used by authentication system * @param {uid} uid uid string * @returns {object} user object */ @@ -43,6 +43,19 @@ class User { return this.db .insert('user', { displayName, email, uid }) .then(id => this.getProfile(id)) + .catch(error => { + // kinda hacky + if (error.errno === 19 && error.message.includes("displayName")) { + throw(Error("That display name is taken! Sorry!")) + } + throw(error) + }) + } + + updateUser(id, data) { + return this.db + .update('user', data, { id }) + .then(() => this.getProfile(id)) } } diff --git a/backend/src/routes/user.js b/backend/src/routes/user.js index e17bccc2..428b7d5f 100644 --- a/backend/src/routes/user.js +++ b/backend/src/routes/user.js @@ -1,20 +1,21 @@ const express = require('express') const router = express.Router() -const { isAuthorized } = require('../utils/helpers') -const { getSelf, getUser, createUser } = require('../controllers/user') +const { isAuthorized, isFirebaseAuthorized } = require('../utils/helpers') +const { getSelf, getUser, createUser, updateUser } = require('../controllers/user') -/* Get data for a specific user */ +/* Get public data for a specific user */ router.get('/:id', getUser) /* no valid jwt then don't allow user to be created */ +router.use(isFirebaseAuthorized) +router.post('/', createUser) + +/* full auth check */ router.use(isAuthorized) -/** - * Both of these require an authorization header - * provide frontend with any user specific data - */ +/* provide frontend with any user specific data */ router.get('/', getSelf) +router.put('/', updateUser) -router.post('/', createUser) module.exports = router diff --git a/backend/src/utils/helpers.js b/backend/src/utils/helpers.js index 4233bb9c..06d1455c 100644 --- a/backend/src/utils/helpers.js +++ b/backend/src/utils/helpers.js @@ -11,12 +11,19 @@ exports.responseHandler = function(fn, response) { exports.toLowerCase = str => str.toLowerCase() -exports.isAuthorized = function(req, res, next) { +exports.isFirebaseAuthorized = function(req, res, next) { if (!req.authorized) { return res.status(401).json({ code: 401, message: 'Unauthorized' }) } next() } +exports.isAuthorized = function(req, res, next) { + if (!req.user) { + return res.status(403).json({ code: 403, message: 'No user profile' }) + } + next() +} + /** * Convert a string to utf8 diff --git a/backend/tests/models/user.js b/backend/tests/models/user.js new file mode 100644 index 00000000..43ea77fb --- /dev/null +++ b/backend/tests/models/user.js @@ -0,0 +1,45 @@ +const { expect } = require('chai') +// use an actual database +const userModel = require('../../src/models/user')(require('../../src/models/db')) + +describe('User Model', () => { + + it('compiles', function () { + expect(userModel) + }) + + describe('User creation and retrieval', () => { + const addUserObj = { displayName: 'mickey', email: 'potato@potatoman.potato', uid: '1234' } + const retrieveUserObj = { email: addUserObj.email, displayName: addUserObj.displayName, degree:null, gradYear:null, description:null, picture:null } + it('it creates a user and retrieves the profile', () => { + return userModel + .createUser(addUserObj) + .then(({ displayName, email, id }) => { + expect(displayName).to.equal(addUserObj.displayName) + expect(email).to.equal(addUserObj.email) + return userModel.getProfile(id) + }) + .then(({ email, displayName, degree, gradYear, description, picture }) => { + const profile = { email, displayName, degree, gradYear, description, picture } + expect(profile).to.deep.equal(retrieveUserObj) + }) + }) + }) + + describe('User update', () => { + const credsObj = { displayName: 'mickeyMoo', email: 'tomatoboy@hotmail.zomg' } + const addUserObj = { ...credsObj, uid: '4321' } + const updateObj = { degree: "BS in CS", gradYear: 2026, description: "testu", picture: "baaaaaa" } + it('it updates the profile correctly', () => { + return userModel + .createUser(addUserObj) + .then(({ id, description }) => { + return userModel.updateUser(id, updateObj) + }) + .then(({ email, displayName, degree, gradYear, description, picture }) => { + const profile = { email, displayName, degree, gradYear, description, picture } + expect(profile).to.deep.equal({ ...credsObj, ...updateObj }) + }) + }) + }) +}) diff --git a/frontend/public/defaultpicture.png b/frontend/public/defaultpicture.png new file mode 100644 index 0000000000000000000000000000000000000000..eda26dd39cf136dd561d2b773517f356f846cdae GIT binary patch literal 4957 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 KLk|z0z4&hd$f$_` literal 0 HcmV?d00001 diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 386c0385..5b86d104 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -131,4 +131,21 @@ input, textarea { border: none; } +textarea { + border: var(--border); + border-radius: 2px; + font: inherit; + resize: none; + padding: 10px; + outline: none; + margin: 10px 0px; + width: calc(100% - 20px); + height: 100px; + transition: 0.2s border ease-in-out; +} + +textarea:focus, textarea:active { + border: 1px solid #acc; +} + diff --git a/frontend/src/components/AppNavBar.vue b/frontend/src/components/AppNavBar.vue index d17374c8..1fb83573 100644 --- a/frontend/src/components/AppNavBar.vue +++ b/frontend/src/components/AppNavBar.vue @@ -1,27 +1,40 @@ diff --git a/frontend/src/views/ForgotPassword.vue b/frontend/src/views/ForgotPassword.vue index 523df71a..4f1d0007 100644 --- a/frontend/src/views/ForgotPassword.vue +++ b/frontend/src/views/ForgotPassword.vue @@ -1,18 +1,20 @@ + + diff --git a/frontend/src/views/SignUp.vue b/frontend/src/views/SignUp.vue index 6b6456b4..3f601b55 100644 --- a/frontend/src/views/SignUp.vue +++ b/frontend/src/views/SignUp.vue @@ -4,13 +4,12 @@ :title="'Sign Up'" :buttonText="'Sign Up'" :error="error" - :clickHandler="clickHandler" + :clickHandler="createAccount" :link="{ text: 'Already have an account?', name: 'Login' }" > - @@ -33,14 +32,20 @@ export default { }, components: { AppAuthForm, AuthInput }, computed: { - ...mapGetters('auth', [ 'error', 'loading' ]) + ...mapGetters('auth', [ 'loading', 'error', 'isFirebaseAuthorised', 'isLoggedIn', 'hasProfile' ]) + }, + // reroute whenever auth loading state changes + watch: { + loading() { this.reroute() } }, methods: { - clickHandler() { + createAccount() { const { email, password } = this - this.$store.dispatch('auth/signUp', { email, password }) - .then(() => this.$router.push('/')) - .catch(e => {}) + this.$store.dispatch('auth/createAccount', { email, password }) + }, + reroute() { + if (this.isLoggedIn) this.$router.push('/') + if (this.isFirebaseAuthorised && !this.hasProfile) this.$router.push('/create-profile') } }, created() {