notifications

main
alex 2023-09-03 16:37:06 +02:00
parent e8a81ec85e
commit 6dbfcec0c5
11 changed files with 400 additions and 1 deletions

View File

@ -44,4 +44,5 @@ func InitDatabase() {
db.AutoMigrate(&structs.RolePermission{}) db.AutoMigrate(&structs.RolePermission{})
db.AutoMigrate(&structs.UserApiKey{}) db.AutoMigrate(&structs.UserApiKey{})
db.AutoMigrate(&structs.EquipmentDocumentation{}) db.AutoMigrate(&structs.EquipmentDocumentation{})
db.AutoMigrate(&structs.Notification{})
} }

View File

@ -0,0 +1,127 @@
package notification
import (
"jannex/admin-dashboard-backend/modules/database"
"jannex/admin-dashboard-backend/modules/structs"
"jannex/admin-dashboard-backend/modules/utils"
"jannex/admin-dashboard-backend/socketclients"
"time"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
func GetTotalNotifications(userId string) int {
var totalNotifications int64
database.DB.Model(&structs.Notification{}).Where("user_id = ?", userId).Count(&totalNotifications)
return int(totalNotifications)
}
func GetNotifications(userId string) []structs.Notification {
var notifications []structs.Notification
database.DB.Model(&structs.Notification{}).Where("user_id = ?", userId).Find(&notifications)
return notifications
}
func AddNotification(c *fiber.Ctx, body structs.AddNotificationRequest) error {
log.Info().Msgf("body %v", body)
var userIds []string
if len(body.UserIds) == 0 && body.NeededPermission == "" && len(body.RoleIds) == 0 {
return c.SendStatus(fiber.StatusBadRequest)
}
if len(body.UserIds) > 0 {
// check if user with provided user id in body.UserIds exist in database and add to userIds
database.DB.Model(&structs.User{}).Where("id IN ?", body.UserIds).Select("id").Find(&userIds)
}
var roleIds []string
if body.NeededPermission != "" {
// fetch all role_ids from role_permissions and use the role_id to get all users which are in the role from database which have the permission provided in body.NeededPermission and add to userIds
database.DB.Model(&structs.RolePermission{}).Where("permission_id = ?", body.NeededPermission).Select("role_id").Find(&roleIds)
var userIdsTemp []string
database.DB.Model(&structs.User{}).Where("role_id IN ?", roleIds).Select("id").Find(&userIdsTemp)
userIds = append(userIds, userIdsTemp...)
}
if len(body.RoleIds) > 0 {
var userIdsTemp []string
database.DB.Model(&structs.User{}).Where("role_id IN ?", body.RoleIds).Select("id").Find(&userIdsTemp)
userIds = append(userIds, userIdsTemp...)
}
// no users found
if len(userIds) == 0 {
return c.SendStatus(fiber.StatusUnprocessableEntity)
}
// remove duplicates
userIds = unique(userIds)
// add notification to database
for _, userId := range userIds {
notify := structs.Notification{
Id: uuid.New().String(),
UserId: userId,
Type: body.Type,
Title: body.Title,
CreatedAt: time.Now(),
}
database.DB.Create(&notify)
// send notification to user
socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
Cmd: utils.SentCmdNewNotification,
Body: notify,
})
}
log.Info().Msgf("ids %v", userIds)
return c.SendStatus(fiber.StatusOK)
}
func unique(slice []string) []string {
keys := make(map[string]bool)
list := []string{}
for _, entry := range slice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
func DeleteAllNotifications(userId string) {
database.DB.Delete(&structs.Notification{}, "user_id = ?", userId)
socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
Cmd: utils.SentCmdAllNotificationsDeleted,
})
}
func DeleteOneNotification(userId string, notificationId string) {
database.DB.Delete(&structs.Notification{}, "user_id = ? AND id = ?", userId, notificationId)
socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
Cmd: utils.SentCmdOneNotificationDeleted,
Body: notificationId,
})
}

