This repository contains a real-time web chat application implemented using Node.js, Express, Socket.IO, RxJS and Angular. It contains users and admins with groups and channels.
This application currently holds the following functionality:
- Create groups
- Create channels within groups
- Create/invite users to a channel
- Remove groups, channels and users from channels
- Assign users with Group admin or Super admin roles
- Also has group admin privileges
- Can remove users from the chat application
- Identified by username
- One persistent user called Super who is a Super admin
- Can assign an email address to itself
The root directory of the repository contains the README.md
file and the files necessary for Node.js. All of the Node.js logic is contained in a single file called index.js
. The root directory also contains a sub-directory called chat-app
which contains all the files necessary for Angular.
The chat-app
directory contains all the auto-generated files. The project's files are contained inside the src/app
sub-directory. In here, there are four directories, each containing its respective Angular Component. It also contains TypeScript files for Angular services.
The approach of using Git in this project was to commit changes at any time a new functionality was added. An example would be a task to add functionality for a user. The task would be broken down into sub-tasks and when a sub-task has been implemented, a commit would be added to Git.
Ensure that the root directory of the Git repository contains an images
directory. This directory is where the uploaded images are saved and statically served back to the user.
The main data structures that were used in this project were JavaScrict Array
objects and JavaScript Object
objects.
The JavaScript Array
was mainly used to store things that required easy iterator behaviour. Some use cases:
- List of users
- List of channels
- List of groups
The JavaScript Object
was mainly used to store the user data in a JSON file. The basic template of a user Object
looks like this:
class UserDataTemplate {
constructor() {
this.username = "";
this.password = "password";
this.email = "";
this.superAdmin = false;
this.groupAdmin = false;
this.profileImage = "images/default/profile.gif";
this.groups = [
{
"name": "newbies",
"channels": [
"general",
"help"
]
},
{
"name": "general",
"channels": [
"general",
"chitchat",
"topic of the day"
]
}
]
}
};
The user's username would be the key to the Object
value and each user was stored in a users.json
file which contained an Object
of users as properties.
JSON was also used to store the (data of the) body parameters of POST requests where the request or the response was encoded in JSON.
The data structure to store messages were also done by using a JSON Object
.
let content = {
"username": username,
"groupName": groupName,
"channelName": channelName,
"message": message,
"profileImage": profileImage,
"isFile": isFile
}
The username is used to show the user's name who sent the message. The group and channel name are unique IDs for the message. The message itself is a string of text. The profileImage
is a path string to the statically served profile image on the server. The isFile
is a boolean which denotes whether this message is a text message or an image file being sent.
The chat application is separated into two parts, the client and the server. The client is created using the Angular framework. The client manages the models and views (components in Angular), which involves the dashboard, groups, channels and login. These components make up the single-page application (SPA) of the web chat app.
The server (back-end) is RESTful because it does not maintain any state. It has a number of routes which manages CRUD actions. The server connects to a MongoDB database which contains the data on the users and their messages, channels, groups and other information.
The REST API on the Node.js server was implemented using the Express library.
- Description:
- Get user data as a JavaScript
Object
on a user.
- Get user data as a JavaScript
- Parameters:
- username:string - The username of the user to retrieve the data on.
- Return value:
- The user data
Object
- The user data
- Description:
- Get all the groups that exist in the system.
- Parameters:
- None
- Return value:
- An array of strings of group names
- Description:
- Update the email of the user.
- Parameters:
- username:string The username of the user.
- email:string The email of the user to update
- Return value:
- Returns the request body back to the user to confirm it was successful.
- Description:
- Remove the group from the system.
- Parameters:
- groupName:string The name of the group to remove.
- Return value:
- The
Object
of users.
- The
- Description:
- Create a new group in the system.
- Parameters:
- username:string The user's username.
- groupName:string The group name to create
- Return value:
- An array of all groups in the system.
- Description:
- Create a new channel within a group.
- Parameters:
- groupName:string The group that the new channel belongs to.
- channelName:string The name of the new channel.
- Return value:
- A new array of channels in the group.
- Description:
- Remove a channel of a group.
- Parameters:
- groupName:string The group name.
- channelName:string The channel name.
- Return value:
- Return a new array of the channels in the group.
- Description:
- Get all the channels in a group
- Parameters:
- groupName:string The channels of the group name.
- Return value:
- An array of channel names.
- Description:
- Get all the users in a group
- Parameters:
- groupName:string The group name to get users.
- Return value:
- All the users in the group.
- Description:
- Get all the user data in the system.
- Parameters:
- None
- Return value:
- Returns all the user data object.
- Description:
- Remove a user from a group
- Parameters:
- username:string The username of the user to remove.
- groupName:string The group where the user will be removed.
- Return value:
- Return the updated list of users in the group.
- Description:
- Add a user to a group.
- Parameters:
- username:string The username of the user to add.
- groupName:string The group to add the user.
- Return value:
- Return the user data of all the users in the system.
- Description:
- Add a new user to an existing channel in a group.
- Parameters:
- username:string The username of the user to add
- groupName:string The group the user will be added to.
- channelName The channel the new user will be added to.
- Return value:
- Return the object of all the user data.
- Description:
- Remove a user from a channel in a group.
- Parameters:
- username:string The username of the user to remove.
- _groupName:string: The name of the group the channel and the user belongs to.
- channelName:string The channel that the user belongs to.
- Return value:
- Return the object of all the user data.
- Description:
- Remove a user from the system.
- Parameters:
- username:string The username of the user to remove.
- Return value:
- Return the object of all the user data.
- Description:
- Add group admin role to a user.
- Parameters:
- username:string The username of a user to add admin role to.
- Return value:
- Return the object of all the user data.
- Description:
- Add super admin role to a user.
- Parameters:
- username:string The username of a user to add admin role to.
- Return value:
- Return the object of all the user data.
The Angular application contains components, services and routes.
There were four components that were used in this application.
- channel
- dashboard
- group
- login
The Channel component contains all the user-interface features of a chat channel. It contains a list of users, the current channel name, the chat box and the textfield and button to send messages.
The Dashboard component contains all the groups available to the user, their username, their current email, an input field to update their email and a log out button.
For Group admins, there's a form to create new groups and buttons to remove groups. Note that default groups newbies and general cannot be removed.
For a Super admin, they can see the entire list of users in the system. Super admins can remove users from the system (except the persistent Super user) and assign Group or Super admin roles to existing users.
The Group component contains all the information for groups. This includes the group name, the logged in user, the available channels to the user and a log out button.
Group admins will have an input form to add new channels and buttons to remove channels. Note, default channel general cannot be removed (however an admin can remove a group in the Dashboard component).
Group admins can also add users to the group and remove users from the group. Adding a non-existent user simply creates the user and adds them to the group. Adding a user to the group automatically adds them to the default channel general.
The Login component allows a user to log in. Any user can type any username and log in. User data will persist after they log out. If a username does not exist in the system, the server will seamlessly create the user in the background.
The image service handles the upload of images to the server. It contains a function that performs a POST request to the server.
The socket service handles the web socket communications in real-time. The sockets contain three rooms:
- join
- leave
- new-message
Join notifies the server that the user has joined the channel. Leave notifies the server that the user has left the channel. New-message is used for new messages, which may be text or images.
The server responds in the message room, broadcasting only to the room which it originally received from.
The users service contains the requests for all dashboard, groups and channel data manipulation to and from the server.
- users - Each document is the data of the users. This includes their username, their admins status, their groups and channels.
- messages - Each document is a message that was sent in a channel.
npm init
npm install express
npm install socket.io
npm install body-parser
npm install @types/socket.io-client
- SocketIO for clientnpm install formidable
- In
polyfill.ts
, add:
(window as any).global = window;
const bodyParser = require('body-parser')
app.use(bodyParser.json())
Author: Edmond Chuc
Website: www.edmondchuc.com
Repository: https://github.com/edmondchuc/web-chat-app