diff --git a/models/ExampleModel.js b/models/ExampleModel.js new file mode 100644 index 0000000..7a96407 --- /dev/null +++ b/models/ExampleModel.js @@ -0,0 +1,44 @@ +const db = require('../db/Pool'); + + +module.exports = class ExampleModel { + async create() { + + } + + async update() { + + } + + async findOneByEmail(email) { + try { + const statement = `SELECT * FROM example WHERE email = $1`; + const values = [email]; + const result = await db.query(statement, values); + + if (result.rows?.length) { + return result.rows[0]; + } + + return null; + } catch(e) { + throw new Error(e); + } + } + + async findAll() { + try { + const statement = "SELECT * FROM example"; + const result = await db.query(statement); + + if (result.rows?.length) return result.rows; + return null; + } catch(e) { + throw new Error(e); + } + } + + async deleteOne() { + + } +} \ No newline at end of file diff --git a/models/UserModel.js b/models/UserModel.js new file mode 100644 index 0000000..003156b --- /dev/null +++ b/models/UserModel.js @@ -0,0 +1,65 @@ +const db = require('../db/Pool'); +const pgp = require('pg-promise')({ capSQL: true }); + +module.exports = class UserModel { + async create(data) { + try { + const statement = pgp.helpers.insert(data, null, 'users') + 'RETURNING *'; + const result = await db.query(statement); + + if (result.rows.length) { + return result.rows[0]; + } + + return null; + } catch(e) { + throw new Error(e); + } + } + + async update(data) { + try { + const { id, ...params } = data; + + const condition = pgp.as.format('WHERE id = ${id} RETURNING *', { id }); + const statement = pgp.helpers.update(params, null, 'users') + condition; + const result = await db.query(statement); + if (result.rows.length) return result.rows[0]; + return null; + } catch(e) { + throw new Error(e); + } + } + + async findOneById(id) { + try { + const statement = `SELECT * FROM users WHERE id = $1`; + const values = [id]; + const result = await db.query(statement, values); + + if (result.rows?.length) { + return result.rows[0]; + } + + return null; + } catch(e) { + throw new Error(e); + } + } + + async findOneByEmail(email) { + try { + const statement = 'SELECT * FROM users WHERE email = $1'; + const values = [email]; + const result = await db.query(statement, values); + + if (result.rows.length) { + return result.rows[0]; + } + + return null; + } catch(e) { + throw new Error(e); + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 59ee9fe..6c669ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,10 +17,12 @@ "express": "^4.17.3", "express-session": "^1.17.3", "helmet": "^5.1.0", + "http-errors": "^2.0.0", "js-yaml": "^4.1.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.7.3", + "pg-promise": "^10.12.0", "swagger-ui-express": "^4.4.0", "uuid": "^8.3.2" }, @@ -169,6 +171,14 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/assert-options": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz", + "integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1308,14 +1318,14 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "node_modules/pg": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz", - "integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", + "pg-pool": "^3.5.2", "pg-protocol": "^1.5.0", "pg-types": "^2.1.0", "pgpass": "1.x" @@ -1324,7 +1334,7 @@ "node": ">= 8.0.0" }, "peerDependencies": { - "pg-native": ">=2.0.0" + "pg-native": ">=3.0.1" }, "peerDependenciesMeta": { "pg-native": { @@ -1345,14 +1355,36 @@ "node": ">=4.0.0" } }, + "node_modules/pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==", + "engines": { + "node": ">=8.0" + } + }, "node_modules/pg-pool": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz", - "integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", "peerDependencies": { "pg": ">=8.0" } }, + "node_modules/pg-promise": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.0.tgz", + "integrity": "sha512-7uN64iEHrhtRcOaU/AT3925S20JzQJG2nWVK2IUz5SlhB1eNdkXjAYoQtei+5kLJo81mOWcFq7x9J9VRldp0ig==", + "dependencies": { + "assert-options": "0.7.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + }, + "engines": { + "node": ">=12.0" + } + }, "node_modules/pg-protocol": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", @@ -1697,6 +1729,14 @@ "semver": "bin/semver.js" } }, + "node_modules/spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==", + "engines": { + "node": ">=4.5" + } + }, "node_modules/split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", @@ -2065,6 +2105,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "assert-options": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.7.0.tgz", + "integrity": "sha512-7q9uNH/Dh8gFgpIIb9ja8PJEWA5AQy3xnBC8jtKs8K/gNVCr1K6kIvlm59HUyYgvM7oEDoLzGgPcGd9FqhtXEQ==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2907,14 +2952,14 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "pg": { - "version": "8.7.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.7.3.tgz", - "integrity": "sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", "requires": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.5.0", - "pg-pool": "^3.5.1", + "pg-pool": "^3.5.2", "pg-protocol": "^1.5.0", "pg-types": "^2.1.0", "pgpass": "1.x" @@ -2930,12 +2975,28 @@ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" }, + "pg-minify": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/pg-minify/-/pg-minify-1.6.2.tgz", + "integrity": "sha512-1KdmFGGTP6jplJoI8MfvRlfvMiyBivMRP7/ffh4a11RUFJ7kC2J0ZHlipoKiH/1hz+DVgceon9U2qbaHpPeyPg==" + }, "pg-pool": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz", - "integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", "requires": {} }, + "pg-promise": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.12.0.tgz", + "integrity": "sha512-7uN64iEHrhtRcOaU/AT3925S20JzQJG2nWVK2IUz5SlhB1eNdkXjAYoQtei+5kLJo81mOWcFq7x9J9VRldp0ig==", + "requires": { + "assert-options": "0.7.0", + "pg": "8.8.0", + "pg-minify": "1.6.2", + "spex": "3.2.0" + } + }, "pg-protocol": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", @@ -3189,6 +3250,11 @@ } } }, + "spex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spex/-/spex-3.2.0.tgz", + "integrity": "sha512-9srjJM7NaymrpwMHvSmpDeIK5GoRMX/Tq0E8aOlDPS54dDnDUIp30DrP9SphMPEETDLzEM9+4qo+KipmbtPecg==" + }, "split2": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", diff --git a/package.json b/package.json index 4dc2994..d7fbe13 100644 --- a/package.json +++ b/package.json @@ -23,10 +23,12 @@ "express": "^4.17.3", "express-session": "^1.17.3", "helmet": "^5.1.0", + "http-errors": "^2.0.0", "js-yaml": "^4.1.0", "passport": "^0.6.0", "passport-local": "^1.0.0", "pg": "^8.7.3", + "pg-promise": "^10.12.0", "swagger-ui-express": "^4.4.0", "uuid": "^8.3.2" }, diff --git a/routes/API.js b/routes/API.js index d8a91c6..b8ce8a4 100644 --- a/routes/API.js +++ b/routes/API.js @@ -3,7 +3,7 @@ const productsRouter = require('./products'); const registerRouter = require('./register'); const loginRouter = require('./login'); -module.exports = (app, passport) => { +module.exports = async (app, passport) => { loginRouter(app, passport); productsRouter(app); registerRouter(app); diff --git a/services/Auth.js b/services/Auth.js index 5be780e..e7d623b 100644 --- a/services/Auth.js +++ b/services/Auth.js @@ -1,3 +1,10 @@ +/** + * IMPORTANT: + * THIS VERSION OF THE AUTH SERVICE IS DEPRECATED + * THESE SERVICES WILL BE MIGRATED TO AuthService.js + * WITHIN THE SAME DIRECTORY +**/ + const { connect } = require('../db/Pool'); const bcrypt = require('bcrypt'); diff --git a/services/AuthService.js b/services/AuthService.js new file mode 100644 index 0000000..d090fa1 --- /dev/null +++ b/services/AuthService.js @@ -0,0 +1,45 @@ +const bcrypt = require('bcrypt'); +const createError = require('http-errors'); +const UserModel = require('../models/UserModel'); +const UserInstance = new UserModel(); + +module.exports = class AuthService { + async register(data) { + const { email } = data; + + try { + const user = await UserInstance.findOneById(email); + if (user) { + throw createError(409, 'Email already in use'); + } + + return await UserInstance.create(data); + } catch(e) { + throw new Error(e); + } + } + + // yet to be implemented + async login(data) { + const { email, password } = data; + + try { + const user = await UserInstance.findOneByEmail(email); + if (!user) throw createError(401, 'Incorrect email or password'); + + const match = await bcrypt.compare(user.password, password); + if (!match) throw createError(401, 'Incorrect email or password'); + + return user; + } catch(e) { + throw createError(500, e); + } + } + + // yet to be implemented + async deleteOne(data) { + return data; + } + + // TO IMPLEMENT: google, facebook passport strategies +} \ No newline at end of file diff --git a/services/ExampleService.js b/services/ExampleService.js new file mode 100644 index 0000000..6e62b13 --- /dev/null +++ b/services/ExampleService.js @@ -0,0 +1,5 @@ +const pgp = require('pg-promise')({ capSQL: true }); + +module.exports = class ExampleService { + +} \ No newline at end of file diff --git a/services/UserService.js b/services/UserService.js new file mode 100644 index 0000000..89880bb --- /dev/null +++ b/services/UserService.js @@ -0,0 +1,26 @@ +const createError = require('http-errors'); +const UserModel = require('../models/UserModel'); +const UserInstance = new UserModel(); + +module.exports = class UserService { + async get(data) { + const { id } = data; + + try { + const user = await UserInstance.findOneById(id); + if (!user) throw createError(404, 'User not found'); + return user; + } catch(e) { + throw new Error(e); + } + } + + async update(data) { + try { + const user = await UserInstance.update(data); + return user; + } catch(e) { + throw new Error(e); + } + } +} \ No newline at end of file