View File

@ -0,0 +1,29 @@
package structs
import "time"
type Notification struct {
Id string
// success = 1, info = 2, warning = 3, error = 4
Type uint8
UserId string
Title string
CreatedAt time.Time
}
// swagger:model AddNotificationRequest
type AddNotificationRequest struct {
Type uint8
// send notification to specific users
UserIds []string
// send notification to users with specific permission
NeededPermission string
// send notification to users with specific role
RoleIds []string
Title string
}
// swagger:model NotificationsResponse
type NotificationsResponse struct {
Notifications []Notification
}

View File

@ -64,6 +64,7 @@ type UserInfoResponse struct {
Permissions []string Permissions []string
AvailableCategoryGroups []string AvailableCategoryGroups []string
Users []AllUsers Users []AllUsers
TotalNotifications int
} }
// swagger:model UserProfileResponse // swagger:model UserProfileResponse

View File

@ -90,6 +90,9 @@ const (
SentCmdInstallingGlobalPythonPackagesFinished = 38 SentCmdInstallingGlobalPythonPackagesFinished = 38
SentCmdUpdateUsers = 39 SentCmdUpdateUsers = 39
SentCmdCheckingForGroupTasksCategoryGroupChanges = 40 SentCmdCheckingForGroupTasksCategoryGroupChanges = 40
SentCmdNewNotification = 41
SentCmdAllNotificationsDeleted = 42
SentCmdOneNotificationDeleted = 43
) )
// commands received from web clients // commands received from web clients
@ -117,6 +120,8 @@ const (
ReceivedCmdGroupTasksInstallPythonPackages = 21 ReceivedCmdGroupTasksInstallPythonPackages = 21
ReceivedCmdGroupTasksInstallGlobalPythonPackages = 22 ReceivedCmdGroupTasksInstallGlobalPythonPackages = 22
ReceivedCmdSubscribeToTopic = 23 ReceivedCmdSubscribeToTopic = 23
ReceivedCmdDeleteAllNotifications = 24
ReceivedCmdDeleteOneNotification = 25
) )
const ( const (

View File

@ -416,6 +416,86 @@
} }
} }
}, },
"/notifications": {
"get": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"notifications"
],
"summary": "Get notifications",
"operationId": "notificationsGetNotifications",
"parameters": [
{
"description": "You can create a new api key in your user profile",
"name": "X-Api-Key",
"in": "header"
}
],
"responses": {
"200": {
"description": "Notifications",
"schema": {
"$ref": "#/definitions/NotificationsResponse"
}
},
"401": {
"description": "No permissions"
},
"500": {
"description": "Failed to get notifications"
}
}
},
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"notifications"
],
"summary": "Add a new notification",
"operationId": "notificationsAddNotification",
"parameters": [
{
"description": "You can create a new api key in your user profile",
"name": "X-Api-Key",
"in": "header"
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/AddNotificationRequest"
}
}
],
"responses": {
"200": {
"description": "New notification added successfully"
},
"400": {
"description": "Invalid request body"
},
"401": {
"description": "No permissions"
},
"422": {
"description": "No users found"
},
"500": {
"description": "Failed to add notification"
}
}
}
},
"/user/auth/login": { "/user/auth/login": {
"post": { "post": {
"consumes": [ "consumes": [
@ -686,6 +766,37 @@
} }
}, },
"definitions": { "definitions": {
"AddNotificationRequest": {
"type": "object",
"properties": {
"NeededPermission": {
"description": "send notification to users with specific permission",
"type": "string"
},
"RoleIds": {
"description": "send notification to users with specific role",
"type": "array",
"items": {
"type": "string"
}
},
"Title": {
"type": "string"
},
"Type": {
"type": "integer",
"format": "uint8"
},
"UserIds": {
"description": "send notification to specific users",
"type": "array",
"items": {
"type": "string"
}
}
},
"x-go-package": "jannex/admin-dashboard-backend/modules/structs"
},
"AdminAreaRolesResponse": { "AdminAreaRolesResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -1058,6 +1169,42 @@
"x-go-name": "GroupTaskStepsRequest", "x-go-name": "GroupTaskStepsRequest",
"x-go-package": "jannex/admin-dashboard-backend/modules/structs" "x-go-package": "jannex/admin-dashboard-backend/modules/structs"
}, },
"Notification": {
"type": "object",
"properties": {
"CreatedAt": {
"type": "string",
"format": "date-time"
},
"Id": {
"type": "string"
},
"Title": {
"type": "string"
},
"Type": {
"description": "success = 1, info = 2, warning = 3, error = 4",
"type": "integer",
"format": "uint8"
},
"UserId": {
"type": "string"
}
},
"x-go-package": "jannex/admin-dashboard-backend/modules/structs"
},
"NotificationsResponse": {
"type": "object",
"properties": {
"Notifications": {
"type": "array",
"items": {
"$ref": "#/definitions/Notification"
}
}
},
"x-go-package": "jannex/admin-dashboard-backend/modules/structs"
},
"Role": { "Role": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -1206,6 +1353,10 @@
"type": "string" "type": "string"
} }
}, },
"TotalNotifications": {
"type": "integer",
"format": "int64"
},
"UserId": { "UserId": {
"type": "string" "type": "string"
}, },

