1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package consoleapi 5 6import ( 7 "encoding/json" 8 "net/http" 9 "strconv" 10 11 "github.com/gorilla/mux" 12 "github.com/zeebo/errs" 13 "go.uber.org/zap" 14 15 "storj.io/common/uuid" 16 "storj.io/storj/storagenode/notifications" 17) 18 19// ErrNotificationsAPI - console notifications api error type. 20var ErrNotificationsAPI = errs.Class("consoleapi notifications") 21 22// Notifications is an api controller that exposes all notifications related api. 23type Notifications struct { 24 service *notifications.Service 25 26 log *zap.Logger 27} 28 29// Page contains notifications and related information. 30type Page struct { 31 Notifications []notifications.Notification `json:"notifications"` 32 33 Offset uint64 `json:"offset"` 34 Limit uint `json:"limit"` 35 CurrentPage uint `json:"currentPage"` 36 PageCount uint `json:"pageCount"` 37} 38 39// NewNotifications is a constructor for notifications controller. 40func NewNotifications(log *zap.Logger, service *notifications.Service) *Notifications { 41 return &Notifications{ 42 log: log, 43 service: service, 44 } 45} 46 47// ReadNotification updates specific notification in database as read. 48func (notification *Notifications) ReadNotification(w http.ResponseWriter, r *http.Request) { 49 ctx := r.Context() 50 var err error 51 defer mon.Task()(&ctx)(&err) 52 53 w.Header().Set(contentType, applicationJSON) 54 55 params := mux.Vars(r) 56 id, ok := params["id"] 57 if !ok { 58 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 59 return 60 } 61 62 notificationID, err := uuid.FromString(id) 63 if err != nil { 64 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 65 return 66 } 67 68 err = notification.service.Read(ctx, notificationID) 69 if err != nil { 70 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 71 return 72 } 73} 74 75// ReadAllNotifications updates all notifications in database as read. 76func (notification *Notifications) ReadAllNotifications(w http.ResponseWriter, r *http.Request) { 77 ctx := r.Context() 78 var err error 79 defer mon.Task()(&ctx)(&err) 80 81 w.Header().Set(contentType, applicationJSON) 82 83 err = notification.service.ReadAll(ctx) 84 if err != nil { 85 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 86 return 87 } 88} 89 90// ListNotifications returns listed page of notifications from database. 91func (notification *Notifications) ListNotifications(w http.ResponseWriter, r *http.Request) { 92 ctx := r.Context() 93 var err error 94 defer mon.Task()(&ctx)(&err) 95 96 w.Header().Set(contentType, applicationJSON) 97 98 limit, err := strconv.ParseUint(r.URL.Query().Get("limit"), 10, 32) 99 if err != nil { 100 notification.serveJSONError(w, http.StatusBadRequest, ErrNotificationsAPI.Wrap(err)) 101 return 102 } 103 104 page, err := strconv.ParseUint(r.URL.Query().Get("page"), 10, 32) 105 if err != nil { 106 notification.serveJSONError(w, http.StatusBadRequest, ErrNotificationsAPI.Wrap(err)) 107 return 108 } 109 110 cursor := notifications.Cursor{ 111 Limit: uint(limit), 112 Page: uint(page), 113 } 114 115 notificationList, err := notification.service.List(ctx, cursor) 116 if err != nil { 117 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 118 return 119 } 120 121 unreadCount, err := notification.service.UnreadAmount(ctx) 122 if err != nil { 123 notification.serveJSONError(w, http.StatusInternalServerError, ErrNotificationsAPI.Wrap(err)) 124 return 125 } 126 127 var result struct { 128 Page Page `json:"page"` 129 UnreadCount int `json:"unreadCount"` 130 TotalCount int `json:"totalCount"` 131 } 132 133 result.Page.Notifications = notificationList.Notifications 134 result.Page.Limit = notificationList.Limit 135 result.Page.CurrentPage = notificationList.CurrentPage 136 result.Page.Offset = notificationList.Offset 137 result.Page.PageCount = notificationList.PageCount 138 result.UnreadCount = unreadCount 139 result.TotalCount = int(notificationList.TotalCount) 140 141 if err := json.NewEncoder(w).Encode(result); err != nil { 142 notification.log.Error("failed to encode json list notifications response", zap.Error(ErrNotificationsAPI.Wrap(err))) 143 return 144 } 145} 146 147// serveJSONError writes JSON error to response output stream. 148func (notification *Notifications) serveJSONError(w http.ResponseWriter, status int, err error) { 149 w.WriteHeader(status) 150 151 var response struct { 152 Error string `json:"error"` 153 } 154 155 response.Error = err.Error() 156 157 err = json.NewEncoder(w).Encode(response) 158 if err != nil { 159 notification.log.Error("failed to write json error response", zap.Error(ErrNotificationsAPI.Wrap(err))) 160 return 161 } 162} 163