Skip to content

Notification system using socket.io, remove user/admin, case-insensitive functionality #135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ NODE_ENV="development"
JWT_SECRET="thisismysupersecrettokenjustkidding"
DATABASE_URL="mongodb://localhost:27017/donut-development"
SENDGRID_API_KEY = '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'
SOCKET_PORT = 8810
3 changes: 2 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ PORT=3000
NODE_ENV=testing
JWT_SECRET=thisismysupersecrettokenjustkidding
DATABASE_URL=mongodb+srv://donut-admin:[email protected]/donut-testing?retryWrites=true&w=majority
SENDGRID_API_KEY = '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'
SENDGRID_API_KEY = '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'
SOCKET_PORT = 8810
20 changes: 19 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const logger = require('morgan')
const cookieParser = require('cookie-parser')
const createError = require('http-errors')
const path = require('path')
const socket = require('socket.io')

const indexRouter = require('./app/routes/index')
const authRouter = require('./app/routes/auth')
Expand All @@ -14,8 +15,20 @@ const shortUrlRouter = require('./app/routes/urlShortner')
const organizationRouter = require('./app/routes/organisation')
const commentRouter = require('./app/routes/comment')
const projectRouter = require('./app/routes/project')
const notificationRouter = require('./app/routes/notification')

const app = express()
const server = require('http').Server(app)

server.listen(process.env.SOCKET_PORT || 8810)
// WARNING: app.listen(80) will NOT work here!

const io = socket.listen(server)
let count = 0
io.on('connection', (socket) => {
console.log('socket connected count ', count++)
io.emit('user connected')
})

// view engine setup
app.set('views', path.join(__dirname, 'views'))
Expand All @@ -26,7 +39,12 @@ app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())
app.use(express.static(path.join(__dirname, 'public')))
app.use((req, res, next) => {
req.io = io
next()
})

app.use('/notification', notificationRouter)
app.use('/', indexRouter)
app.use('/auth', authRouter)
app.use('/user', usersRouter)
Expand All @@ -53,4 +71,4 @@ app.use(function (err, req, res, next) {
res.render('error')
})

module.exports = app
module.exports = { app, io }
63 changes: 47 additions & 16 deletions app/controllers/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,31 @@ const HANDLER = require('../utils/response-helper')
const HttpStatus = require('http-status-codes')
const permission = require('../utils/permission')
const helper = require('../utils/paginate')
const notificationHelper = require('../utils/notif-helper')
const notification = {
heading: '',
content: '',
tag: ''
}