View File

@ -0,0 +1,73 @@
package notification
import (
"jannex/admin-dashboard-backend/modules/notification"
"jannex/admin-dashboard-backend/modules/structs"
"jannex/admin-dashboard-backend/modules/utils"
"github.com/gofiber/fiber/v2"
)
func AddNotification(c *fiber.Ctx) error {
// swagger:operation POST /notifications notifications notificationsAddNotification
// ---
// summary: Add a new notification
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: X-Api-Key
// in: header
// description: You can create a new api key in your user profile
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/AddNotificationRequest"
// responses:
// '200':
// description: New notification added successfully
// '400':
// description: Invalid request body
// '401':
// description: No permissions
// '422':
// description: No users found
// '500':
// description: Failed to add notification
var body structs.AddNotificationRequest
if err := utils.BodyParserHelper(c, &body); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
return notification.AddNotification(c, body)
}
func GetNotifications(c *fiber.Ctx) error {
// swagger:operation GET /notifications notifications notificationsGetNotifications
// ---
// summary: Get notifications
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: X-Api-Key
// in: header
// description: You can create a new api key in your user profile
// responses:
// '200':
// description: Notifications
// schema:
// "$ref": "#/definitions/NotificationsResponse"
// '401':
// description: No permissions
// '500':
// description: Failed to get notifications
return c.JSON(structs.NotificationsResponse{
Notifications: notification.GetNotifications(c.Locals("userId").(string)),
})
}

View File

@ -3,6 +3,7 @@ package user
import ( import (
"jannex/admin-dashboard-backend/modules/cache" "jannex/admin-dashboard-backend/modules/cache"
"jannex/admin-dashboard-backend/modules/database" "jannex/admin-dashboard-backend/modules/database"
"jannex/admin-dashboard-backend/modules/notification"
"jannex/admin-dashboard-backend/modules/structs" "jannex/admin-dashboard-backend/modules/structs"
"jannex/admin-dashboard-backend/modules/utils" "jannex/admin-dashboard-backend/modules/utils"
"jannex/admin-dashboard-backend/socketclients" "jannex/admin-dashboard-backend/socketclients"
@ -55,5 +56,6 @@ func UserInfo(c *fiber.Ctx) error {
Permissions: socketclients.GetPermissionsByRoleId(user.RoleId), Permissions: socketclients.GetPermissionsByRoleId(user.RoleId),
AvailableCategoryGroups: categories, AvailableCategoryGroups: categories,
Users: socketclients.GetAllUsers(), Users: socketclients.GetAllUsers(),
TotalNotifications: notification.GetTotalNotifications(user.Id),
}) })
} }

