Skip to content
This repository has been archived by the owner on Oct 26, 2024. It is now read-only.
/ ChitChatApp Public archive

Room-based chat application that enables users to create, join, and engage in chat rooms, built with a modern tech stack, combining powerful backend technologies with front-end frameworks to deliver a smooth and engaging user experience.

License

Notifications You must be signed in to change notification settings

NanddoSalas/ChitChatApp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


todo: get a logo

Full Stack room-based Chat Application
Try Demo

Table of Contents
  1. About The Project
  2. Design and Development of the Project
  3. Getting Started
  4. License

About The Project

project screenshot

ChitChatApp is a room-based chat application. This Full Stack project enables users to create, join, and engage in chat rooms seamlessly. With a focus on real-time communication ChitChatApp is built with a modern tech stack, combining powerful backend technologies with sleek front-end frameworks to deliver a smooth and engaging user experience.

Features

  • Room Creation and Management: Easily create and manage multiple chat rooms.
  • User Authentication: Secure user login and registration using modern authentication techniques.
  • Responsive Design: Accessible on both desktop and mobile devices for a seamless experience.
  • Real-Time Messaging: Enjoy instant messaging powered by WebSockets for real-time communication.

Motivation

The primary motivation behind creating ChitChatApp was to enhance my skills in both backend and frontend development. This project serves as a hands-on platform to achieve the following goals:

  • Learn a New Backend Technology: One of the key objectives was to gain proficiency in Java and Spring. By integrating these technologies, I aimed to build a robust and scalable backend system that supports real-time communication and efficient data management.

  • Enhance Frontend Development Skills: Another important goal was to deepen my knowledge as a frontend developer. This project provided an opportunity to work with modern frontend frameworks and tools, focusing on creating a seamless and responsive user interface that enhances the user experience.

(back to top)

Design and Development of the Project

Database

database schema

Authentication and Authorization

Authentication

The API is secured by JWT Authentication, where a user can choose to sign in using credentials (email and password) or a Google Account to get an Access Token and use it for subsequent requests:

sequenceDiagram
    participant R as React App
    participant S as Spring Server
    participant G as Google

    R -->> + S: GET /rooms
    S -->> - R: HTTP 401 Unauthorized

    R -->> + S: <br/><br/><br/>POST /auth/signin, credentials
    S -->> - R: HTTP 200 OK, accessToken

    R -->> + G: <br/><br/><br/>Authenticate with Google and Consent
    G -->> - R: Authorization Code
    R -->> + S: POST /auth/signin/google, authorization code
    S -->> + G: Exchange authorization code
    G -->> - S: ID Token
    S -->> - R: HTTP 200 OK, accessToken

    R -->> + S: <br/><br/><br/>GET /roms, accessToken
    S -->> - R: HTTP 200 OK, body
Loading

Authrorization

Every authenticated user will have a role assigned to it, it could be:

  • Member: A member can send, see, and receive messages from other users, public rooms and private rooms where he is a member of.
  • Admin: An admin can do all a member can do, and also manage invitation and rooms (can't manage invitations or rooms created by other users).
  • ServerAdmin: A server admin can do all an admin can do, and also he's able to update user's role (from member to admin, back and forth).

API Overview


Authentication

POST /auth/signup (Sign up with credentials) (Role required: none)
Body Parameters
NameTypeRequired
emailstringtrue
passwordstringtrue
fullNamestringtrue
inviteCodestringtrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
404
{
  "data": null,
  "errors": {
    "inviteCode": "Invalid invitation code",
    "email": "Email already in use"
  }
}
POST /auth/signup/google (Sign up with Google) (Role required: none)
Body Parameters
NameTypeRequired
codestringtrue
inviteCodestringtrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
404
{
  "data": null,
  "errors": {
    "message": "Unable to Sign up with that Google Account, try a different one!"
  }
}
POST /auth/signin (Sign in with credential) (Role required: none)
Body Parameters
NameTypeRequired
emailstringtrue
passwordstringtrue
Responses
StatusResponse Body Example
200
{
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJhbGljZS5qb2huc29uQGV4YW1wbGUuY29tIiwic2NvcGUiOiJTZXJ2ZXJBZG1pbiJ9.kC3rk6NQSbDCEJTciLDSPjZF7JO7eraa6imLn_yO3PA",
    "user": {
      "id": 1,
      "fullName": "Alice Johnson",
      "email": "[email protected]",
      "avatar": "https://randomuser.me/api/portraits/women/1.jpg",
      "about": "Loves hiking and outdoor adventures.",
      "role": "ServerAdmin",
      "creationDate": "2024-07-12 20:12:44.825401",
      "hasPassword": true,
      "hasGoogle": false,
      "hasGigHub": false
    }
  },
  "errors": null
}
404
{
  "data": null,
  "errors": {
    "message": "Invalid credentials"
  }
}
POST /auth/signin/google (Sign in with Google) (Role requires: none)
Body Parameters
NameTypeRequired
codestringtrue
Responses
StatusResponse Body Example
200
{
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJhbGljZS5qb2huc29uQGV4YW1wbGUuY29tIiwic2NvcGUiOiJTZXJ2ZXJBZG1pbiJ9.kC3rk6NQSbDCEJTciLDSPjZF7JO7eraa6imLn_yO3PA",
    "user": {
      "id": 1,
      "fullName": "Alice Johnson",
      "email": "[email protected]",
      "avatar": "https://randomuser.me/api/portraits/women/1.jpg",
      "about": "Loves hiking and outdoor adventures.",
      "role": "ServerAdmin",
      "creationDate": "2024-07-12 20:12:44.825401",
      "hasPassword": true,
      "hasGoogle": false,
      "hasGigHub": false
    }
  },
  "errors": null
}
404
{
  "data": null,
  "errors": {
    "message": "User does not exist, Sign up first!"
  }
}