module.exports = {
createEvent: async (req, res, next) => {
const event = new Event(req.body)
try {
event.createdBy = req.user._id
await event.save()
req.io.emit('new event created', { data: event.eventName })
notification.heading = 'New Event!'
notification.content = `${event.eventName} is added!`
notification.tag = 'New!'
notificationHelper.addToNotificationForAll(req, res, notification, next)
res.status(HttpStatus.CREATED).json({ event: event })
} catch (error) {
res.status(HttpStatus.BAD_REQUEST).json({ error: error })
}
},

updateEvent: async (req, res) => {
updateEvent: async (req, res, next) => {
const { id } = req.params
const updates = Object.keys(req.body)
try {
Expand All @@ -29,15 +40,21 @@ module.exports = {
event[update] = req.body[update]
})
await event.save()
req.io.emit('event update', { data: `Event: ${event.eventName} is updated!` })
notification.heading = 'Event update!'
notification.content = `${event.eventName} is updated!`
notification.tag = 'Update'
notificationHelper.addToNotificationForAll(req, res, notification, next)
res.status(HttpStatus.OK).json({ event: event })
} catch (error) {
HANDLER.handleError(res, error)
}
},

rsvp: async (req, res) => {
rsvp: async (req, res, next) => {
const { yes, no, maybe } = req.body
const { id } = req.params
notification.tag = 'RSVP'
try {
const data = await Event.findById(id)
if (!data) {
Expand All @@ -47,14 +64,23 @@ module.exports = {
if (data.rsvpMaybe.includes(req.user.id) ||
data.rsvpNo.includes(req.user.id) ||
data.rsvpYes.includes(req.user.id)) {
return res.status(HttpStatus.OK).json({ msg: 'You have already done the rsvp' })
req.io.emit('already rsvp', { data: 'You have already done the rsvp' })
notification.heading = 'Already rsvp!'
notification.content = 'You have already done the rsvp'
notificationHelper.addToNotificationForUser(req.user._id, res, notification, next)
res.status(HttpStatus.OK).json({ msg: 'You have already done the rsvp' })
return
}
const event = await Event.findByIdAndUpdate(id)
if (yes) {
try {
event.rsvpYes.push(req.user.id)
await event.save()
return res.status(HttpStatus.OK).json({ rsvpData: data })
req.io.emit('rsvp done', { data: 'RSVP successfully done!' })
notification.heading = 'RSVP done!'
notification.content = 'RSVP successfully done!'
notificationHelper.addToNotificationForUser(req.user._id, res, notification, next)
res.status(HttpStatus.OK).json({ rsvpData: data })
} catch (error) {
return res.status(HttpStatus.BAD_REQUEST).json({ error: error })
}
Expand All @@ -63,7 +89,11 @@ module.exports = {
try {
event.rsvpNo.push(req.user.id)
await event.save()
return res.status(HttpStatus.OK).json({ rsvpData: data })
req.io.emit('rsvp done', { data: 'RSVP successfully done!' })
notification.heading = 'RSVP done!'
notification.content = 'RSVP successfully done!'
notificationHelper.addToNotificationForUser(req.user._id, res, notification, next)
res.status(HttpStatus.OK).json({ rsvpData: data })
} catch (error) {
return res.status(HttpStatus.BAD_REQUEST).json({ error: error })
}
Expand All @@ -72,7 +102,11 @@ module.exports = {
try {
event.rsvpMaybe.push(req.user.id)
await event.save()
return res.status(HttpStatus.OK).json({ rsvpData: data })
req.io.emit('rsvp done', { data: 'RSVP successfully done!' })
notification.heading = 'RSVP done!'
notification.content = 'RSVP successfully done!'
notificationHelper.addToNotificationForUser(req.user._id, res, notification, next)
res.status(HttpStatus.OK).json({ rsvpData: data })
} catch (error) {
return res.status(HttpStatus.BAD_REQUEST).json({ error: error })
}
Expand All @@ -98,12 +132,10 @@ module.exports = {
GetAllEvent: async (req, res, next) => {
try {
const EventData = await Event.find({}, {}, helper.paginate(req))
.populate('createdBy', ['name.firstName', 'name.lastName', '_id', 'isAdmin'])
.sort({ eventDate: -1 })
.lean()
if (!EventData) {
return res.status(HttpStatus.NOT_FOUND).json({ error: 'No such Event is available!' })
}
return res.status(HttpStatus.OK).json({ Event: EventData })
return res.status(HttpStatus.OK).json({ events: EventData })
} catch (error) {
HANDLER.handleError(res, error)
}
Expand All @@ -118,6 +150,11 @@ module.exports = {
}
if (permission.check(req, res, deleteEvent.createdBy)) {
await Event.findByIdAndRemove(id)
req.io.emit('event deleted', { data: deleteEvent.eventName })
notification.heading = 'Event deleted!'
notification.content = `Event ${deleteEvent.eventName} is deleted!`
notification.tag = 'Deleted'
notificationHelper.addToNotificationForAll(req, res, notification, next)
return res.status(HttpStatus.OK).json({ deleteEvent: deleteEvent, message: 'Deleted the event' })
}
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'Not permitted!' })
Expand All @@ -132,9 +169,6 @@ module.exports = {
.sort({ eventDate: -1 })
.exec()
console.log('Upcoming events ', events)
if (events.length === 0) {
return res.status(HttpStatus.OK).json({ msg: 'No Upcoming events exists!' })
}
return res.status(HttpStatus.OK).json({ events })
} catch (error) {
HANDLER.handleError(res, next)
Expand All @@ -147,9 +181,6 @@ module.exports = {
.sort({ eventDate: -1 })
.populate('createdBy', '_id name.firstName name.lastName')
.exec()
if (events.length === 0) {
return res.status(HttpStatus.OK).json({ msg: 'No events posted by user!' })
}
return res.status(HttpStatus.OK).json({ events })
} catch (error) {
HANDLER.handleError(res, error)
Expand Down
38 changes: 38 additions & 0 deletions app/controllers/notification.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const HANDLER = require('../utils/response-helper')
const HttpStatus = require('http-status-codes')
const Notifications = require('../models/Notifications')
const helper = require('../utils/paginate')
const User = require('../models/User')

module.exports = {
// GET ALL THE NOTIFICATIONS FOR ALL
getOrgNotifications: async (req, res, next) => {
try {
const notifications = await Notifications.find({}, {}, helper.paginate(req))
.lean()
.sort({ createdAt: -1 })
.exec()
return res.status(HttpStatus.OK).json({ notifications })
} catch (error) {
HANDLER.handleError(res, error)
}
},
// GET LOGGED IN USER NOTIFICATIONS
getUserNotification: async (req, res, next) => {
const userId = req.user._id
try {
const user = await User.findById(userId)
if (!user) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'No such user exists!' })
}
// get all notifications of existing user
const notifications = user.notifications
if (notifications.length === 0) {
return res.status(HttpStatus.OK).json({ msg: 'No new notifications!' })
}
return res.status(HttpStatus.OK).json({ notifications })
} catch (error) {
HANDLER.handleError(res, error)
}
}
}
79 changes: 72 additions & 7 deletions app/controllers/organization.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ const Organization = require('../models/Organisation')
const HANDLER = require('../utils/response-helper')
const HttpStatus = require('http-status-codes')
const helper = require('../utils/uploader')
const notificationHelper = require('../utils/notif-helper')
const User = require('../models/User')
const Project = require('../models/Project')
const Event = require('../models/Event')
const permission = require('../utils/permission')
const TAGS = require('../utils/notificationTags')
const notification = {
heading: '',
content: '',
tag: ''
}

module.exports = {
createOrganization: async (req, res, next) => {
Expand All @@ -15,7 +22,12 @@ module.exports = {
}
try {
await org.save()
res.status(HttpStatus.CREATED).json({ org })
req.io.emit('new org created', { data: org.name })
notification.heading = 'New org!'
notification.content = `${org.name} is created!`
notification.tag = TAGS.NEW
notificationHelper.addToNotificationForAll(req, res, notification, next)
return res.status(HttpStatus.CREATED).json({ org })
} catch (error) {
HANDLER.handleError(res, error)
}
Expand Down Expand Up @@ -80,6 +92,11 @@ module.exports = {
if (!permission.check(req, res)) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'You don\'t have the permission!' })
}
req.io.emit('org deleted', { data: org.name })
notification.heading = 'Org deleted!'
notification.content = `${org.name} is deleted!`
notification.tag = TAGS.DELETE
notificationHelper.addToNotificationForAll(req, res, notification, next)
return res.status(HttpStatus.OK).json({ organization: org })
} catch (error) {
HANDLER.handleError(res, error)
Expand Down Expand Up @@ -111,15 +128,25 @@ module.exports = {
}
// if user is admin or not
const adminIds = organization.adminInfo.adminId
const isAdmin = adminIds.indexOf(req.user.id)
const isAdmin = adminIds.indexOf(req.user.id) || req.user.isAdmin
// user is admin then perform operation
if (isAdmin !== -1) {
if (isAdmin !== -1 || req.user.isAdmin) {
// toggle maintenance mode
organization.isMaintenance = !organization.isMaintenance
await organization.save()
notification.tag = TAGS.MAINTENANCE

if (organization.isMaintenance) {
req.io.emit('org under maintenance', { data: organization.name })
notification.heading = 'Maintenance mode on!'
notification.content = `${organization.name} is kept under maintenance!`
notificationHelper.addToNotificationForAll(req, res, notification, next)
return res.status(HttpStatus.OK).json({ msg: 'Organization is kept under the maintenance!!' })
}

req.io.emit('org revoked maintenance', { data: organization.name })
notification.heading = 'Maintenance mode off!'
notification.content = `${organization.name} is revoked from maintenance!`
return res.status(HttpStatus.OK).json({ msg: 'Organization is recovered from maintenance!!' })
} else {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'You don\'t have access to triggerMaintenance!' })
Expand Down Expand Up @@ -191,9 +218,15 @@ module.exports = {
try {
const { search } = req.query
if (search) {
const regex = search.split(' ')
const member = await User.find({ $or: [{ 'name.firstName': regex }, { 'name.lastName': regex }] })
.select('name email isAdmin info.about.designation')
const queryTerm = search.split(' ')
const regex = new RegExp('^' + queryTerm + '$', 'i')
const member = await User.find({
$or: [
{ 'name.firstName': { $regex: regex } },
{ 'name.lastName': { $regex: regex } }
]
})
.select('name email isAdmin info.about.designation isRemoved')
.lean()
.sort({ createdAt: -1 })
.exec()
Expand All @@ -203,7 +236,7 @@ module.exports = {
return res.status(HttpStatus.OK).json({ member })
} else {
const members = await User.find({})
.select('name email isAdmin info.about.designation')
.select('name email isAdmin info.about.designation isRemoved')
.lean()
.sort({ createdAt: -1 })
.exec()
Expand All @@ -215,5 +248,37 @@ module.exports = {
} catch (error) {
HANDLER.handleError(res, error)
}
},
// REMOVE ADMIN
removeAdmin: async (req, res, next) => {
try {
const { userId, orgId } = req.params
const org = await Organization.findById(orgId)
if (!org) {
return res.status(HttpStatus.NOT_FOUND).json({ msg: 'No org exists!' })
}
// only permitted for admins
if (!req.user.isAdmin) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'You are not permitted!' })
}
// console.log('Permitted to removeAdmin')
// REMOVE ADMINS FROM ADMINS LIST
const admins = org.adminInfo.adminId
console.log('adminIds ', admins)
const removableIndex = admins.indexOf(userId)
if (removableIndex === -1) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: 'User is not an admin!' })
}
// user is admin so remove
org.adminInfo.adminId.splice(removableIndex, 1)
await org.save()
// also make isAdmin false
const user = await User.findById(userId)
user.isAdmin = false
await user.save()
return res.status(HttpStatus.OK).json({ org })
} catch (error) {
HANDLER.handleError(res, error)
}
}
}
Loading