diff --git a/.gitignore b/.gitignore index 812ab26..032c396 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ chat -.vscode \ No newline at end of file +.vscode +chat.exe +.gitignore \ No newline at end of file diff --git a/chat.service b/chat.service new file mode 100644 index 0000000..304ef15 --- /dev/null +++ b/chat.service @@ -0,0 +1,12 @@ +[Unit] +Description=chat +Requires=postgresql.service +After=postgresql.service +[Service] +WorkingDirectory=/opt/chat +ExecStart=/opt/chat/chat +ExecReload=/opt/chat/chat +TimeoutSec=300 +Restart=always +[Install] +WantedBy=multi-user.target diff --git a/cmd/root.go b/cmd/root.go index 8253e39..2b8af65 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -42,5 +42,6 @@ func (a *App) Run() { logic.ServeWs(c.Writer, c.Request, roomId, a.H) }) - router.Run("0.0.0.0:9080") + router.RunTLS("0.0.0.0:9999", "/etc/letsencrypt/live/mvp02.ditar.ru/fullchain.pem", + "/etc/letsencrypt/live/mvp02.ditar.ru/privkey.pem") } diff --git a/internal/controllers/messages.go b/internal/controllers/messages.go index 1bf8fca..6af1104 100644 --- a/internal/controllers/messages.go +++ b/internal/controllers/messages.go @@ -22,7 +22,16 @@ func listMessages(store storage.Storage, router *gin.Engine) { query := c.Request.URL.Query() offset := 0 limit := 100 - history := store.ListMessages(roomId) + offst := query.Get("offset") + + if val, err := strconv.Atoi(offst); offst != "" && err == nil { + offset = val + } + lmt := query.Get("limit") + if val, err := strconv.Atoi(lmt); lmt != "" && err == nil { + limit = val + } + history := store.ListMessages(roomId, offset, limit) if history == nil { c.IndentedJSON(http.StatusBadRequest, nil) @@ -32,22 +41,9 @@ func listMessages(store storage.Storage, router *gin.Engine) { if limit > msgLen { limit = msgLen } - offst := query.Get("offset") - - if val, err := strconv.Atoi(offst); offst != "" && err == nil { - if val < msgLen { - offset = val - } - } - lmt := query.Get("limit") - if val, err := strconv.Atoi(lmt); lmt != "" && err == nil { - if val < msgLen { - limit = val - } - } c.IndentedJSON(http.StatusOK, types.MessageHistory{ - Data: history.Data[offset : offset+limit], + Data: history.Data, Total: len(history.Data), }) }) diff --git a/internal/controllers/participants.go b/internal/controllers/participants.go index 23f329e..276281f 100644 --- a/internal/controllers/participants.go +++ b/internal/controllers/participants.go @@ -18,14 +18,21 @@ func createParticipant(store storage.Storage, router *gin.Engine) { jsonData, err := ioutil.ReadAll(c.Request.Body) if err != nil { fmt.Println(err.Error()) + c.IndentedJSON(http.StatusBadRequest, nil) + return } pers := &types.Client{} err = json.Unmarshal(jsonData, pers) if err != nil { fmt.Println(err.Error()) + c.IndentedJSON(http.StatusBadRequest, nil) + return } - // id := uuid.New() - // pers.ID = id.String() + if pers.Name == "" { + c.IndentedJSON(http.StatusBadRequest, nil) + return + } + pers.CreatedAt = time.Now() pers.UpdatedAt = time.Now() store.StoreParticipant(*pers) @@ -40,13 +47,9 @@ func listParticipants(store storage.Storage, router *gin.Engine) { } func readParticipant(store storage.Storage, router *gin.Engine) { - router.GET("/v1/participants/:id", func(c *gin.Context) { - clientID := c.Param("id") - ID, err := strconv.Atoi(clientID) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, err) - } - part := store.GetParticipant(uint(ID)) + router.GET("/v1/participants/:external_id", func(c *gin.Context) { + clientID := c.Param("external_id") + part := store.GetParticipant(clientID) if part.ID == 0 { c.IndentedJSON(http.StatusNotFound, nil) return diff --git a/internal/controllers/rooms.go b/internal/controllers/rooms.go index 07cfc2c..d55c456 100644 --- a/internal/controllers/rooms.go +++ b/internal/controllers/rooms.go @@ -28,11 +28,21 @@ func createRoom(store storage.Storage, router *gin.Engine) { c.IndentedJSON(http.StatusBadRequest, nil) return } + if room.Name == "" { + c.IndentedJSON(http.StatusBadRequest, nil) + return + } room.CreatedAt = time.Now() room.UpdatedAt = time.Now() room.Participants = pq.Int64Array{} room.PinnedMessages = pq.Int64Array{} - store.AddRoom(room) + err = store.AddRoom(room) + if err != nil { + c.Error(err) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"status": false, "message": err.Error()}) + // c.IndentedJSON(http.StatusBadRequest, nil) + return + } c.IndentedJSON(http.StatusCreated, nil) }) } @@ -44,7 +54,12 @@ func readRoom(store storage.Storage, router *gin.Engine) { c.IndentedJSON(http.StatusBadRequest, nil) return } - c.IndentedJSON(http.StatusOK, store.GetRoom(room)) + rm := store.GetRoom(room) + if rm == nil || rm.Name == "" { + c.IndentedJSON(http.StatusNotFound, nil) + return + } + c.IndentedJSON(http.StatusOK, rm) }) } @@ -53,17 +68,27 @@ func updateRoom(store storage.Storage, router *gin.Engine) { jsonData, err := ioutil.ReadAll(c.Request.Body) if err != nil { fmt.Println(err.Error()) - c.IndentedJSON(http.StatusBadRequest, nil) + c.Error(err) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"status": false, "message": err.Error()}) + // c.IndentedJSON(http.StatusBadRequest, nil) } room := &types.Room{} err = json.Unmarshal(jsonData, room) if err != nil { fmt.Println(err.Error()) - c.IndentedJSON(http.StatusBadRequest, nil) + c.Error(err) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"status": false, "message": err.Error()}) + // c.IndentedJSON(http.StatusBadRequest, nil) return } roomId := c.Param("roomId") - store.EditRoom(roomId, room) + err = store.EditRoom(roomId, room) + if err != nil { + c.Error(err) + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"status": false, "message": err.Error()}) + // c.IndentedJSON(http.StatusBadRequest, nil) + return + } c.IndentedJSON(http.StatusOK, nil) }) } diff --git a/internal/logic/client.go b/internal/logic/client.go index 4f7c3c2..ea9ff3e 100644 --- a/internal/logic/client.go +++ b/internal/logic/client.go @@ -25,6 +25,9 @@ const ( var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }, } // connection is an middleman between the websocket connection and the hub. diff --git a/internal/logic/hub.go b/internal/logic/hub.go index 8b4fdb4..efb6a31 100644 --- a/internal/logic/hub.go +++ b/internal/logic/hub.go @@ -75,19 +75,21 @@ func (h *Hub) Run() { } case m := <-h.broadcast: connections := h.rooms[m.room] + stored := false for c := range connections { select { case c.send <- m.data: - msg := &types.Message{} - err := json.Unmarshal(m.data, msg) - if err != nil { - fmt.Print(err.Error()) - break + if !stored { + msg := &types.Message{} + err := json.Unmarshal(m.data, msg) + if err != nil { + fmt.Print(err.Error()) + break + } + h.storage.StoreMessage(*msg, m.room) + stored = true } - // msg.ID = uuid.New().String() - // msg.CreationTime = time.Now() - // msg.EditTime = time.Now() - h.storage.StoreMessage(*msg, m.room) + default: close(c.send) delete(connections, c) diff --git a/internal/storage/postgres/messages.go b/internal/storage/postgres/messages.go index 66e50e6..07bed31 100644 --- a/internal/storage/postgres/messages.go +++ b/internal/storage/postgres/messages.go @@ -2,16 +2,20 @@ package postgres import ( "chat/internal/types" + "log" ) func (s *PostgresStorage) StoreMessage(msg types.Message, room string) { msg.Room = room - s.db.Save(&msg) + tx := s.db.Save(&msg) + if tx.Error != nil { + log.Println(tx.Error.Error()) + } } -func (s *PostgresStorage) ListMessages(room string) *types.MessageHistory { +func (s *PostgresStorage) ListMessages(room string, offset, limit int) *types.MessageHistory { msgs := []types.Message{} - s.db.Where("room = ?", room).Find(&msgs) + s.db.Order("id desc").Limit(limit).Offset(offset).Where("room = ?", room).Find(&msgs) return &types.MessageHistory{ Total: len(msgs), Data: msgs, diff --git a/internal/storage/postgres/participants.go b/internal/storage/postgres/participants.go index d9453e8..150e662 100644 --- a/internal/storage/postgres/participants.go +++ b/internal/storage/postgres/participants.go @@ -8,9 +8,9 @@ func (s *PostgresStorage) StoreParticipant(patricipant types.Client) { s.db.Save(&patricipant) } -func (s *PostgresStorage) GetParticipant(id uint) types.Client { +func (s *PostgresStorage) GetParticipant(external_id string) types.Client { cl := &types.Client{} - s.db.First(cl, id) + s.db.Where("external_id = ?", external_id).Find(cl) return *cl } diff --git a/internal/storage/postgres/rooms.go b/internal/storage/postgres/rooms.go index 50f34e8..89a91f9 100644 --- a/internal/storage/postgres/rooms.go +++ b/internal/storage/postgres/rooms.go @@ -29,12 +29,14 @@ func (s *PostgresStorage) GetRoom(room string) *types.Room { return rm } -func (s *PostgresStorage) AddRoom(room *types.Room) { - s.db.Save(room) +func (s *PostgresStorage) AddRoom(room *types.Room) error { + tx := s.db.Save(room) + return tx.Error } -func (s *PostgresStorage) EditRoom(prevName string, room *types.Room) { - s.db.Save(room) +func (s *PostgresStorage) EditRoom(prevName string, room *types.Room) error { + tx := s.db.Save(room) + return tx.Error } func (s *PostgresStorage) DeleteRoom(room string) { diff --git a/internal/storage/postgres/storage.go b/internal/storage/postgres/storage.go index fab3690..a40d87a 100644 --- a/internal/storage/postgres/storage.go +++ b/internal/storage/postgres/storage.go @@ -12,7 +12,7 @@ type PostgresStorage struct { } func NewPostgresStorageStorage() *PostgresStorage { - dsn := "host=localhost user=postgres password=dev dbname=chat port=5432 sslmode=disable TimeZone=Europe/Moscow" + dsn := "host=localhost user=postgres password=postgres dbname=chat port=5432 sslmode=disable TimeZone=Europe/Moscow" db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { return nil diff --git a/internal/storage/storage.go b/internal/storage/storage.go index 1cb0177..6b0132e 100644 --- a/internal/storage/storage.go +++ b/internal/storage/storage.go @@ -15,13 +15,13 @@ type MessageStorage interface { StoreMessage(msg types.Message, room string) EditMessage(msg types.Message, room string) GetMessage(uid uint, room string) types.Message - ListMessages(room string) *types.MessageHistory + ListMessages(room string, offset, limit int) *types.MessageHistory } type ParticipantStorage interface { StoreParticipant(participant types.Client) DeleteParticipant(uid uint) - GetParticipant(uid uint) types.Client + GetParticipant(external_id string) types.Client EditParticipant(participant types.Client) ListParticipants() types.ClientList } @@ -30,8 +30,8 @@ type RoomsStorage interface { ListRooms() *types.ShortRoomInfoList GetRoom(room string) *types.Room CheckRoom(room string) bool - AddRoom(room *types.Room) - EditRoom(prevName string, room *types.Room) + AddRoom(room *types.Room) error + EditRoom(prevName string, room *types.Room) error DeleteRoom(room string) AddParticipantInRoom(patricipant uint, room string) ListParticipantsInRoom(room string) types.ClientList diff --git a/internal/types/room.go b/internal/types/room.go index d2be6df..16b49f3 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -13,7 +13,7 @@ type Room struct { } type ShortRoomInfo struct { - Name string + Name string `gorm:"unique"` } type ShortRoomInfoList struct {