Users

GET /users (Retrieve all Users) (Role requires: Member)
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "id": 1,
      "fullName": "Alice Johnson",
      "email": "[email protected]",
      "avatar": "https://randomuser.me/api/portraits/women/1.jpg",
      "about": "Loves hiking and outdoor adventures.",
      "role": "ServerAdmin",
      "creationDate": "2024-07-12 20:12:44.825401"
    },
    {
      "id": 2,
      "fullName": "Bob Smith",
      "email": "[email protected]",
      "avatar": "https://randomuser.me/api/portraits/men/1.jpg",
      "about": "Avid reader and writer.",
      "role": "Member",
      "creationDate": "2024-07-12 20:12:44.825401"
    }
  ],
  "errors": null
}
PUT /users/${userId}/role (Update User's Role) (Role requires: ServerAdmin)
Body Parameters
NameTypeRequired
role"Member" | "Admin"true
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
PUT /users/${userId}/profile (Update User's Profile) (Role requires: Member)
Body Parameters
NameTypeRequired
fullNamestringtrue
aboutstringtrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
PUT /users/${userId}/password (Update User's Password) (Role requires: Member)
Body Parameters
NameTypeRequired
oldPasswordstringtrue
newPasswordstringtrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
400
{
  "data": null,
  "errors": {
    "oldPassword": "Invalid old password"
  }
}
PUT /users/${userId}/google (Connect Google Account) (Role requires: Member)
Body Parameters
NameTypeRequired
codestringtrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
400
{
  "data": null,
  "errors": {
    "message": "Unable to connect that Google Account, try a different one!"
  }
}
DELETE /users/${userId}/google (Disconnect Google Account) (Role requires: Member)
Body Parameters
NameTypeRequired
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}

Invitations

GET /invitations (Retrieve all Invitations) (Role requires: Admin)
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "id": 1,
      "inviteCode": "YAgSKjtGGVseZgva",
      "uses": 2,
      "maxUses": 10,
      "revoked": false,
      "creatioDate": "2024-07-12 20:12:44.837065"
    },
    {
      "id": 4,
      "inviteCode": "YAgSKjtGGVseZgva",
      "uses": 1,
      "maxUses": 3,
      "revoked": false,
      "creatioDate": "2024-07-12 20:13:52.06593"
    }
  ],
  "errors": null
}
POST /invitations (Create Invitation) (Role requires: Admin)
Body Parameters
NameTypeRequired
limitnumberfalse
Responses
StatusResponse Body Example
200
{
  "data": {
    "id": 5,
    "inviteCode": "BU5DOkGngG57EVSb",
    "uses": 0,
    "maxUses": 3,
    "revoked": false,
    "creatioDate": "2024-07-12 20:45:01.546282"
  },
  "errors": null
}
DELETE /invitations/${invitationId} (Revoke Invitation) (Role requires: Admin)
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}

Rooms

GET /rooms (Retrieve all Rooms) (Role requires: Member)
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "id": 1,
      "roomName": "General Chat",
      "creatorId": 1,
      "creationDate": "2024-07-12 20:47:21.250011",
      "hasAccess": true,
      "private": false
    },
    {
      "id": 2,
      "roomName": "Gaming Room",
      "creatorId": 3,
      "creationDate": "2024-07-12 20:47:21.250011",
      "hasAccess": false,
      "private": false
    },
    {
      "id": 3,
      "roomName": "Admin Room",
      "creatorId": 1,
      "creationDate": "2024-07-12 20:47:21.250011",
      "hasAccess": true,
      "private": true
    }
  ],
  "errors": null
}
POST /rooms (Create Room) (Role requires: Admin)
Body Parameters
NameTypeRequired
namestringtrue
privatebooleanfalse
Responses
StatusResponse Body Example
200
{
  "data": {
    "id": 6,
    "roomName": "Awesome private room",
    "creatorId": 1,
    "creationDate": "2024-07-12 20:52:20.460003",
    "hasAccess": true,
    "private": true
  },
  "errors": null
}
PUT /rooms/${roomId} (Update Room) (Role requires: Admin)
Body Parameters
NameTypeRequired
namestringtrue
privatebooleantrue
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}
DELETE /rooms/${roomId} (Delete Room) (Role requires: Admin)
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}

Members

