Skip to content
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

FInished Super Chat #30

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
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 .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# project_superchat
Build a realtime multi-room chat application. Make it super.

# Super Chat
Superchat is, basically, a series of chat rooms—only it must also be super. It's super-ness will be conveyed primarily through its live-updating interface, built upon a Redis store. Its super-ness might also exhibit itself vis à vis its aesthetics (though some may dismiss these efforts as superficial).

<p align="center">
<img src="/public/img/live_view.png" width="650"/>
</p>

## Getting Started

If you have [installed node](https://nodejs.org/en/download/) on your computer, type the following commands. User Node version newer than 6.5.

```
$ npm init
$ npm install express --save
$ redis-server //in another tab
$ npm start
```


## Authors

* **Dariusz Biskupski** - *Initial work* - https://dariuszbiskupski.com


## Acknowledgments

This assignment is created for [Viking Code School](https://www.vikingcodeschool.com/)
Binary file added dump.rdb
Binary file not shown.
133 changes: 133 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
const express = require('express');
const app = express();

// BODY PARSER
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded( { extended: false }) );

//SOCKET.IO
const server = require('http').createServer(app);
const io = require('socket.io')(server);
app.use(
"/socket.io",
express.static(__dirname + "node_modules/socket.io-client/dist/")
);

//HANDLEBARS
const exphbs = require('express-handlebars');
app.engine('handlebars', exphbs( { defaultLayout: 'main' } ));
app.set('view engine', 'handlebars');

//COOKIE PARSER
const cookieParser = require('cookie-parser');
app.use( cookieParser() );

//ASSETS
app.use(express.static(`${__dirname}/public`));

const {
createMessage,
createUser,
createRoom,
getRoomMessages,
getRooms,
clearDatabase,
getRoomsWithStats,
getRoomMessagesWithAuthors } = require('./models/model')


app.get('/login', (req, res)=> {
res.render('login')
});

app.get('/logout', (req, res)=> {
req.cookies.login = {};
res.render('login')
});

app.post('/login', (req, res)=> {
let login = {
'username': req.body.login } || {};
createUser(login.username);
res.cookie('login', login );
res.redirect('/rooms');
});



app.get('/rooms', async (req, res, next)=> {
const login = req.cookies.login || {};
let allRooms = await getRoomsWithStats();
try {
if (!login.username) {
res.redirect('/login')
} else {
res.render('rooms', { login, allRooms })
}
} catch(e) {
next(e);
}
});

app.get('/rooms/create', async (req, res, next)=> {
const login = req.cookies.login || {};
let allRooms = await getRoomsWithStats();
try {
if (!login.username) {
res.redirect('/login')
} else {
res.render('create_room', { login, allRooms })
}
} catch(e) {
next(e);
}
});

app.post('/rooms/create', (req, res)=> {
let login = req.cookies.login || {};
let roomName = req.body.roomName;
if (login.username) {
createRoom(roomName, login.username);
io.sockets.emit('room', roomName, 1);
res.redirect('/rooms/' + roomName);
} else {
res.redirect('back');
}
})


app.get('/rooms/:roomName', async (req, res, next)=> {
const login = req.cookies.login || {};
let roomName = req.params.roomName;
let rooms = await getRooms();
let roomMessages = {};
try {
let allRooms = await getRoomsWithStats();
if (!login.username) {
res.redirect('/login')
} else {
if (!roomName || !rooms.includes(roomName) ) {
res.redirect('/rooms');
} else {
let roomMessages = await getRoomMessagesWithAuthors(roomName);
res.render('chat', { login, allRooms, roomName, roomMessages })
}
}
} catch(e) {
next(e);
}
})

app.post('/rooms/:roomName', (req, res)=> {
let userName = req.cookies.login.username;
let roomName = req.params.roomName;
let body = req.body.body;
createMessage(body, userName, roomName);
io.sockets.emit('chat', body, userName, roomName);
res.redirect('back');
})



// app.listen(3000);
server.listen(process.env.PORT || 5000);
145 changes: 145 additions & 0 deletions models/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
const asyncRedis = require("async-redis");
// let redisClient = null;

// if (process.env.REDISTOGO_URL) {
// // redistogo connection
// console.log('reis to go/??? is it there')
// console.log(process.env.REDISTOGO_URL)
// const rtg = require("url").parse(process.env.REDISTOGO_URL);
// // const redis = require("redis").createClient(rtg.port, rtg.hostname);
// redisClient = asyncRedis.createClient(rtg.port, rtg.hostname);
// redisClient.auth(rtg.auth.split(":")[1]);
// } else {
// var redis = require("redis").createClient();
const redisClient = asyncRedis.createClient(process.env.REDIS_URL);
// }



redisClient.on('connect', ()=> {
console.log('connected to Redis');
})

redisClient.on("error", function (err) {
console.log("Error " + err);
});


let idx = 0;

const createMessage = async (body, userName, roomName) => {
roomMessageIds = await redisClient.hget('room-' + roomName, 'messages');
let members = await redisClient.hget('room-' + roomName, 'members');
if (roomMessageIds.length > 0) {
roomMessageIds = roomMessageIds.split(',');
idx = parseInt( roomMessageIds[0]) + 1;
roomMessageIds.unshift(idx);
roomMessageIds = roomMessageIds.join(',');
} else {
idx = 0;
roomMessageIds = idx;
}
redisClient.hmset(`message-${idx}`, 'body', body, 'authorId', userName, 'room', roomName, 'createdAt', new Date());
if (!members.includes(userName)) {
members += `,${userName}`;
}
redisClient.hset('room-' + roomName, 'messages', roomMessageIds, 'members', members);
idx += 1;
}


const createUser = (userName) => {
if ( !findUser(userName) ) {
redisClient.hmset(`u-${userName}`, 'createdAt', new Date());
}
}

const createRoom = (roomName, userName) => {
redisClient.hmset(`room-${roomName}`, 'messages', '', 'members', userName, 'createdAt', new Date());
}

const getMessageAuthor = async (messageId) => {
return await redisClient.hget(`message-${messageId}`, 'authorId');
}


const clearDatabase = async () => {
let msgsKeys = await redisClient.keys('message-*');
let userKeys = await redisClient.keys('user-*');
let uKeys = await redisClient.keys('u-*');
await redisClient.del('room-1');
let roomKeys = await redisClient.keys('room-*');
await redisClient.del(msgsKeys);
await redisClient.del(userKeys);
await redisClient.del(uKeys);
await redisClient.del(roomKeys);
console.log('clearing completed')
}

const getMessageBody = async (id) => {
return await redisClient.hget(`message-${id}`, 'body');
}

const getRoomMessages = async (roomName) => {
return await redisClient.hget(`room-${roomName}`, 'messages');
}

const getRoomMessagesWithAuthors = async (roomName) => {
let messageIds = await getRoomMessages(roomName);
messageIds = messageIds.split(',');
let messagesByAuthor = await [];
let index = 0;
if (!messageIds) {
return
}
for( let id of messageIds ) {
let author = await getMessageAuthor(id);
messagesByAuthor[index] = {'author': author,
'body': await getMessageBody(id)
}
index += 1;
}
return await messagesByAuthor;
}

const getRooms = async () => {
const regex = /[^room-](w+)/g;
let rooms = await [];
let roomArr = await redisClient.keys('room-*');
for( let name of roomArr ) {
let reg = name.match(/[^room-](.*)/g)
rooms.push( reg[0] );
};
return rooms;
}

const getRoomsWithStats = async () => {
let allRoomsWithStats = await {};
let allRooms = await getRooms();
if (allRooms.length > 0) {
for( let room of allRooms ) {
let users = await redisClient.hget(`room-${room}`, 'members');
if (users) {
users = users.split(',');
allRoomsWithStats[room] = users.length;
}
}
}
return allRoomsWithStats;
}

const findUser = async (userName) => {
return await redisClient.keys(`u-${userName}`)
}


module.exports = {
createMessage,
createUser,
createRoom,
getRoomMessages,
getRooms,
getRoomMessagesWithAuthors,
getRoomsWithStats,
clearDatabase
}
Loading