View File

@ -10,6 +10,7 @@ import (
"jannex/admin-dashboard-backend/routers/router/api/v1/equipment" "jannex/admin-dashboard-backend/routers/router/api/v1/equipment"
"jannex/admin-dashboard-backend/routers/router/api/v1/grouptasks" "jannex/admin-dashboard-backend/routers/router/api/v1/grouptasks"
log "jannex/admin-dashboard-backend/routers/router/api/v1/logger" log "jannex/admin-dashboard-backend/routers/router/api/v1/logger"
"jannex/admin-dashboard-backend/routers/router/api/v1/notification"
"jannex/admin-dashboard-backend/routers/router/api/v1/user" "jannex/admin-dashboard-backend/routers/router/api/v1/user"
"jannex/admin-dashboard-backend/routers/router/api/v1/users" "jannex/admin-dashboard-backend/routers/router/api/v1/users"
"jannex/admin-dashboard-backend/socketclients" "jannex/admin-dashboard-backend/socketclients"
@ -56,6 +57,10 @@ func SetupRoutes(app *fiber.App) {
a := v1.Group("/adminarea") a := v1.Group("/adminarea")
a.Get("/roles", requestAccessValidation, adminarea.GetRoles) a.Get("/roles", requestAccessValidation, adminarea.GetRoles)
ns := v1.Group("/notifications")
ns.Get("/", requestAccessValidation, notification.GetNotifications)
ns.Post("/", requestAccessValidation, notification.AddNotification)
app.Static("/", config.Cfg.FolderPaths.PublicStatic) app.Static("/", config.Cfg.FolderPaths.PublicStatic)
} }

View File

@ -51,7 +51,6 @@ func BroadcastMessageToTopicExceptUserSessionId(topic string, ignoreUserSessionI
} }
func hasClientSubscribedToTopic(topic string, clientTopic string) bool { func hasClientSubscribedToTopic(topic string, clientTopic string) bool {
log.Info().Msgf("hasClientSubscribedToTopic %s %s", topic, clientTopic)
return clientTopic == topic || strings.HasPrefix(clientTopic, topic) return clientTopic == topic || strings.HasPrefix(clientTopic, topic)
} }

View File

@ -7,6 +7,7 @@ import (
"jannex/admin-dashboard-backend/modules/database" "jannex/admin-dashboard-backend/modules/database"
"jannex/admin-dashboard-backend/modules/grouptasks" "jannex/admin-dashboard-backend/modules/grouptasks"
"jannex/admin-dashboard-backend/modules/logger" "jannex/admin-dashboard-backend/modules/logger"
"jannex/admin-dashboard-backend/modules/notification"
"jannex/admin-dashboard-backend/modules/structs" "jannex/admin-dashboard-backend/modules/structs"
"jannex/admin-dashboard-backend/modules/systempermissions" "jannex/admin-dashboard-backend/modules/systempermissions"
"jannex/admin-dashboard-backend/modules/utils" "jannex/admin-dashboard-backend/modules/utils"
@ -356,6 +357,11 @@ func RunHub() {
cache.SubscribeSocketClientToTopic(data.Conn.Locals("sessionId").(string), receivedMessage.Body["topic"].(string)) cache.SubscribeSocketClientToTopic(data.Conn.Locals("sessionId").(string), receivedMessage.Body["topic"].(string))
break break
case utils.ReceivedCmdDeleteAllNotifications:
notification.DeleteAllNotifications(data.Conn.Locals("userId").(string))
break
case utils.ReceivedCmdDeleteOneNotification:
notification.DeleteOneNotification(data.Conn.Locals("userId").(string), receivedMessage.Body["notificationId"].(string))
default: default:
log.Error().Msgf("Received unknown message: %v", receivedMessage) log.Error().Msgf("Received unknown message: %v", receivedMessage)
break break