GET /rooms/${roomId}/members (Retrieve all Room Members) (Role requires: Member)
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "userId": 1,
      "memberSince": "2024-07-12 20:47:21.255017"
    },
    {
      "userId": 2,
      "memberSince": "2024-07-12 20:47:21.255017"
    },
    {
      "userId": 3,
      "memberSince": "2024-07-12 20:47:21.255017"
    },
    {
      "userId": 4,
      "memberSince": "2024-07-12 20:47:21.255017"
    }
  ],
  "errors": null
}
POST /rooms/${roomId}/members (Create Room Member) (Role requires: Admin)
Body Parameters
NameTypeRequired
userIdnumbertrue
Responses
StatusResponse Body Example
200
{
  "data": {
    "roomId": 1,
    "userId": 2,
    "creationDate": "2024-07-12 21:01:00.173227"
  },
  "errors": null
}
DELETE /rooms/${roomId}/members/${userId} (Delete Room Member) (Role requires: Admin)
Responses
StatusResponse Body Example
200
{
  "data": null,
  "errors": null
}

Messages

GET /users/${userId}/messages (Retrieve paginated Direct Messages) (Role requires: Member)
Query Parameters
NameTypeRequired
cursornumberfalse
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "id": 2,
      "senderId": 2,
      "creationDate": "2024-07-12 20:47:21.266432",
      "body": "Sure Alice, I will check it now."
    },
    {
      "id": 1,
      "senderId": 1,
      "creationDate": "2024-07-12 20:47:21.266432",
      "body": "Hey Bob, can you check the admin panel?"
    }
  ],
  "errors": null
}
GET /rooms/${roomId}/messages (Retrieve paginated Room Messages) (Role requires: Member)
Query Parameters
NameTypeRequired
cursornumberfalse
Responses
StatusResponse Body Example
200
{
  "data": [
    {
      "id": 9,
      "senderId": 1,
      "creationDate": "2024-07-12 20:47:21.260368",
      "body": "Here is a song I like."
    },
    {
      "id": 8,
      "senderId": 5,
      "creationDate": "2024-07-12 20:47:21.260368",
      "body": "Let’s share some music recommendations."
    }
  ],
  "errors": null
}
POST /users/${userId}/messages (Send Direct Message) (Role requires: Member)
Body Parameters
NameTypeRequired
bodystringtrue
Responses
StatusResponse Body Example
200
{
  "data": {
    "id": 12,
    "senderId": 1,
    "creationDate": "2024-07-12 21:09:25.748969",
    "body": "Hi there"
  },
  "errors": null
}
POST /rooms/${roomId}/messages (Send Room Message) (Role requires: Member)
Body Parameters
NameTypeRequired
bodystringtrue
Responses
StatusResponse Body Example
200
{
  "data": {
    "id": 7,
    "senderId": 1,
    "creationDate": "2024-07-12 21:09:48.029569",
    "body": "Hi there"
  },
  "errors": null
}

Real Time Architecture

Real-Time functionality is built using STOMP (Streaming Text Oriented Messaging Protocol) over WebSockets, where a user hits an API endpoint that creates, updates or deletes resources and those changes will be sended to subscribed clients.

Endpoints where a client can suscribe to:

Public STOMP Destination
/topic/new-user
/topic/update-user
/topic/new-room
/topic/update-room
/topic/delete-room
/topic/new-room-message
Filtered STOMP Destinations Who recieves
/user/queue/update-role User who's role been updated
/user/queue/new-private-room-message Members of the private room
/user/queue/new-direct-message User whose message is sent to
/user/queue/new-room-member User who became a room's member
/user/queue/delete-room-member User whose no longer a room's member

(back to top)

Getting Started

Packages

This project is made up of 2 packages.

  • server/ (Spring server)
  • packages/web/ (React app)

Prerequisites

  • Java 17
  • Node.js v17
  • PostgreSQL database
  • Google Client ID and Secret for Google Authentication

Installation

  1. Get the code into your local machine
git clone https://github.com/NanddoSalas/ChitChatApp.git
cd ChitChatApp
  1. Install React app dependencies
yarn install
  1. Setup React app environment at packages/web/.env
cp packages/web/.env.example packages/web/.env

Sample environment

VITE_GOOGLE_CLIENT_ID=
VITE_API_URL=http://localhost:8080
VITE_STOMP_URL=ws://localhost:8080/socket
  1. Start React app
yarn dev
  1. Setup Spring server application properties at server/src/main/resources/application.properties

Sample application properties

spring.application.name=ChitChatApp Server
spring.datasource.url=jdbc:postgresql://localhost:5432/chitchatapp
spring.datasource.username=user
spring.datasource.password=password
spring.sql.init.mode=always
secret.key=defautlSecret
google.clientId=
google.clientSecret=
google.redirectUri=http://localhost:5173
  1. Start Spring server
cd server
./mvnw spring-boot:run

(back to top)

License

Distributed under the MIT License. See LICENSE for more information.

(back to top)

````

About

Room-based chat application that enables users to create, join, and engage in chat rooms, built with a modern tech stack, combining powerful backend technologies with front-end frameworks to deliver a smooth and engaging user experience.

Topics

Resources

License

Stars

Watchers

Forks

Languages