Multi-room chat application developed with Gin, GORM, Go Redis and Gorilla WebSocket.
Server is deployed on Google Cloud using Cloud Run and can scale to multiple instances. Memorystore (Redis) is primary used for it's pubsub feature to broadcast messages to all server instances. It's also used for storing temporary data like one time passwords, login attempts and rate limiting. Cloud SQL is used for storing persistent data like users, rooms and messages. Cloud Build is used for building and deploying the application.
This component diagram gives a general representation of how the backend’s main components depend on each other. For the server was chosen, a layered architecture which gives enough structure without being overly complicated. The three main layers are Handlers, Services, and Repositories. Handlers and Web sockets receive requests from the front end application. Services hold all the business logic. Repositories are responsible for data persistence in our PostgreSQL database. And Redis is used for its pubsub feature that allows to connect and send messages between a lot of clients. Redis is also used for storing temporary passwords as well as IP addresses for DDoS protection.
Create a .env
file in the root directory. And add these default values:
DATABASE_URL=postgres://postgres:password@postgres:5432/rooms
REDIS_URL=redis://:password@redis:6379/0
API_SECRET=SecretSecretSecret
TOKEN_HOUR_LIFESPAN=12
ENVIRONMENT=dev
PORT=8080
CORS_ORIGIN=*
RATE_LIMIT=30
RATE_WINDOW=1s
LOGIN_ATTEMPTS=5
LOGIN_WINDOW=10m
OTP_EXPIRY=10m
If you want to use SMTP for one time password emails. Add your SMTP credentials:
SENDER_NAME=Rooms 💬
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
EMAIL_PASSWORD=password
More information about Docker. To run the application type this command in the root folder.
$ docker compose up
You might have to run this command twice if it doesn't work the first time :)
For authentication are used bearer tokens.
-
User
/api/users
-
[GET]
/
- Get all users -
[GET]
/:id
- Get user by ID -
[GET]
/current
- Get current user -
[POST]
/send-otp
- Send OTP email{ "email": "[email protected]" }
-
[POST]
/register
- Register user{ "firstName": "First", "lastName": "Last", "email": "[email protected]", "password": "password" }
-
[POST]
/register-otp
- Register user with OTP{ "firstName": "First", "lastName": "Last", "email": "[email protected]", "phone": "123456789", "password": "password", "otp": "123456" }
-
[POST]
/login
- Login user{ "email": "[email protected]", "password": "password" }
-
[POST]
/login-otp
- Login user with OTP{ "email": "[email protected]", "password": "password", "otp": "123456" }
-
[GET]
/email/:email
- Search user by email -
[PUT]
/update
- Update user{ "firstName": "First", "lastName": "Last", "email": "[email protected]", "phone": "123456789" }
-
[PUT]
/update-otp
- Update user with OTP{ "firstName": "First", "lastName": "Last", "email": "[email protected]", "phone": "123456789", "otp": "123456" }
-
[POST]
/:id/roles/:role
- Add role to user -
[DELETE]
/:id/roles/:role
- Remove role from user
-
-
Room
/api/rooms
-
[GET]
/
- Get all rooms -
[GET]
/:id
- Get room by ID -
[POST]
/
- Create room{ "name": "room" }
-
[PUT]
/:id
- Update room by ID{ "name": "updated room" }
-
[DELETE]
/:id
- Delete room by ID -
[POST]
/:room_id/users/:user_id
- Add user to room -
[DELETE]
/:room_id/users/:user_id
- Remove user from room
-
-
Message
/api/messages
-
[GET]
/:room_id?page=1&size=10
- Get paginated messages by room ID -
[POST]
/:room_id
- Create message{ "text": "Hello World!" }
-
[PUT]
/:id
- Update message by ID{ "text": "Goodbye World!" }
-
[DELETE]
/:id
- Delete message by ID
-
-
WebSocket
/api/ws
- [GET]
/
- Connect to all user's rooms
- [GET]
-
Create Message
{ "text": "Hello World!", "command": "CreateMessage", "targetId": "room_id", "roomId": "room_id" }
-
Update Message
{ "text": "Goodbye World!", "command": "UpdateMessage", "targetId": "message_id", "roomId": "room_id" }
-
Delete Message
{ "text": "anything", "command": "DeleteMessage", "targetId": "message_id", "roomId": "room_id" }
-
Add User to Room
{ "text": "anything", "command": "AddUser", "targetId": "user_id", "roomId": "room_id" }
-
Remove User from Room
{ "text": "anything", "command": "RemoveUser", "targetId": "user_id", "roomId": "room_id" }
-
Create Room
{ "text": "Room Name", "command": "CreateRoom", "targetId": "anything", "roomId": "anything" }
-
Update Room
{ "text": "New Room Name", "command": "UpdateRoom", "targetId": "room_id", "roomId": "room_id" }
-
Delete Room
{ "text": "anything", "command": "DeleteRoom", "targetId": "room_id", "roomId": "room_id" }