From 9517c4864aef776405b4ec498fd55b7cd9ea2bf5 Mon Sep 17 00:00:00 2001 From: cemn <127931228+fridge7809@users.noreply.github.com> Date: Sat, 17 Feb 2024 12:04:46 +0100 Subject: [PATCH 1/2] implement message flashes --- app.js | 13 +++--- package-lock.json | 48 ++++++++++++++++----- package.json | 1 + routes/login.js | 102 ++++++++++++++++++++++++--------------------- routes/register.js | 27 +++++++++++- routes/timeline.js | 9 ++++ views/layout.pug | 8 ++++ 7 files changed, 142 insertions(+), 66 deletions(-) diff --git a/app.js b/app.js index ab4097b..1a8caea 100644 --- a/app.js +++ b/app.js @@ -11,6 +11,7 @@ const path = require('path'); // Core Node.js module to handle and transform fil const cookieParser = require('cookie-parser'); // Middleware to parse and set cookies in request objects const logger = require('morgan'); // HTTP request logger middleware for node.js const session = require('express-session'); +var flash = require('connect-flash'); // DB Modules const sqlite3 = require('sqlite3').verbose(); @@ -39,6 +40,7 @@ app.use(express.json()); // Parses incoming requests with JSON payloads, making app.use(express.urlencoded({ extended: false })); // Parses incoming requests with URL-encoded payloads, useful for form submissions app.use(cookieParser()); // Parse Cookie header and populate req.cookies with an object keyed by cookie names app.use(express.static(path.join(__dirname, 'public'))); // Serve static files (images, CSS, JavaScript) from the 'public' directory +app.use(flash()); app.use(session({ secret: 'devving-and-opssing', @@ -51,12 +53,6 @@ app.use('/login', loginRouter); // Use the login router for requests to '/login' app.use('/register', registerRouter); // Use the register router for requests to '/register' app.use('/', timelineRouter); // Use the public timeline router for requests to '/' - -// Catch 404 and forward to error handler -app.use(function (req, res, next) { - next(createError(404)); // If no middleware has responded by now, it means a 404 error should be generated and forwarded to the error handler -}); - // Error handler middleware app.use(function (err, req, res, next) { // Set locals, providing error details only in development environment @@ -68,5 +64,10 @@ app.use(function (err, req, res, next) { res.render('error'); // Uses the view engine to render the error page }); +app.use(function (req, res, next) { + res.success_messages = req.flash('success'); + res.error_messages = req.flash('error'); + next(); +}) // Export the app for use by other modules (like the server starter script) module.exports = app; diff --git a/package-lock.json b/package-lock.json index 0f88948..6899e13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "bcrypt": "^5.1.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "^4.18.2", @@ -644,6 +645,14 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1528,11 +1537,18 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "optional": true + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "optional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -1640,6 +1656,12 @@ "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==" }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "optional": true + }, "node_modules/jstransformer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", @@ -2774,16 +2796,16 @@ } }, "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", "optional": true, "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, @@ -2824,6 +2846,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "optional": true }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "optional": true + }, "node_modules/sqlite3": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", diff --git a/package.json b/package.json index 2382a9e..5e0bc7a 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "dependencies": { "bcrypt": "^5.1.1", + "connect-flash": "^0.1.1", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "^4.18.2", diff --git a/routes/login.js b/routes/login.js index 7b017d3..57d5883 100644 --- a/routes/login.js +++ b/routes/login.js @@ -5,66 +5,72 @@ const sqlite3 = require('sqlite3').verbose(); var session = require('express-session'); router.use(session({ - secret: 'devving-and-opssing', - resave: false, - saveUninitialized: true + secret: 'devving-and-opssing', + resave: false, + saveUninitialized: true })); +router.use(function (req, res, next) { + res.locals.success_messages = req.flash('success'); + res.locals.error_messages = req.flash('error'); + next(); +}) + // Database connection let db = new sqlite3.Database('./db/minitwit.db', sqlite3.OPEN_READWRITE, (err) => { - if (err) { - console.error(err.message); - } else { - console.log('Connected to the minitwit.db'); - } + if (err) { + console.error(err.message); + } else { + console.log('Connected to the minitwit.db'); + } }); /* GET login page. */ router.get('/', function (req, res) { - const g = { user: req.session.username }; - res.render('login', { title: 'Login', g: g }); + const g = { user: req.session.username }; + res.render('login', { title: 'Login', g: g }); }); -router.post('/', (req, res) => { - const { username, password } = req.body; +router.post('/', (req, res, next) => { + const { username, password } = req.body; - // Input validation - if (!username || !password) { - return res.status(400).send('Username and password are required'); - } + // Input validation + if (!username || !password) { + req.flash('error', 'Please enter username and password'); + return res.redirect('/login'); + } - // Check user in DB - const sql = 'SELECT * FROM user WHERE username = ?'; - db.get(sql, [username], (err, user) => { - if (err) { - console.error(err.message); - return res.status(500).send('Server error'); - } - if (!user) { - // User not found - return res.status(400).send('Invalid username or password'); - } else { - // Compare submitted password with hashed password in DB - bcrypt.compare(password, user.pw_hash, (err, result) => { - if (err) { - console.error(err.message); - return res.status(500).send('Server error'); - } - if (result) { - // Passwords match - req.session.username = { - id: user.user_id, - username: username - }; - console.log(req.session.username) - return res.redirect('/'); - } else { - // Passwords dont match - return res.status(400).send('Invalid username or password'); - } - }); - } - }); + // Check user in DB + const sql = 'SELECT * FROM user WHERE username = ?'; + db.get(sql, [username], (err, user) => { + if (err) { + console.error(err.message); + return res.status(500).send('Server error'); + } + if (!user) { + req.flash('error', 'Invalid username or password'); + res.redirect('/login'); + } else { + bcrypt.compare(password, user.pw_hash, (err, result) => { + if (err) { + console.error(err.message); + return res.status(500).send('Server error'); + } + if (result) { + req.session.username = { + id: user.user_id, + username: username + }; + console.log(req.session.username) + req.flash('success', 'You were logged in'); + return res.redirect('/'); + } else { + // Passwords dont match + return res.status(400).send('Invalid username or password'); + } + }); + } + }); }); module.exports = router; diff --git a/routes/register.js b/routes/register.js index 9d33b2a..c24427a 100644 --- a/routes/register.js +++ b/routes/register.js @@ -10,20 +10,43 @@ router.get('/', function (req, res, next) { res.render('register', { title: 'Register', g: g }); }); -router.post('/', async (req, res) => { +router.use(function (req, res, next) { + res.locals.success_messages = req.flash('success'); + res.locals.error_messages = req.flash('error'); + next(); +}) + +const validateEmail = (email) => { + return String(email) + .toLowerCase() + .match( + /^(([^<>()[\]\\.,;:\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,}))$/ + ); +}; + +router.post('/', async function (req, res, next) { const { username, email, password } = req.body; + const validEmail = validateEmail(email); + + if (!validEmail) { + req.flash('error', 'Please enter a valid email address') + return res.redirect('/register') + } + try { const emailExists = await userService.getUserIdByEmailIfExists(email); const usernameExists = await userService.getUserIdByUsernameIfExists(username); if (emailExists || usernameExists) { - return res.status(400).send('Username or password already exists'); + req.flash('error', 'User already exists'); + return res.redirect('/register') } const hashedPassword = await bcrypt.hash(password, 10); await userService.registerUser(username, email, hashedPassword); + req.flash('success', 'You were registered, please login') return res.redirect('/login'); } catch (err) { console.error(err); diff --git a/routes/timeline.js b/routes/timeline.js index 0aa6caa..e7e038f 100644 --- a/routes/timeline.js +++ b/routes/timeline.js @@ -11,6 +11,12 @@ router.use(session({ saveUninitialized: true })); +router.use(function (req, res, next) { + res.locals.success_messages = req.flash('success'); + res.locals.error_messages = req.flash('error'); + next(); +}) + const requireAuth = (req, res, next) => { if (req.session.username) { next(); @@ -46,6 +52,7 @@ router.post('/add_message', requireAuth, async function (req, res, next) { const messageContent = req.body.text; const currentDate = Math.floor(new Date().getTime() / 1000); await userService.addMessage(userId, messageContent, currentDate); + req.flash('success', 'Nice tweet!'); res.redirect('/') } catch (error) { console.log(error); @@ -159,6 +166,7 @@ router.get('/:username/follow', requireAuth, async function (req, res, next) { let messages = await userService.getMessagesByUserId(whom_id); const followed = await userService.followUser(who_id, whom_id); + req.flash('success', `You are now following ${whom_username}`) // TODO: implement flashes // res.redirect(`/${whom_username}`); res.render('timeline', { @@ -198,6 +206,7 @@ router.get('/:username/unfollow', requireAuth, async function (req, res, next) { let messages = await userService.getMessagesByUserId(whom_id); const followed = await userService.unfollowUser(who_id, whom_id); + req.flash('success', `You have unfollowed ${whom_username}`) // TODO: implement flashes // res.redirect(`/${whom_username}`); res.render('timeline', { diff --git a/views/layout.pug b/views/layout.pug index e29587a..0f6e89a 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -15,6 +15,14 @@ html a(href=('/public')) public timeline | a(href=('/register')) sign up | a(href=('/login')) sign in | + if success_messages + ul.flashes + each message in success_messages + li= message + if error_messages + ul.flashes + each message in error_messages + li= message div(class='body') block content div(class='footer') Maxitwit — An Express application From 375a94b1accaccf0bf3f03d0ebfefd147d8b23bf Mon Sep 17 00:00:00 2001 From: cemn <127931228+fridge7809@users.noreply.github.com> Date: Sat, 17 Feb 2024 15:34:06 +0100 Subject: [PATCH 2/2] add flash messages, init db on bootstrap --- app.js | 18 ++++--- db/database.js | 33 +++++++++++++ routes/login.js | 31 +++++++----- routes/register.js | 57 +++++++++++++++++++--- routes/timeline.js | 103 ++++++++++++---------------------------- services/userService.js | 45 +++++++----------- views/layout.pug | 2 +- views/timeline.pug | 2 +- 8 files changed, 163 insertions(+), 128 deletions(-) create mode 100644 db/database.js diff --git a/app.js b/app.js index 1a8caea..33f37bb 100644 --- a/app.js +++ b/app.js @@ -11,14 +11,18 @@ const path = require('path'); // Core Node.js module to handle and transform fil const cookieParser = require('cookie-parser'); // Middleware to parse and set cookies in request objects const logger = require('morgan'); // HTTP request logger middleware for node.js const session = require('express-session'); -var flash = require('connect-flash'); +const flash = require('connect-flash'); -// DB Modules -const sqlite3 = require('sqlite3').verbose(); -const db = new sqlite3.Database('./db/minitwit.db'); // todo This needs to be set up in an init function with error handling instead -const fs = require('fs'); -const sqlFilePath = path.join(__dirname, 'db', 'schema.sql'); -const sql = fs.readFileSync(sqlFilePath, 'utf8'); +// Initialize database schema +const db = require('./db/database'); + +db.initSchema() + .then(() => { + console.log('Database schema initialized successfully.'); + }) + .catch((err) => { + console.error('Error initializing database schema:', err); + }); // Import routers for different paths diff --git a/db/database.js b/db/database.js new file mode 100644 index 0000000..1e4080b --- /dev/null +++ b/db/database.js @@ -0,0 +1,33 @@ +const sqlite3 = require('sqlite3').verbose(); +const fs = require('fs'); +const path = require('path'); + +class Database { + constructor() { + if (!Database.instance) { + this.db = new sqlite3.Database('./db/minitwit.db'); + Database.instance = this; + } + return Database.instance; + } + + getDb() { + return this.db; + } + + async initSchema() { + const sqlFilePath = path.join(__dirname, 'schema.sql'); + const sql = fs.readFileSync(sqlFilePath, 'utf8'); + return new Promise((resolve, reject) => { + this.db.exec(sql, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } +} + +module.exports = new Database(); diff --git a/routes/login.js b/routes/login.js index 57d5883..8e04fdd 100644 --- a/routes/login.js +++ b/routes/login.js @@ -3,6 +3,7 @@ var router = express.Router(); const bcrypt = require('bcrypt'); const sqlite3 = require('sqlite3').verbose(); var session = require('express-session'); +const db = require('../db/database'); router.use(session({ secret: 'devving-and-opssing', @@ -16,18 +17,22 @@ router.use(function (req, res, next) { next(); }) -// Database connection -let db = new sqlite3.Database('./db/minitwit.db', sqlite3.OPEN_READWRITE, (err) => { - if (err) { - console.error(err.message); - } else { - console.log('Connected to the minitwit.db'); - } -}); +// If user is logged in, return id and username from session, otherwise empty user +// Use the returned value to populate views +function getUserCredentialsFromSession(req) { + if (req.session.username) { + return { + user: { + id: req.session.username.id, + username: req.session.username.username + } + } + } return { user: {} } +} /* GET login page. */ router.get('/', function (req, res) { - const g = { user: req.session.username }; + const g = getUserCredentialsFromSession(req); res.render('login', { title: 'Login', g: g }); }); @@ -42,13 +47,13 @@ router.post('/', (req, res, next) => { // Check user in DB const sql = 'SELECT * FROM user WHERE username = ?'; - db.get(sql, [username], (err, user) => { + db.getDb().get(sql, [username], (err, user) => { if (err) { console.error(err.message); return res.status(500).send('Server error'); } if (!user) { - req.flash('error', 'Invalid username or password'); + req.flash('error', 'Invalid username'); res.redirect('/login'); } else { bcrypt.compare(password, user.pw_hash, (err, result) => { @@ -65,8 +70,8 @@ router.post('/', (req, res, next) => { req.flash('success', 'You were logged in'); return res.redirect('/'); } else { - // Passwords dont match - return res.status(400).send('Invalid username or password'); + req.flash('error', 'Invalid password'); + return res.redirect('/login'); } }); } diff --git a/routes/register.js b/routes/register.js index c24427a..a4f971f 100644 --- a/routes/register.js +++ b/routes/register.js @@ -1,12 +1,37 @@ var express = require('express'); var router = express.Router(); const bcrypt = require('bcrypt'); +const session = require('express-session'); const UserService = require('../services/userService'); const userService = new UserService(); +router.use(session({ + secret: 'devving-and-opssing', + resave: false, + saveUninitialized: true +})); + + +router.use(function (req, res, next) { + res.locals.success_messages = req.flash('success'); + res.locals.error_messages = req.flash('error'); + next(); +}) + +function getUserCredentialsFromSession(req) { + if (req.session.username) { + return { + user: { + id: req.session.username.id, + username: req.session.username.username + } + } + } return { user: {} } +} + /* GET register page. */ router.get('/', function (req, res, next) { - const g = { user: req.session.username }; + const g = getUserCredentialsFromSession(req); res.render('register', { title: 'Register', g: g }); }); @@ -25,12 +50,27 @@ const validateEmail = (email) => { }; router.post('/', async function (req, res, next) { - const { username, email, password } = req.body; + const { username, email, password, password2 } = req.body; const validEmail = validateEmail(email); + if (password != password2) { + req.flash('error', 'The two passwords do not match') + return res.redirect('/register') + } + + if (!username) { + req.flash('error', 'You have to enter a username') + return res.redirect('/register') + } + if (!validEmail) { - req.flash('error', 'Please enter a valid email address') + req.flash('error', 'You have to enter a valid email address') + return res.redirect('/register') + } + + if (!password) { + req.flash('error', 'You have to enter a password') return res.redirect('/register') } @@ -38,15 +78,20 @@ router.post('/', async function (req, res, next) { const emailExists = await userService.getUserIdByEmailIfExists(email); const usernameExists = await userService.getUserIdByUsernameIfExists(username); - if (emailExists || usernameExists) { - req.flash('error', 'User already exists'); + if (emailExists) { + req.flash('error', 'That email is taken'); + return res.redirect('/register') + } + + if (usernameExists) { + req.flash('error', 'The username is already taken') return res.redirect('/register') } const hashedPassword = await bcrypt.hash(password, 10); await userService.registerUser(username, email, hashedPassword); - req.flash('success', 'You were registered, please login') + req.flash('success', 'You were successfully registered and can login now') return res.redirect('/login'); } catch (err) { console.error(err); diff --git a/routes/timeline.js b/routes/timeline.js index e7e038f..9ff8245 100644 --- a/routes/timeline.js +++ b/routes/timeline.js @@ -11,6 +11,17 @@ router.use(session({ saveUninitialized: true })); +function getUserCredentialsFromSession(req) { + if (req.session.username) { + return { + user: { + id: req.session.username.id, + username: req.session.username.username + } + } + } return { user: {} } +} + router.use(function (req, res, next) { res.locals.success_messages = req.flash('success'); res.locals.error_messages = req.flash('error'); @@ -52,7 +63,7 @@ router.post('/add_message', requireAuth, async function (req, res, next) { const messageContent = req.body.text; const currentDate = Math.floor(new Date().getTime() / 1000); await userService.addMessage(userId, messageContent, currentDate); - req.flash('success', 'Nice tweet!'); + req.flash('success', 'Your message was recorded'); res.redirect('/') } catch (error) { console.log(error); @@ -60,16 +71,15 @@ router.post('/add_message', requireAuth, async function (req, res, next) { } }) -router.get('/logout', function (req, res) { +router.get('/logout', requireAuth, function (req, res) { req.session.destroy(); res.redirect('/public'); }); router.get('/', requireAuth, async function (req, res, next) { try { - const userId = req.session.username.id; - const g = { user: req.session.username }; - let messages = await userService.getMessagesFromUserAndFollowedUsers(userId); + const g = getUserCredentialsFromSession(req); + let messages = await userService.getMessagesFromUserAndFollowedUsers(g.user.id); res.render('timeline', { endpoint: 'timeline', title: `${g.user.username}'s timeline`, @@ -84,13 +94,9 @@ router.get('/', requireAuth, async function (req, res, next) { router.get('/public', async function (req, res, next) { try { - - const g = { - user: req.session.username - }; + const g = getUserCredentialsFromSession(req); let messages = await userService.getPublicTimelineMessages(); res.render('timeline', { - endpoint: 'timeline', title: `Public Timeline`, messages: formatMessages(messages), g: g, @@ -104,29 +110,26 @@ router.get('/public', async function (req, res, next) { -router.get('/:username', requireAuth, async function (req, res, next) { +router.get('/:username', async function (req, res, next) { try { - const who_id = req.session.username.id; - const g = { - user: { - id: req.session.username.id, - username: req.session.username.username - } - }; + const g = getUserCredentialsFromSession(req); const whom_username = req.params.username; const whom_id = await userService.getUserIdByUsername(whom_username); const profile_user = { user: { - id: whom_id, + id: whom_id.user_id, username: whom_username } }; - const followed = await userService.isFollowing(who_id, whom_id); + let followed = false; + if (g) { + followed = await userService.isFollowing(g.user.id, whom_id.user_id); + } - let messages = await userService.getMessagesByUserId(whom_id); + let messages = await userService.getMessagesByUserId(whom_id.user_id); res.render('timeline', { endpoint: 'user', @@ -145,38 +148,15 @@ router.get('/:username', requireAuth, async function (req, res, next) { router.get('/:username/follow', requireAuth, async function (req, res, next) { try { - const who_id = req.session.username.id; - const g = { - user: { - id: req.session.username.id, - username: req.session.username.username - - } - }; + const g = getUserCredentialsFromSession(req); const whom_username = req.params.username; const whom_id = await userService.getUserIdByUsername(whom_username); - const profile_user = { - user: { - id: whom_id, - username: whom_username - } - }; - let messages = await userService.getMessagesByUserId(whom_id); + await userService.followUser(g.user.id, whom_id.user_id) - const followed = await userService.followUser(who_id, whom_id); req.flash('success', `You are now following ${whom_username}`) - // TODO: implement flashes - // res.redirect(`/${whom_username}`); - res.render('timeline', { - endpoint: 'user', - title: `${whom_username}'s Timeline`, - messages: formatMessages(messages), - g: g, - profile_user: profile_user, - followed: followed, - }); + res.redirect(`/${whom_username}`); } catch (error) { console.error(error.message); res.status(500).send('Server error'); @@ -185,38 +165,15 @@ router.get('/:username/follow', requireAuth, async function (req, res, next) { router.get('/:username/unfollow', requireAuth, async function (req, res, next) { try { - const who_id = req.session.username.id; - const g = { - user: { - id: req.session.username.id, - username: req.session.username.username - - } - }; + const g = getUserCredentialsFromSession(req); const whom_username = req.params.username; const whom_id = await userService.getUserIdByUsername(whom_username); - const profile_user = { - user: { - id: whom_id, - username: whom_username - } - }; - let messages = await userService.getMessagesByUserId(whom_id); + await userService.unfollowUser(g.user.id, whom_id.user_id); - const followed = await userService.unfollowUser(who_id, whom_id); req.flash('success', `You have unfollowed ${whom_username}`) - // TODO: implement flashes - // res.redirect(`/${whom_username}`); - res.render('timeline', { - endpoint: 'user', - title: `${whom_username}'s Timeline`, - messages: formatMessages(messages), - g: g, - profile_user: profile_user, - followed: followed, - }); + res.redirect(`/${whom_username}`); } catch (error) { console.error(error.message); res.status(500).send('Server error'); diff --git a/services/userService.js b/services/userService.js index 309e721..d45ab64 100644 --- a/services/userService.js +++ b/services/userService.js @@ -1,21 +1,12 @@ -const sqlite3 = require('sqlite3').verbose(); +const db = require('../db/database'); class UserService { - constructor() { - this.db = new sqlite3.Database('./db/minitwit.db', sqlite3.OPEN_READWRITE, (err) => { - if (err) { - console.error(err.message); - } else { - console.log('Added db connection from user service'); - } - }); - } async addMessage(userId, messageContent, currentDate) { const flagged = 0; const sql = `INSERT INTO message (author_id, text, pub_date, flagged) VALUES (?, ?, ?, ?)`; return new Promise((resolve, reject) => { - this.db.run(sql, [userId, messageContent, currentDate, flagged], (err) => { + db.getDb().run(sql, [userId, messageContent, currentDate, flagged], (err) => { if (err) { reject(err); } else { @@ -33,7 +24,7 @@ class UserService { ORDER BY message.pub_date DESC LIMIT 50`; return new Promise((resolve, reject) => { - this.db.all(sql, [id], (err, messages) => { + db.getDb().all(sql, [id], (err, messages) => { if (err) { reject(err); } else { @@ -56,7 +47,7 @@ class UserService { ORDER BY message.pub_date DESC LIMIT 50`; return new Promise((resolve, reject) => { - this.db.all(sql, [userId, userId], (err, messages) => { + db.getDb().all(sql, [userId, userId], (err, messages) => { if (err) { reject(err); } else { @@ -74,7 +65,7 @@ class UserService { ORDER BY message.pub_date DESC LIMIT 50`; return new Promise((resolve, reject) => { - this.db.all(sql, [], (err, messages) => { + db.getDb().all(sql, [], (err, messages) => { if (err) { reject(err); } else { @@ -88,7 +79,7 @@ class UserService { const sql = `SELECT user_id FROM user WHERE user.username = ?`; return new Promise((resolve, reject) => { - this.db.get(sql, [username], (err, row) => { + db.getDb().get(sql, [username], (err, row) => { if (err) { reject(err); } else { @@ -101,7 +92,7 @@ class UserService { async getUserByUsername(username) { const sql = 'SELECT * FROM user WHERE username = ?'; return new Promise((reject, resolve) => { - this.db.get(sql, [username], (err, row) => { + db.getDb().get(sql, [username], (err, row) => { if (err) { console.error(err.message); reject(err); @@ -115,11 +106,11 @@ class UserService { const sql = `SELECT user_id FROM user WHERE user.username = ?`; return new Promise((resolve, reject) => { - this.db.get(sql, [username], (err, row) => { + db.getDb().get(sql, [username], (err, id) => { if (err) { reject(err); } else { - resolve(row.user_id); + resolve(id); } }); }); @@ -129,11 +120,11 @@ class UserService { const sql = `SELECT user_id FROM user WHERE user.email = ?`; return new Promise((resolve, reject) => { - this.db.get(sql, [email], (err, row) => { + db.getDb().get(sql, [email], (err, id) => { if (err) { reject(err); } else { - resolve(row.user_id); + resolve(id); } }); }); @@ -143,7 +134,7 @@ class UserService { const sql = `SELECT user_id FROM user WHERE user.email = ?`; return new Promise((resolve, reject) => { - this.db.get(sql, [email], (err, row) => { + db.getDb().get(sql, [email], (err, row) => { if (err) { reject(err); } else { @@ -153,11 +144,11 @@ class UserService { }); } - async isFollowing(userId, followedId) { + async isFollowing(who_id, whom_id) { const sql = `SELECT * FROM follower WHERE who_id = ? AND whom_id = ?`; return new Promise((resolve, reject) => { - this.db.get(sql, [userId, followedId], (err, row) => { + db.getDb().get(sql, [who_id, whom_id], (err, row) => { if (err) { reject(err); } @@ -176,7 +167,7 @@ class UserService { from follower f where follower.who_id = ?` return new Promise((resolve, reject) => { - this.db.all(sql, [userId], (err, followed) => { + db.getDb().all(sql, [userId], (err, followed) => { if (err) { reject(err); } else { @@ -190,7 +181,7 @@ class UserService { async followUser(userId, followedId) { const sql = `INSERT INTO follower (who_id, whom_id) VALUES (?, ?)`; return new Promise((resolve, reject) => { - this.db.run(sql, [userId, followedId], (err) => { + db.getDb().run(sql, [userId, followedId], (err) => { if (err) { reject(err); } else { @@ -203,7 +194,7 @@ class UserService { async unfollowUser(userId, followedId) { const sql = `DELETE FROM follower WHERE who_id = ? AND whom_id = ?`; return new Promise((resolve, reject) => { - this.db.run(sql, [userId, followedId], (err) => { + db.getDb().run(sql, [userId, followedId], (err) => { if (err) { reject(err); } else { @@ -216,7 +207,7 @@ class UserService { async registerUser(username, email, hash) { const sql = `INSERT INTO user (username, email, pw_hash) VALUES (?, ?, ?)`; return new Promise((resolve, reject) => { - this.db.run(sql, [username, email, hash], (err) => { + db.getDb().run(sql, [username, email, hash], (err) => { if (err) { console.error(err.message); reject(err); diff --git a/views/layout.pug b/views/layout.pug index 0f6e89a..b0330e0 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -7,7 +7,7 @@ html div(class='page') h1 Maxitwit div(class='navigation') - if g && g.user + if g.user.id a(href=('/')) my timeline | a(href=('/public')) public timeline | a(href=('/logout')) sign out #{g.user.username} diff --git a/views/timeline.pug b/views/timeline.pug index f25ef9d..babdd8a 100644 --- a/views/timeline.pug +++ b/views/timeline.pug @@ -2,7 +2,7 @@ extends layout.pug block content h2= title - if g.user + if g.user.id if endpoint === 'user' .followstatus if g.user.id === profile_user.user.id