Skip to content

Commit

Permalink
some refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
ruskaof committed Mar 28, 2024
1 parent 38d78cc commit 03e5e45
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 96 deletions.
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func main() {
hub := chat.NewHub()
go hub.Run()

userService := usrserv.NewUserService(hub, db)
userDao := dao.NewUserDao(db)
userService := usrserv.NewUserService(hub, userDao)
userController := handler.NewUserController(userService)
userController.InitHandlers(hub)

Expand Down
14 changes: 9 additions & 5 deletions src/dto/dto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dto

import "encoding/json"
import (
"encoding/json"
"time"
)

type UserInfo struct {
UserId int64 `json:"id"`
Expand All @@ -17,10 +20,11 @@ type WebSocketClientMessage struct {
}

type WebSocketServerMessage struct {
ChatId int64 `json:"chatId"`
UserId int64 `json:"userId"`
Username string `json:"username"`
Message string `json:"message"`
ChatId int64 `json:"chatId"`
UserId int64 `json:"userId"`
Username string `json:"username"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
}

func SerializeWebSocketServerMessage(message WebSocketServerMessage) []byte {
Expand Down
6 changes: 1 addition & 5 deletions src/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,7 @@ func (uc UserController) handlerFindDirectChat(w http.ResponseWriter, r *http.Re
return
}

chatId, badUsers, err := uc.userService.FindDirectChat(requesterUid, userId) // todo: show old messages
if badUsers {
w.WriteHeader(http.StatusNotFound)
return
}
chatId, err := uc.userService.FindDirectChat(requesterUid, userId) // todo: show old messages
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
Expand Down
98 changes: 98 additions & 0 deletions src/model/dao/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package dao

import (
"dwelt/src/config"
"dwelt/src/model/entity"
"dwelt/src/utils"
"errors"
"fmt"

"gorm.io/driver/postgres"
Expand All @@ -28,3 +30,99 @@ func InitDB() *gorm.DB {
}),
)
}

type UserDao struct {
db *gorm.DB
}

func NewUserDao(db *gorm.DB) *UserDao {
return &UserDao{db: db}
}

func (ud *UserDao) FindUserByUsernameAndPassword(username, password string) (*entity.User, error) {
users := make([]entity.User, 0)
err := ud.db.Where("username = ? AND password = ?", username, password).Find(&users).Error
if err != nil {
return nil, err
}

if len(users) == 0 {
return nil, nil
}

return &users[0], nil
}

func (ud *UserDao) FindUserById(userId int64) (*entity.User, error) {
users := make([]entity.User, 0)
err := ud.db.Where("id = ?", userId).Find(&users).Error
if err != nil {
return nil, err
}

if len(users) == 0 {
return nil, nil
}

return &users[0], nil
}

// CreateUser creates a new user in the database
func (ud *UserDao) CreateUser(username, password string) (userId int64, duplicate bool, err error) {
user := entity.User{
Username: username,
Password: password,
}

err = ud.db.Create(&user).Error
if errors.Is(err, gorm.ErrDuplicatedKey) {
return 0, true, nil
}

return user.ID, false, err
}

// SearchUsers searches for users with a username that starts with the given prefix
func (ud *UserDao) SearchUsers(prefix string, limit int) (users []entity.User, err error) {
err = ud.db.Where("username LIKE ?", prefix+"%").Limit(limit).Find(&users).Error
return
}

func (ud *UserDao) FindDirectChat(userId1, userId2 int64) (*entity.Chat, error) {
var chatEntity entity.Chat
err := ud.db.
Joins("JOIN users_chats uc1 ON chats.id = uc1.chat_id").
Joins("JOIN users_chats uc2 ON uc1.chat_id = uc2.chat_id").
Where("uc1.user_id = ?", userId1).
Where("uc2.user_id = ?", userId2).
First(&chatEntity).Error
if err != nil {
return nil, err
}
return &chatEntity, nil
}

// CreateChat creates a new chat in the database
func (ud *UserDao) CreateChat(chat *entity.Chat) (chatId int64, err error) {
err = ud.db.Create(chat).Error
return chat.ID, err
}

func (ud *UserDao) FindChatById(chatId int64) (*entity.Chat, error) {
chat := &entity.Chat{
ID: chatId,
}
err := ud.db.Preload("Users").First(chat).Error
return chat, err
}

// SaveMessage saves a message in the database
func (ud *UserDao) SaveMessage(message *entity.Message) error {
return ud.db.Create(message).Error
}

// FindMessagesByChatId finds messages by chat id
func (ud *UserDao) FindMessagesByChatId(chatId int64, limit int32) (messages []entity.Message) {
ud.db.Where("chat_id = ?", chatId).Limit(int(limit)).Find(&messages)
return
}
7 changes: 7 additions & 0 deletions src/model/entity/enitity.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ type Chat struct {
Name string `gorm:"column:name"`
Users []User `gorm:"many2many:users_chats;"`
}

type Message struct {
ID int64 `gorm:"column:id"`
CreatedAt time.Time `gorm:"column:created_at"`
Text string `gorm:"column:text"`
ChatId int64 `gorm:"column:chat_id"`
}
135 changes: 50 additions & 85 deletions src/service/usrserv/usrserv.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@ package usrserv
import (
"crypto/sha512"
"dwelt/src/dto"
"dwelt/src/model/dao"
"dwelt/src/model/entity"
"dwelt/src/ws/chat"
"encoding/hex"
"errors"
"gorm.io/gorm"
"log/slog"
)

type UserService struct {
wsHub *chat.Hub
db *gorm.DB
wsHub *chat.Hub
userDao *dao.UserDao
}

func NewUserService(wsHub *chat.Hub, db *gorm.DB) *UserService {
func NewUserService(wsHub *chat.Hub, userDao *dao.UserDao) *UserService {
return &UserService{
wsHub: wsHub,
db: db,
wsHub: wsHub,
userDao: userDao,
}
}

Expand All @@ -30,51 +29,32 @@ func hashPassword(password string) string {
}

func (us *UserService) ValidateUser(username string, password string) (userId int64, valid bool, err error) {
var user entity.User

err = us.db.Where("username = ? AND password = ?", username, hashPassword(password)).First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
slog.Debug("User not found", "username", username, "password", password)
err = nil
return
}
user, err := us.userDao.FindUserByUsernameAndPassword(username, hashPassword(password))
if err != nil {
slog.Error(err.Error(), "method", "ValidateUser")
return
}

if user == nil {
valid = false
return
}

userId = user.ID
valid = true

return
}

func (us *UserService) RegisterUser(username string, password string) (userId int64, duplicate bool, err error) {
user := entity.User{
Username: username,
Password: hashPassword(password),
}

err = us.db.Create(&user).Error
if errors.Is(err, gorm.ErrDuplicatedKey) {
duplicate = true
err = nil
return
}
if err != nil {
slog.Error(err.Error(), "method", "RegisterUser")
return
}

userId = user.ID
userId, duplicate, err = us.userDao.CreateUser(username, hashPassword(password))
return
}

func (us *UserService) SearchUsers(prefix string, limit int) (users []dto.UserResponse, err error) {
var usersEntity []entity.User
err = us.db.Where("username LIKE ?", prefix+"%").Limit(limit).Find(&usersEntity).Error
usersEntity, err := us.userDao.SearchUsers(prefix, limit)
if err != nil {
slog.Error(err.Error(), "method", "SearchUsers")
return
}

users = make([]dto.UserResponse, len(usersEntity))
Expand All @@ -85,54 +65,30 @@ func (us *UserService) SearchUsers(prefix string, limit int) (users []dto.UserRe
return
}

func (us *UserService) FindDirectChat(requesterUid int64, directToUid int64) (chatId int64, badUsers bool, err error) {
// check if both users exist
var count int64
err = us.db.Model(&entity.User{}).Where("id IN (?)", []int64{requesterUid, directToUid}).Count(&count).Error
if err != nil {
slog.Error(err.Error(), "method", "FindDirectChat")
return
}
if count != 2 {
badUsers = true
return
}

var chatEntity entity.Chat
err = us.db.
Joins("JOIN users_chats uc1 ON chats.id = uc1.chat_id").
Joins("JOIN users_chats uc2 ON uc1.chat_id = uc2.chat_id").
Where("uc1.user_id = ?", requesterUid).
Where("uc2.user_id = ?", directToUid).
First(&chatEntity).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
err = nil
}
func (us *UserService) FindDirectChat(requesterUid int64, directToUid int64) (chatId int64, err error) {
directChat, err := us.userDao.FindDirectChat(requesterUid, directToUid)
if err != nil {
slog.Error(err.Error(), "method", "CreateDirectChat")
return
}

if chatEntity.ID != 0 {
chatId = chatEntity.ID
return
if directChat != nil {
return directChat.ID, nil
}

// create chat with associated users but don't create the users
chatEntity = entity.Chat{
newChat := entity.Chat{
Name: "",
Users: []entity.User{
{ID: requesterUid},
{ID: directToUid},
},
}
err = us.db.Create(&chatEntity).Error

newChatId, err := us.userDao.CreateChat(&newChat)
if err != nil {
slog.Error(err.Error(), "method", "CreateDirectChat")
return
}

chatId = chatEntity.ID
return
return newChatId, nil
}

func (us *UserService) StartHandlingMessages() {
Expand All @@ -149,38 +105,47 @@ func (us *UserService) StartHandlingMessages() {

func (us *UserService) handleMessage(message chat.IncomingClientMessage) {
// find chat
chatEntity := entity.Chat{
ID: message.Message.ChatId,
}

err := us.db.Model(&entity.Chat{}).Preload("Users").First(&chatEntity, message.Message.ChatId).Error
chatEntity, err := us.userDao.FindChatById(message.Message.ChatId)
if err != nil {
slog.Error(err.Error(), "method", "HandleMessage")
slog.Error(err.Error(), "method", "handleMessage")
return
}

// check if user is in chat
inChat := false
var username string
// check if the user is in the chat
userInChat := false
username := ""
var receiversUserIds []int64
for _, user := range chatEntity.Users {
receiversUserIds = append(receiversUserIds, user.ID)
if user.ID == message.ClientId {
inChat = true
userInChat = true
username = user.Username
}
receiversUserIds = append(receiversUserIds, user.ID)
}

if !inChat {
slog.Error("User is not in chat", "userId", message.ClientId, "chatId", message.Message.ChatId)
if !userInChat {
slog.Error("User not in chat", "method", "handleMessage")
return
}

// save message
messageEntity := entity.Message{
Text: message.Message.Message,
ChatId: message.Message.ChatId,
}

err = us.userDao.SaveMessage(&messageEntity)
if err != nil {
slog.Error(err.Error(), "method", "handleMessage")
return
}

serverMessage := dto.WebSocketServerMessage{
ChatId: message.Message.ChatId,
UserId: message.ClientId,
Username: username,
Message: message.Message.Message,
ChatId: message.Message.ChatId,
UserId: message.ClientId,
Username: username,
Message: message.Message.Message,
CreatedAt: messageEntity.CreatedAt,
}

us.wsHub.SendToSelected(serverMessage, receiversUserIds)
Expand Down

0 comments on commit 03e5e45

Please sign in to comment.