websocket
parent
0538e8b3ea
commit
1232a909c1
1
go.mod
1
go.mod
|
@ -14,6 +14,7 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.6 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/fasthttp/websocket v1.5.3 // indirect
|
github.com/fasthttp/websocket v1.5.3 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -1,5 +1,13 @@
|
||||||
git.ex.umbach.dev/Alex/roese-utils v1.0.21 h1:ae1AHQh8UHJVLbpk5Gf4S5IP0EEWGD1JFIeX9cIcYcc=
|
git.ex.umbach.dev/Alex/roese-utils v1.0.21 h1:ae1AHQh8UHJVLbpk5Gf4S5IP0EEWGD1JFIeX9cIcYcc=
|
||||||
git.ex.umbach.dev/Alex/roese-utils v1.0.21/go.mod h1:hFcnKQl6nuGFEMCxK/eVQBUD6ixBFlAaiy4E2aQqUL8=
|
git.ex.umbach.dev/Alex/roese-utils v1.0.21/go.mod h1:hFcnKQl6nuGFEMCxK/eVQBUD6ixBFlAaiy4E2aQqUL8=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.0 h1:0vhIxeFNHdo4ftVHEGxZ35Mf0jkRuSs9QkfelWFDySc=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.0/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.1 h1:J9sRarL6OJ4JOaE8UH1yykOYdjq5H6TehMDq86269iA=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.1/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.2 h1:RvE0e+Eja/9HOU5reEG2UU18mgDgKYD8gXJOrOntRmc=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.2/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.6 h1:Af+2jD4aC3+4qMgSmTn4nvRosAS5ETTX9JR3gznlGPI=
|
||||||
|
git.ex.umbach.dev/LMS/libcore v1.0.6/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
|
8
main.go
8
main.go
|
@ -7,13 +7,14 @@ import (
|
||||||
|
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rsconfig"
|
"git.ex.umbach.dev/Alex/roese-utils/rsconfig"
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/websocket/v2"
|
"github.com/gofiber/websocket/v2"
|
||||||
"lms.de/backend/modules/config"
|
"lms.de/backend/modules/config"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/logger"
|
"lms.de/backend/modules/logger"
|
||||||
"lms.de/backend/modules/structs"
|
"lms.de/backend/modules/permissions"
|
||||||
"lms.de/backend/modules/utils"
|
"lms.de/backend/modules/utils"
|
||||||
"lms.de/backend/routers/router"
|
"lms.de/backend/routers/router"
|
||||||
"lms.de/backend/socketserver"
|
"lms.de/backend/socketserver"
|
||||||
|
@ -51,6 +52,7 @@ MARIADB_DATABASE_NAME=db_database_name`)
|
||||||
|
|
||||||
utils.ValidatorInit()
|
utils.ValidatorInit()
|
||||||
database.InitDatabase()
|
database.InitDatabase()
|
||||||
|
permissions.InitPermissions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -79,12 +81,12 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate ws session
|
// validate ws session
|
||||||
var userSession structs.UserSession
|
var userSession models.UserSession
|
||||||
|
|
||||||
database.DB.Select("user_id").First(&userSession, "session = ?", sessionId)
|
database.DB.Select("user_id").First(&userSession, "session = ?", sessionId)
|
||||||
|
|
||||||
if userSession.UserId != "" {
|
if userSession.UserId != "" {
|
||||||
var user structs.User
|
var user models.User
|
||||||
|
|
||||||
database.DB.First(&user, "id = ?", userSession.UserId)
|
database.DB.First(&user, "id = ?", userSession.UserId)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
var masterRolePermissions []uint16
|
||||||
|
var muMRP sync.RWMutex
|
||||||
|
|
||||||
|
func SetMasterRolePermissions(permissions []uint16) {
|
||||||
|
muMRP.Lock()
|
||||||
|
masterRolePermissions = permissions
|
||||||
|
muMRP.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMasterRolePermissions() []uint16 {
|
||||||
|
muMRP.RLock()
|
||||||
|
defer muMRP.RUnlock()
|
||||||
|
|
||||||
|
return masterRolePermissions
|
||||||
|
}
|
|
@ -3,11 +3,11 @@ package database
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
"lms.de/backend/modules/config"
|
"lms.de/backend/modules/config"
|
||||||
"lms.de/backend/modules/structs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var DB *gorm.DB
|
var DB *gorm.DB
|
||||||
|
@ -35,15 +35,15 @@ func InitDatabase() {
|
||||||
|
|
||||||
DB = db
|
DB = db
|
||||||
|
|
||||||
db.AutoMigrate(&structs.Organization{})
|
db.AutoMigrate(&models.Organization{})
|
||||||
db.AutoMigrate(&structs.Role{})
|
db.AutoMigrate(&models.Role{})
|
||||||
db.AutoMigrate(&structs.RolePermission{})
|
db.AutoMigrate(&models.RolePermission{})
|
||||||
db.AutoMigrate(&structs.User{})
|
db.AutoMigrate(&models.User{})
|
||||||
db.AutoMigrate(&structs.UserSession{})
|
db.AutoMigrate(&models.UserSession{})
|
||||||
db.AutoMigrate(&structs.Lesson{})
|
db.AutoMigrate(&models.Lesson{})
|
||||||
db.AutoMigrate(&structs.LessonContent{})
|
db.AutoMigrate(&models.LessonContent{})
|
||||||
db.AutoMigrate(&structs.Question{})
|
db.AutoMigrate(&models.Question{})
|
||||||
db.AutoMigrate(&structs.QuestionLike{})
|
db.AutoMigrate(&models.QuestionLike{})
|
||||||
db.AutoMigrate(&structs.QuestionReply{})
|
db.AutoMigrate(&models.QuestionReply{})
|
||||||
db.AutoMigrate(&structs.QuestionReplyLike{})
|
db.AutoMigrate(&models.QuestionReplyLike{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lms.de/backend/modules/cache"
|
||||||
|
"lms.de/backend/modules/database"
|
||||||
|
"lms.de/backend/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitPermissions() {
|
||||||
|
cache.SetMasterRolePermissions(utils.Permissions)
|
||||||
|
|
||||||
|
// delete permission from database if not longer in permissions
|
||||||
|
|
||||||
|
database.DB.Exec("DELETE FROM role_permissions WHERE permission NOT IN (?)", utils.Permissions)
|
||||||
|
}
|
|
@ -7,28 +7,6 @@ const (
|
||||||
LessonStateDraft = 2
|
LessonStateDraft = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type Lesson struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
OrganizationId string `gorm:"type:varchar(36)"`
|
|
||||||
State uint8 `gorm:"type:tinyint(1)"`
|
|
||||||
Title string `gorm:"type:varchar(255)"`
|
|
||||||
ThumbnailUrl string `gorm:"type:varchar(255)"`
|
|
||||||
CreatorUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type LessonContent struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
LessonId string `gorm:"type:varchar(36)"`
|
|
||||||
Page uint16 `gorm:"type:smallint(5)"` // Page number
|
|
||||||
Position uint16 `gorm:"type:smallint(5)"` // Position on the page
|
|
||||||
Type uint8 // Type of content, like text, image, video, etc
|
|
||||||
Data string `gorm:"type:text"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:model LessonResponse
|
// swagger:model LessonResponse
|
||||||
type LessonResponse struct {
|
type LessonResponse struct {
|
||||||
Id string
|
Id string
|
||||||
|
|
|
@ -1,20 +1,5 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Organization struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
Subdomain string `gorm:"type:varchar(255)"`
|
|
||||||
OwnerUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CompanyName string `gorm:"type:varchar(255)"`
|
|
||||||
PrimaryColor string `gorm:"type:varchar(6)"`
|
|
||||||
LogoUrl string `gorm:"type:varchar(255)"`
|
|
||||||
BannerUrl string `gorm:"type:varchar(255)"`
|
|
||||||
SignUpScreenUrl string `gorm:"type:varchar(255)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:model CreateOrganizationRequest
|
// swagger:model CreateOrganizationRequest
|
||||||
type CreateOrganizationRequest struct {
|
type CreateOrganizationRequest struct {
|
||||||
Email string
|
Email string
|
||||||
|
|
|
@ -1,38 +1 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Question struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
LessonId string `gorm:"type:varchar(36)"`
|
|
||||||
Question string `gorm:"type:text"`
|
|
||||||
Likes uint16 `gorm:"type:smallint(5)"`
|
|
||||||
CreatorUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type QuestionLike struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
QuestionId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatorUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type QuestionReply struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
QuestionId string `gorm:"type:varchar(36)"`
|
|
||||||
Reply string `gorm:"type:text"`
|
|
||||||
CreatorUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type QuestionReplyLike struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
QuestionReplyId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatorUserId string `gorm:"type:varchar(36)"`
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +1,38 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
type Role struct {
|
type RolesResponse struct {
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
Roles []HelperRole
|
||||||
OrganizationId string `gorm:"type:varchar(36)"`
|
|
||||||
Name string `gorm:"type:varchar(255)"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RolePermission struct {
|
type HelperRole struct {
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
Id string
|
||||||
RoleId string `gorm:"type:varchar(36)"`
|
Permissions []uint16
|
||||||
Permission string `gorm:"type:varchar(255)"`
|
Users []HelperRoleUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HelperRoleUser struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
ProfilePictureUrl string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
type HelperRole struct {
|
||||||
|
Id string
|
||||||
|
Name string
|
||||||
|
Master bool
|
||||||
|
Permissions []uint16
|
||||||
|
Users []HelperRoleUser
|
||||||
|
}
|
||||||
|
|
||||||
|
type HelperRoleUser struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
ProfilePictureUrl string
|
||||||
|
}
|
||||||
|
|
||||||
|
// swagger:model CreateRoleRequest
|
||||||
|
type CreateRoleRequest struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -19,6 +19,7 @@ const (
|
||||||
type SocketClient struct {
|
type SocketClient struct {
|
||||||
SessionId string
|
SessionId string
|
||||||
BrowserTabSession string
|
BrowserTabSession string
|
||||||
|
OrganizationId string
|
||||||
UserId string
|
UserId string
|
||||||
Conn *websocket.Conn
|
Conn *websocket.Conn
|
||||||
connMu sync.Mutex
|
connMu sync.Mutex
|
||||||
|
|
|
@ -1,35 +1,5 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
OrganizationId string `gorm:"type:varchar(36)"`
|
|
||||||
State uint8 `gorm:"type:tinyint(1)"`
|
|
||||||
Active bool `gorm:"type:tinyint(1)"`
|
|
||||||
RoleId string `gorm:"type:varchar(36)"`
|
|
||||||
FirstName string `gorm:"type:varchar(255)"`
|
|
||||||
LastName string `gorm:"type:varchar(255)"`
|
|
||||||
Email string `gorm:"type:varchar(255)"`
|
|
||||||
Password string `gorm:"type:varchar(255)"`
|
|
||||||
ProfilePictureUrl string `gorm:"type:varchar(255)"`
|
|
||||||
LastOnlineAt time.Time
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserSession struct {
|
|
||||||
Id string `gorm:"primaryKey;type:varchar(36)"`
|
|
||||||
UserId string `gorm:"type:varchar(36)"`
|
|
||||||
OrganizationId string `gorm:"type:varchar(36)"`
|
|
||||||
Session string `gorm:"type:varchar(36)"`
|
|
||||||
UserAgent string `gorm:"type:varchar(255)"`
|
|
||||||
ExpiresAt time.Time
|
|
||||||
LastUsedAt time.Time
|
|
||||||
CreatedAt time.Time
|
|
||||||
UpdatedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetUserResponse struct {
|
type GetUserResponse struct {
|
||||||
AvatarUrl string
|
AvatarUrl string
|
||||||
}
|
}
|
||||||
|
@ -54,3 +24,11 @@ type TeamMember struct {
|
||||||
RoleId string
|
RoleId string
|
||||||
ProfilePictureUrl string
|
ProfilePictureUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateTeamMemberRequest struct {
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
RoleId string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,15 @@ const (
|
||||||
maxPassword = "64"
|
maxPassword = "64"
|
||||||
MaxPassword = 64
|
MaxPassword = 64
|
||||||
|
|
||||||
LenHeaderXAuthorization = 36
|
LenHeaderXAuthorization = 36
|
||||||
lenHeaderXAuthorization = "36"
|
lenHeaderXAuthorization = "36"
|
||||||
LenUserId = 36
|
LenUserId = 36
|
||||||
LenHeaderXApiKey = 36
|
LenHeaderXApiKey = 36
|
||||||
|
LenHeaderBrowserTabSession = 36
|
||||||
|
|
||||||
HeaderXAuthorization = "X-Authorization"
|
HeaderXAuthorization = "X-Authorization"
|
||||||
HeaderXApiKey = "X-Api-Key"
|
HeaderXApiKey = "X-Api-Key"
|
||||||
|
HeaderBrowserTabSession = "Browser-Tab-Session"
|
||||||
|
|
||||||
SessionExpiresAtTime = 7 * 24 * 60 * 60 // 1 week
|
SessionExpiresAtTime = 7 * 24 * 60 * 60 // 1 week
|
||||||
|
|
||||||
|
@ -38,3 +40,59 @@ const (
|
||||||
LessonContentTypeText = 2
|
LessonContentTypeText = 2
|
||||||
LessonContentTypeImage
|
LessonContentTypeImage
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PermissionTeamInviteNewTeamMember = 1
|
||||||
|
PermissionTeamRemoveTeamMember = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var Permissions = []uint16{
|
||||||
|
PermissionTeamInviteNewTeamMember,
|
||||||
|
PermissionTeamRemoveTeamMember,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleAdminId = "d0f0fa0d-3f3b-438b-a76f-7febeb8aab57"
|
||||||
|
RoleModeratorId = "b7359e12-359e-423b-b39c-f0d4069adebc"
|
||||||
|
RoleUserId = "a1f084ad-d501-4015-b326-4c5c46fd1c5e"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Permissions for each role
|
||||||
|
|
||||||
|
var AdminPermissions = Permissions
|
||||||
|
|
||||||
|
var ModeratorPermissions = []uint16{
|
||||||
|
PermissionTeamInviteNewTeamMember,
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserPermissions = []uint16{}
|
||||||
|
|
||||||
|
var Roles = map[string][]uint16{
|
||||||
|
RoleAdminId: AdminPermissions,
|
||||||
|
RoleModeratorId: ModeratorPermissions,
|
||||||
|
RoleUserId: UserPermissions,
|
||||||
|
}
|
||||||
|
|
||||||
|
// websocket
|
||||||
|
|
||||||
|
// commands sent to websocket clients
|
||||||
|
const (
|
||||||
|
SendCmdSettingsUpdated = 1
|
||||||
|
SendCmdSettingsUpdatedLogo = 2
|
||||||
|
SendCmdSettingsUpdatedBanner = 3
|
||||||
|
SendCmdSettingsUpdatedSubdomain = 4
|
||||||
|
SendCmdTeamAddedMember = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// commands received from websocket clients
|
||||||
|
const (
|
||||||
|
ReceivedCmdSubscribeToTopic = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SubscribedTopicLessons = "/lessons"
|
||||||
|
SubscribedTopicTeam = "/team"
|
||||||
|
SubscribedTopicRoles = "/roles"
|
||||||
|
SubscribedTopicSettings = "/settings"
|
||||||
|
SubscribedTopicAccount = "/account"
|
||||||
|
)
|
||||||
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"lms.de/backend/modules/config"
|
"lms.de/backend/modules/config"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/structs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetXAuhorizationHeader(c *fiber.Ctx) string {
|
func GetXAuhorizationHeader(c *fiber.Ctx) string {
|
||||||
|
@ -32,6 +32,15 @@ func GetXApiKeyHeader(c *fiber.Ctx) string {
|
||||||
return c.GetReqHeaders()[HeaderXApiKey][0]
|
return c.GetReqHeaders()[HeaderXApiKey][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBrowserTabSessionHeader(c *fiber.Ctx) string {
|
||||||
|
// check if header is set
|
||||||
|
if len(c.GetReqHeaders()[HeaderBrowserTabSession]) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.GetReqHeaders()[HeaderBrowserTabSession][0]
|
||||||
|
}
|
||||||
|
|
||||||
func GetSessionExpiresAtTime() time.Time {
|
func GetSessionExpiresAtTime() time.Time {
|
||||||
return time.Now().Add(time.Second * SessionExpiresAtTime)
|
return time.Now().Add(time.Second * SessionExpiresAtTime)
|
||||||
}
|
}
|
||||||
|
@ -71,7 +80,7 @@ func GenerateSubdomain() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsSubdomainAlreadyInUse(subdomain string) bool {
|
func IsSubdomainAlreadyInUse(subdomain string) bool {
|
||||||
var organization structs.Organization
|
var organization models.Organization
|
||||||
|
|
||||||
if err := database.DB.Where("subdomain = ?", subdomain).First(&organization).Error; err != nil {
|
if err := database.DB.Where("subdomain = ?", subdomain).First(&organization).Error; err != nil {
|
||||||
return false
|
return false
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
|
@ -1,6 +1,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/structs"
|
"lms.de/backend/modules/structs"
|
||||||
|
@ -24,17 +25,15 @@ func GetApp(c *fiber.Ctx) error {
|
||||||
// '500':
|
// '500':
|
||||||
// description: Failed to fetch app
|
// description: Failed to fetch app
|
||||||
|
|
||||||
var user structs.User
|
var user models.User
|
||||||
|
|
||||||
database.DB.Model(&structs.User{
|
database.DB.Model(&models.User{
|
||||||
Id: c.Locals("userId").(string),
|
Id: c.Locals("userId").(string),
|
||||||
}).Select("profile_picture_url").First(&user)
|
}).Select("profile_picture_url").First(&user)
|
||||||
|
|
||||||
var organization structs.Organization
|
var organization models.Organization
|
||||||
|
|
||||||
database.DB.Model(&structs.Organization{
|
database.DB.Model(&models.Organization{}).Select("company_name", "primary_color", "logo_url", "banner_url").Where("id = ?", c.Locals("organizationId").(string)).First(&organization)
|
||||||
Id: c.Locals("organizationId").(string),
|
|
||||||
}).Select("company_name", "primary_color", "logo_url", "banner_url").First(&organization)
|
|
||||||
|
|
||||||
return c.JSON(structs.GetAppResponse{
|
return c.JSON(structs.GetAppResponse{
|
||||||
User: structs.AppUser{
|
User: structs.AppUser{
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
@ -32,7 +33,9 @@ func GetLessons(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var lessons []structs.LessonResponse
|
var lessons []structs.LessonResponse
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&lessons)
|
if err := database.DB.Model(&models.Lesson{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&lessons).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(lessons)
|
return c.JSON(lessons)
|
||||||
}
|
}
|
||||||
|
@ -53,7 +56,7 @@ func CreateLesson(c *fiber.Ctx) error {
|
||||||
// '500':
|
// '500':
|
||||||
// description: Failed to create lesson.
|
// description: Failed to create lesson.
|
||||||
|
|
||||||
lesson := structs.Lesson{
|
lesson := models.Lesson{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
OrganizationId: c.Locals("organizationId").(string),
|
OrganizationId: c.Locals("organizationId").(string),
|
||||||
State: structs.LessonStateDraft,
|
State: structs.LessonStateDraft,
|
||||||
|
@ -61,7 +64,9 @@ func CreateLesson(c *fiber.Ctx) error {
|
||||||
CreatorUserId: c.Locals("userId").(string),
|
CreatorUserId: c.Locals("userId").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Create(&lesson)
|
if err := database.DB.Create(&lesson).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(
|
return c.JSON(
|
||||||
structs.CreateLessonResponse{
|
structs.CreateLessonResponse{
|
||||||
|
@ -101,7 +106,9 @@ func GetLessonContents(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var lessonContents []structs.GetLessonContentsResponse
|
var lessonContents []structs.GetLessonContentsResponse
|
||||||
|
|
||||||
database.DB.Model(&structs.LessonContent{}).Where("lesson_id = ?", params.LessonId).Find(&lessonContents)
|
if err := database.DB.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Find(&lessonContents).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// sort contents by position
|
// sort contents by position
|
||||||
|
|
||||||
|
@ -141,7 +148,9 @@ func GetLessonSettings(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var lessonSettings structs.LessonSettings
|
var lessonSettings structs.LessonSettings
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("id = ?", params.LessonId).First(&lessonSettings)
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lessonSettings).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(lessonSettings)
|
return c.JSON(lessonSettings)
|
||||||
}
|
}
|
||||||
|
@ -183,7 +192,9 @@ func UpdateLessonPreviewTitle(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("id = ?", params.LessonId).Update("title", body.Title)
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("title", body.Title); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(
|
return c.JSON(
|
||||||
fiber.Map{
|
fiber.Map{
|
||||||
|
@ -237,9 +248,11 @@ func UpdateLessonPreviewThumbnail(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// get current thumbnail
|
// get current thumbnail
|
||||||
|
|
||||||
lesson := structs.Lesson{}
|
lesson := models.Lesson{}
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("id = ?", params.LessonId).First(&lesson)
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lesson); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// delete current thumbnail
|
// delete current thumbnail
|
||||||
|
|
||||||
|
@ -253,7 +266,9 @@ func UpdateLessonPreviewThumbnail(c *fiber.Ctx) error {
|
||||||
|
|
||||||
utils.CreateFolderStructureIfNotExists(publicPath)
|
utils.CreateFolderStructureIfNotExists(publicPath)
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("id = ?", params.LessonId).Update("thumbnail_url", databasePath+fileName)
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("thumbnail_url", databasePath+fileName); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.SaveFile(fileHeader, publicPath+fileName)
|
return c.SaveFile(fileHeader, publicPath+fileName)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +308,9 @@ func UpdateLessonState(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.Lesson{}).Where("id = ?", params.LessonId).Update("state", body.State)
|
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("state", body.State); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(
|
return c.JSON(
|
||||||
fiber.Map{
|
fiber.Map{
|
||||||
|
@ -341,13 +358,15 @@ func AddLessonContent(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// get last position
|
// get last position
|
||||||
|
|
||||||
var lastContent structs.LessonContent
|
var lastContent models.LessonContent
|
||||||
|
|
||||||
database.DB.Select("position").Model(&structs.LessonContent{}).Where("lesson_id = ?", params.LessonId).Order("position desc").First(&lastContent)
|
if err := database.DB.Select("position").Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Order("position desc").First(&lastContent); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// create new content
|
// create new content
|
||||||
|
|
||||||
content := structs.LessonContent{
|
content := models.LessonContent{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
LessonId: params.LessonId,
|
LessonId: params.LessonId,
|
||||||
Page: 1,
|
Page: 1,
|
||||||
|
@ -356,7 +375,9 @@ func AddLessonContent(c *fiber.Ctx) error {
|
||||||
Data: body.Data,
|
Data: body.Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Create(&content)
|
if err := database.DB.Create(&content); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(
|
return c.JSON(
|
||||||
structs.AddLessonContentResponse{
|
structs.AddLessonContentResponse{
|
||||||
|
@ -426,9 +447,11 @@ func UploadLessonContentFile(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// get current file
|
// get current file
|
||||||
|
|
||||||
content := structs.LessonContent{}
|
content := models.LessonContent{}
|
||||||
|
|
||||||
database.DB.Select("type", "data").Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).First(&content)
|
if err := database.DB.Select("type", "data").Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// delete current image
|
// delete current image
|
||||||
|
|
||||||
|
@ -442,7 +465,9 @@ func UploadLessonContentFile(c *fiber.Ctx) error {
|
||||||
|
|
||||||
utils.CreateFolderStructureIfNotExists(publicPath)
|
utils.CreateFolderStructureIfNotExists(publicPath)
|
||||||
|
|
||||||
database.DB.Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).Update("data", databasePath+fileName)
|
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", databasePath+fileName).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
|
if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
@ -494,7 +519,9 @@ func UpdateLessonContent(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).Update("data", body.Data)
|
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", body.Data).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(
|
return c.JSON(
|
||||||
fiber.Map{
|
fiber.Map{
|
||||||
|
@ -553,8 +580,8 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the current position of the content being moved
|
// Fetch the current position of the content being moved
|
||||||
var currentContent structs.LessonContent
|
var currentContent models.LessonContent
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).First(¤tContent).Error; err != nil {
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(¤tContent).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return c.SendStatus(fiber.StatusNotFound)
|
return c.SendStatus(fiber.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
@ -570,7 +597,7 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
|
||||||
|
|
||||||
if oldPosition < newPosition {
|
if oldPosition < newPosition {
|
||||||
// Items between oldPosition and newPosition need to be shifted down
|
// Items between oldPosition and newPosition need to be shifted down
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
||||||
Where("position > ? AND position <= ?", oldPosition, newPosition).
|
Where("position > ? AND position <= ?", oldPosition, newPosition).
|
||||||
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -578,7 +605,7 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Items between newPosition and oldPosition need to be shifted up
|
// Items between newPosition and oldPosition need to be shifted up
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
||||||
Where("position >= ? AND position < ?", newPosition, oldPosition).
|
Where("position >= ? AND position < ?", newPosition, oldPosition).
|
||||||
Update("position", gorm.Expr("position + 1")).Error; err != nil {
|
Update("position", gorm.Expr("position + 1")).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -587,7 +614,7 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the position of the moved content
|
// Update the position of the moved content
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).Update("position", newPosition).Error; err != nil {
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("position", newPosition).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
@ -640,8 +667,8 @@ func DeleteLessonContent(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the current position of the content being deleted
|
// Fetch the current position of the content being deleted
|
||||||
var content structs.LessonContent
|
var content models.LessonContent
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
|
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return c.SendStatus(fiber.StatusNotFound)
|
return c.SendStatus(fiber.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
@ -653,7 +680,7 @@ func DeleteLessonContent(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shift down the positions of all contents with a higher position
|
// Shift down the positions of all contents with a higher position
|
||||||
if err := tx.Model(&structs.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
|
||||||
Where("position > ?", content.Position).
|
Where("position > ?", content.Position).
|
||||||
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
Update("position", gorm.Expr("position - 1")).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
|
|
@ -4,12 +4,15 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
|
"lms.de/backend/modules/logger"
|
||||||
"lms.de/backend/modules/structs"
|
"lms.de/backend/modules/structs"
|
||||||
"lms.de/backend/modules/utils"
|
"lms.de/backend/modules/utils"
|
||||||
)
|
)
|
||||||
|
@ -64,23 +67,42 @@ func CreateOrganization(c *fiber.Ctx) error {
|
||||||
organizationId := uuid.New().String()
|
organizationId := uuid.New().String()
|
||||||
userId := uuid.New().String()
|
userId := uuid.New().String()
|
||||||
subdomain := utils.GenerateSubdomain()
|
subdomain := utils.GenerateSubdomain()
|
||||||
|
// roleId := uuid.New().String()
|
||||||
|
|
||||||
database.DB.Create(&structs.Organization{
|
if err := database.DB.Create(&models.Organization{
|
||||||
Id: organizationId,
|
Id: organizationId,
|
||||||
Subdomain: subdomain,
|
Subdomain: subdomain,
|
||||||
OwnerUserId: userId,
|
OwnerUserId: userId,
|
||||||
CompanyName: "Mustermann GmbH",
|
CompanyName: "Mustermann GmbH",
|
||||||
})
|
PrimaryColor: "1677ff", // blue
|
||||||
|
}).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to create organization, err: "+err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
database.DB.Create(&structs.User{
|
/*
|
||||||
|
if err := database.DB.Create(&models.Role{
|
||||||
|
Id: roleId,
|
||||||
|
OrganizationId: organizationId,
|
||||||
|
Master: true,
|
||||||
|
Name: "Admin",
|
||||||
|
}).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to create role, err: "+err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
} */
|
||||||
|
|
||||||
|
if err := database.DB.Create(&models.User{
|
||||||
Id: userId,
|
Id: userId,
|
||||||
OrganizationId: organizationId,
|
OrganizationId: organizationId,
|
||||||
Active: true,
|
|
||||||
FirstName: "Max",
|
FirstName: "Max",
|
||||||
LastName: "Mustermann",
|
LastName: "Mustermann",
|
||||||
Email: body.Email,
|
Email: body.Email,
|
||||||
Password: string(hashedPassword),
|
Password: string(hashedPassword),
|
||||||
})
|
RoleId: utils.RoleAdminId,
|
||||||
|
}).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to create user, err: "+err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
session, err := rsutils.GenerateSession()
|
session, err := rsutils.GenerateSession()
|
||||||
|
|
||||||
|
@ -88,7 +110,7 @@ func CreateOrganization(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Create(&structs.UserSession{
|
if err := database.DB.Create(&models.UserSession{
|
||||||
Id: session,
|
Id: session,
|
||||||
OrganizationId: organizationId,
|
OrganizationId: organizationId,
|
||||||
Session: uuid.New().String(),
|
Session: uuid.New().String(),
|
||||||
|
@ -96,7 +118,10 @@ func CreateOrganization(c *fiber.Ctx) error {
|
||||||
UserAgent: string(c.Context().UserAgent()),
|
UserAgent: string(c.Context().UserAgent()),
|
||||||
ExpiresAt: utils.GetSessionExpiresAtTime(),
|
ExpiresAt: utils.GetSessionExpiresAtTime(),
|
||||||
LastUsedAt: time.Now(),
|
LastUsedAt: time.Now(),
|
||||||
})
|
}).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to create user session, err: "+err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(structs.CreateOrganizationResponse{
|
return c.JSON(structs.CreateOrganizationResponse{
|
||||||
OrganizationSubdomain: subdomain,
|
OrganizationSubdomain: subdomain,
|
||||||
|
@ -131,7 +156,7 @@ func IsSubdomainAvailable(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
var organization structs.Organization
|
var organization models.Organization
|
||||||
|
|
||||||
database.DB.Select("Id").Where("subdomain = ?", params.Subdomain).First(&organization)
|
database.DB.Select("Id").Where("subdomain = ?", params.Subdomain).First(&organization)
|
||||||
|
|
||||||
|
@ -145,47 +170,3 @@ func IsSubdomainAvailable(c *fiber.Ctx) error {
|
||||||
Available: true,
|
Available: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateSubdomain(c *fiber.Ctx) error {
|
|
||||||
// swagger:operation PATCH /organization/subdomain/{subdomain} organization updateSubdomain
|
|
||||||
// ---
|
|
||||||
// summary: Update organization subdomain
|
|
||||||
// consumes:
|
|
||||||
// - application/json
|
|
||||||
// produces:
|
|
||||||
// - application/json
|
|
||||||
// parameters:
|
|
||||||
// - name: subdomain
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
// type: string
|
|
||||||
// responses:
|
|
||||||
// '200':
|
|
||||||
// description: Subdomain updated successfully
|
|
||||||
// '400':
|
|
||||||
// description: Invalid request body
|
|
||||||
// '500':
|
|
||||||
// description: Failed to update subdomain
|
|
||||||
|
|
||||||
var params structs.SubdomainParam
|
|
||||||
|
|
||||||
if err := rsutils.ParamsParserHelper(c, ¶ms); err != nil {
|
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
organization := structs.Organization{
|
|
||||||
Id: c.Locals("organizationId").(string),
|
|
||||||
}
|
|
||||||
|
|
||||||
database.DB.Select("subdomain").Model(organization).First(&organization)
|
|
||||||
|
|
||||||
if organization.Subdomain == "" {
|
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
database.DB.Model(&organization).Update("subdomain", params.Subdomain)
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"status": "success",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
package organization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"lms.de/backend/modules/database"
|
||||||
|
"lms.de/backend/modules/structs"
|
||||||
|
"lms.de/backend/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRoles(c *fiber.Ctx) error {
|
||||||
|
// swagger:operation GET /organization/roles organization getRoles
|
||||||
|
// ---
|
||||||
|
// summary: Get roles
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: Roles
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/RolesResponse"
|
||||||
|
// '500':
|
||||||
|
// description: Failed to get roles
|
||||||
|
|
||||||
|
/*
|
||||||
|
var roles []models.Role
|
||||||
|
|
||||||
|
if err := database.DB.Model(&models.Role{}).Where("organization_id = ?", c.Locals("organizationId").(string)).Find(&roles).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) == 0 {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
var rolePermissions []uint16
|
||||||
|
|
||||||
|
if err := database.DB.Model(&models.RolePermission{}).Where("role_id = ?", role.Id).Pluck("permission", &rolePermissions).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var users []structs.HelperRoleUser
|
||||||
|
|
||||||
|
if err := database.DB.Model(&models.User{}).Where("role_id = ?", role.Id).Where("organization_id = ?", c.Locals("organizationId").(string)).Find(&users).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesResponse.Roles = append(rolesResponse.Roles, structs.HelperRole{
|
||||||
|
Id: role.Id,
|
||||||
|
Name: role.Name,
|
||||||
|
Master: role.Master,
|
||||||
|
Permissions: rolePermissions,
|
||||||
|
Users: users,
|
||||||
|
})
|
||||||
|
} */
|
||||||
|
|
||||||
|
var rolesResponse structs.RolesResponse
|
||||||
|
|
||||||
|
// order is random so we need to define the order
|
||||||
|
keys := []string{utils.RoleAdminId, utils.RoleModeratorId, utils.RoleUserId}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
role := utils.Roles[key]
|
||||||
|
|
||||||
|
var users []structs.HelperRoleUser
|
||||||
|
|
||||||
|
if err := database.DB.Model(&models.User{}).Where("role_id = ?", key).Where("organization_id = ?", c.Locals("organizationId").(string)).Find(&users).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesResponse.Roles = append(rolesResponse.Roles, structs.HelperRole{
|
||||||
|
Id: key,
|
||||||
|
Permissions: role,
|
||||||
|
Users: users,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(rolesResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func CreateRole(c *fiber.Ctx) error {
|
||||||
|
// swagger:operation POST /organization/roles organization createRole
|
||||||
|
// ---
|
||||||
|
// summary: Create role
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: name
|
||||||
|
// in: body
|
||||||
|
// description: Role name
|
||||||
|
// required: true
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/CreateRoleRequest"
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: Role created
|
||||||
|
// '400':
|
||||||
|
// description: Invalid input
|
||||||
|
// '500':
|
||||||
|
// description: Failed to create role
|
||||||
|
|
||||||
|
var body structs.CreateRoleRequest
|
||||||
|
|
||||||
|
if err := c.BodyParser(&body); err != nil {
|
||||||
|
fmt.Print(err)
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
role := models.Role{
|
||||||
|
Id: uuid.New().String(),
|
||||||
|
OrganizationId: c.Locals("organizationId").(string),
|
||||||
|
Name: body.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Create(&role).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
*/
|
|
@ -1,13 +1,19 @@
|
||||||
package organization
|
package organization
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/structs"
|
"lms.de/backend/modules/structs"
|
||||||
"lms.de/backend/modules/utils"
|
"lms.de/backend/modules/utils"
|
||||||
|
"lms.de/backend/socketclients"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetOrganizationSettings(c *fiber.Ctx) error {
|
func GetOrganizationSettings(c *fiber.Ctx) error {
|
||||||
|
@ -30,7 +36,12 @@ func GetOrganizationSettings(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var organizationSettings structs.GetOrganizationSettingsResponse
|
var organizationSettings structs.GetOrganizationSettingsResponse
|
||||||
|
|
||||||
database.DB.Model(&structs.Organization{}).Select("subdomain", "company_name", "primary_color", "logo_url", "banner_url").First(&organizationSettings)
|
if err := database.DB.Model(&models.Organization{}).
|
||||||
|
Select("subdomain", "company_name", "primary_color", "logo_url", "banner_url").
|
||||||
|
Where("id = ?", c.Locals("organizationId").(string)).
|
||||||
|
First(&organizationSettings).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(organizationSettings)
|
return c.JSON(organizationSettings)
|
||||||
}
|
}
|
||||||
|
@ -62,9 +73,18 @@ func UpdateOrganizationSettings(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.Organization{
|
if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(organizationSettings).Error; err != nil {
|
||||||
Id: c.Locals("organizationId").(string),
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
}).Updates(organizationSettings)
|
}
|
||||||
|
|
||||||
|
socketclients.BroadcastMessageExceptBrowserTabSession(
|
||||||
|
c.Locals("organizationId").(string),
|
||||||
|
c.Locals("browserTabSession").(string),
|
||||||
|
structs.SendSocketMessage{
|
||||||
|
Cmd: utils.SendCmdSettingsUpdated,
|
||||||
|
Body: organizationSettings,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +140,7 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// get current file
|
// get current file
|
||||||
|
|
||||||
organization := structs.Organization{}
|
organization := models.Organization{}
|
||||||
|
|
||||||
selectField := "logo_url"
|
selectField := "logo_url"
|
||||||
|
|
||||||
|
@ -128,9 +148,11 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
|
||||||
selectField = "banner_url"
|
selectField = "banner_url"
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.Organization{
|
if err := database.DB.Model(&models.Organization{
|
||||||
Id: c.Locals("organizationId").(string),
|
Id: c.Locals("organizationId").(string),
|
||||||
}).Select(selectField).First(&organization)
|
}).Select(selectField).First(&organization).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
// delete current file
|
// delete current file
|
||||||
|
|
||||||
|
@ -148,7 +170,7 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
|
||||||
|
|
||||||
utils.CreateFolderStructureIfNotExists(publicPath)
|
utils.CreateFolderStructureIfNotExists(publicPath)
|
||||||
|
|
||||||
update := structs.Organization{}
|
update := models.Organization{}
|
||||||
|
|
||||||
if params.Type == "logo" {
|
if params.Type == "logo" {
|
||||||
update.LogoUrl = databasePath + fileName
|
update.LogoUrl = databasePath + fileName
|
||||||
|
@ -156,9 +178,9 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
|
||||||
update.BannerUrl = databasePath + fileName
|
update.BannerUrl = databasePath + fileName
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Model(&structs.Organization{
|
if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(update).Error; err != nil {
|
||||||
Id: c.Locals("organizationId").(string),
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
}).Updates(update)
|
}
|
||||||
|
|
||||||
if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
|
if err := c.SaveFile(fileHeader, publicPath+fileName); err != nil {
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||||
|
@ -166,7 +188,85 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmdId := utils.SendCmdSettingsUpdatedLogo
|
||||||
|
|
||||||
|
if params.Type == "banner" {
|
||||||
|
cmdId = utils.SendCmdSettingsUpdatedBanner
|
||||||
|
}
|
||||||
|
|
||||||
|
newPath := databasePath + fileName
|
||||||
|
|
||||||
|
socketclients.BroadcastMessageExceptBrowserTabSession(
|
||||||
|
c.Locals("organizationId").(string),
|
||||||
|
c.Locals("browserTabSession").(string),
|
||||||
|
structs.SendSocketMessage{
|
||||||
|
Cmd: cmdId,
|
||||||
|
Body: newPath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"Data": databasePath + fileName,
|
"Data": newPath,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSubdomain(c *fiber.Ctx) error {
|
||||||
|
// swagger:operation PATCH /organization/subdomain/{subdomain} organization updateSubdomain
|
||||||
|
// ---
|
||||||
|
// summary: Update organization subdomain
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: subdomain
|
||||||
|
// in: path
|
||||||
|
// required: true
|
||||||
|
// type: string
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: Subdomain updated successfully
|
||||||
|
// '400':
|
||||||
|
// description: Invalid request body
|
||||||
|
// '500':
|
||||||
|
// description: Failed to update subdomain
|
||||||
|
|
||||||
|
var params structs.SubdomainParam
|
||||||
|
|
||||||
|
if err := rsutils.ParamsParserHelper(c, ¶ms); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
var organization models.Organization
|
||||||
|
|
||||||
|
if err := database.DB.Select("subdomain").Model(organization).Where("id = ?", c.Locals("organizationId").(string)).First(&organization).Error; err != nil {
|
||||||
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return c.SendStatus(fiber.StatusNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if organization.Subdomain == "" {
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Model(&organization).Where("id = ?", c.Locals("organizationId")).Update("subdomain", params.Subdomain).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
orgId := c.Locals("organizationId").(string)
|
||||||
|
|
||||||
|
// send broadcast with delay
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
socketclients.BroadcastMessage(orgId,
|
||||||
|
structs.SendSocketMessage{
|
||||||
|
Cmd: utils.SendCmdSettingsUpdatedSubdomain,
|
||||||
|
Body: params.Subdomain,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"status": "success",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package organization
|
package organization
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/structs"
|
"lms.de/backend/modules/structs"
|
||||||
|
"lms.de/backend/modules/utils"
|
||||||
|
"lms.de/backend/socketclients"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetTeamMembers(c *fiber.Ctx) error {
|
func GetTeamMembers(c *fiber.Ctx) error {
|
||||||
|
@ -28,7 +37,99 @@ func GetTeamMembers(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var users []structs.TeamMember
|
var users []structs.TeamMember
|
||||||
|
|
||||||
database.DB.Model(&structs.User{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&users)
|
if err := database.DB.Model(&models.User{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&users).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(users)
|
return c.JSON(users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateTeamMember(c *fiber.Ctx) error {
|
||||||
|
// swagger:operation POST /organization/team/members organization createTeamMember
|
||||||
|
// ---
|
||||||
|
// summary: Create team member
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/CreateTeamMemberRequest"
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: Team member created successfully
|
||||||
|
// '400':
|
||||||
|
// description: Invalid request body
|
||||||
|
// '500':
|
||||||
|
// description: Failed to create team member
|
||||||
|
|
||||||
|
var body structs.CreateTeamMemberRequest
|
||||||
|
|
||||||
|
if err := rsutils.BodyParserHelper(c, &body); err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := models.User{
|
||||||
|
Id: uuid.New().String(),
|
||||||
|
FirstName: body.FirstName,
|
||||||
|
LastName: body.LastName,
|
||||||
|
Email: body.Email,
|
||||||
|
OrganizationId: c.Locals("organizationId").(string),
|
||||||
|
RoleId: body.RoleId,
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedPassword, err := base64.StdEncoding.DecodeString(body.Password)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Failed to decode base64 password, err: " + err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if passwordValid := utils.IsPasswordLengthValid(string(decodedPassword)); !passwordValid {
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash password
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msg("Failed to hash password, err: " + err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Password = string(hashedPassword)
|
||||||
|
|
||||||
|
// Create user
|
||||||
|
|
||||||
|
if err := database.DB.Create(&user).Error; err != nil {
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
socketclients.BroadcastMessageToTopicExceptBrowserTabSession(
|
||||||
|
c.Locals("organizationId").(string),
|
||||||
|
utils.SubscribedTopicTeam,
|
||||||
|
c.Locals("browserTabSession").(string),
|
||||||
|
structs.SendSocketMessage{
|
||||||
|
Cmd: utils.SendCmdTeamAddedMember,
|
||||||
|
Body: struct {
|
||||||
|
Id string
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
Email string
|
||||||
|
RoleId string
|
||||||
|
}{
|
||||||
|
Id: user.Id,
|
||||||
|
FirstName: user.FirstName,
|
||||||
|
LastName: user.LastName,
|
||||||
|
Email: user.Email,
|
||||||
|
RoleId: user.RoleId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"message": "Team member created successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
"git.ex.umbach.dev/Alex/roese-utils/rsutils"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
@ -57,11 +58,14 @@ func UserLogin(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
var user structs.User
|
var user models.User
|
||||||
|
|
||||||
organizationId := c.Locals("organizationId").(string)
|
organizationId := c.Locals("organizationId").(string)
|
||||||
|
|
||||||
database.DB.Select("id", "active", "password").First(&user, "email = ? AND organization_id = ?", body.Email, organizationId)
|
if err := database.DB.Select("id", "disabled", "password").First(&user, "email = ? AND organization_id = ?", body.Email, organizationId).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to find user with email %s", body.Email)
|
||||||
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
if user.Id == "" {
|
if user.Id == "" {
|
||||||
log.Error().Msg("User not found")
|
log.Error().Msg("User not found")
|
||||||
|
@ -73,7 +77,7 @@ func UserLogin(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusBadRequest)
|
return c.SendStatus(fiber.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.Active {
|
if user.Disabled {
|
||||||
return c.SendStatus(fiber.StatusUnauthorized)
|
return c.SendStatus(fiber.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,13 +87,16 @@ func UserLogin(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusInternalServerError)
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
database.DB.Create(&structs.UserSession{
|
if err := database.DB.Create(&models.UserSession{
|
||||||
Id: uuid.New().String(),
|
Id: uuid.New().String(),
|
||||||
OrganizationId: organizationId,
|
OrganizationId: organizationId,
|
||||||
Session: session,
|
Session: session,
|
||||||
UserId: user.Id,
|
UserId: user.Id,
|
||||||
UserAgent: string(c.Context().UserAgent()),
|
UserAgent: string(c.Context().UserAgent()),
|
||||||
ExpiresAt: utils.GetSessionExpiresAtTime()})
|
ExpiresAt: utils.GetSessionExpiresAtTime()}).Error; err != nil {
|
||||||
|
logger.AddSystemLog(rslogger.LogTypeError, "Failed to create user session, err: "+err.Error())
|
||||||
|
return c.SendStatus(fiber.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
logger.AddSystemLog(rslogger.LogTypeInfo, "User %s has logged in", user.Id)
|
logger.AddSystemLog(rslogger.LogTypeInfo, "User %s has logged in", user.Id)
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ package router
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"lms.de/backend/modules/config"
|
"lms.de/backend/modules/config"
|
||||||
"lms.de/backend/modules/database"
|
"lms.de/backend/modules/database"
|
||||||
"lms.de/backend/modules/structs"
|
|
||||||
"lms.de/backend/modules/utils"
|
"lms.de/backend/modules/utils"
|
||||||
myapp "lms.de/backend/routers/router/api/v1/app"
|
myapp "lms.de/backend/routers/router/api/v1/app"
|
||||||
"lms.de/backend/routers/router/api/v1/lessons"
|
"lms.de/backend/routers/router/api/v1/lessons"
|
||||||
|
@ -22,11 +22,14 @@ func SetupRoutes(app *fiber.App) {
|
||||||
o := v1.Group("/organization")
|
o := v1.Group("/organization")
|
||||||
o.Post("/", organization.CreateOrganization)
|
o.Post("/", organization.CreateOrganization)
|
||||||
o.Get("/team/members", handleOrganizationSubdomain, requestAccessValidation, organization.GetTeamMembers)
|
o.Get("/team/members", handleOrganizationSubdomain, requestAccessValidation, organization.GetTeamMembers)
|
||||||
|
o.Post("/team/members", handleOrganizationSubdomain, requestAccessValidation, organization.CreateTeamMember)
|
||||||
o.Get("/settings", handleOrganizationSubdomain, requestAccessValidation, organization.GetOrganizationSettings)
|
o.Get("/settings", handleOrganizationSubdomain, requestAccessValidation, organization.GetOrganizationSettings)
|
||||||
o.Patch("/settings", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateOrganizationSettings)
|
o.Patch("/settings", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateOrganizationSettings)
|
||||||
o.Post("/file/:type", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateOrganizationFile)
|
o.Post("/file/:type", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateOrganizationFile)
|
||||||
o.Get("/subdomain/:subdomain", organization.IsSubdomainAvailable)
|
o.Get("/subdomain/:subdomain", organization.IsSubdomainAvailable)
|
||||||
o.Patch("/subdomain/:subdomain", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateSubdomain)
|
o.Patch("/subdomain/:subdomain", handleOrganizationSubdomain, requestAccessValidation, organization.UpdateSubdomain)
|
||||||
|
o.Get("/roles", handleOrganizationSubdomain, requestAccessValidation, organization.GetRoles)
|
||||||
|
// o.Post("/roles", handleOrganizationSubdomain, requestAccessValidation, organization.CreateRole)
|
||||||
|
|
||||||
u := v1.Group("/user")
|
u := v1.Group("/user")
|
||||||
u.Post("/auth/login", handleOrganizationSubdomain, user.UserLogin)
|
u.Post("/auth/login", handleOrganizationSubdomain, user.UserLogin)
|
||||||
|
@ -55,7 +58,7 @@ func userSessionValidation(c *fiber.Ctx) error {
|
||||||
return fiber.ErrUnauthorized
|
return fiber.ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
var userSession structs.UserSession
|
var userSession models.UserSession
|
||||||
|
|
||||||
database.DB.Select("session", "user_id").First(&userSession, "session = ? AND organization_id = ?", xAuthorization, c.Locals("organizationId"))
|
database.DB.Select("session", "user_id").First(&userSession, "session = ? AND organization_id = ?", xAuthorization, c.Locals("organizationId"))
|
||||||
|
|
||||||
|
@ -70,6 +73,14 @@ func userSessionValidation(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestAccessValidation(c *fiber.Ctx) error {
|
func requestAccessValidation(c *fiber.Ctx) error {
|
||||||
|
// browser tab session - needed for websocket
|
||||||
|
|
||||||
|
browserTabSession := utils.GetBrowserTabSessionHeader(c)
|
||||||
|
|
||||||
|
if len(browserTabSession) == utils.LenHeaderBrowserTabSession {
|
||||||
|
c.Locals("browserTabSession", browserTabSession)
|
||||||
|
}
|
||||||
|
|
||||||
// user session
|
// user session
|
||||||
xAuthorization := utils.GetXAuhorizationHeader(c)
|
xAuthorization := utils.GetXAuhorizationHeader(c)
|
||||||
|
|
||||||
|
@ -100,7 +111,7 @@ func handleOrganizationSubdomain(c *fiber.Ctx) error {
|
||||||
subdomain := parts[0]
|
subdomain := parts[0]
|
||||||
|
|
||||||
// get organization id by subdomain from database
|
// get organization id by subdomain from database
|
||||||
organization := structs.Organization{}
|
organization := models.Organization{}
|
||||||
|
|
||||||
database.DB.Select("id").First(&organization, "subdomain = ?", subdomain)
|
database.DB.Select("id").First(&organization, "subdomain = ?", subdomain)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package socketclients
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"lms.de/backend/modules/cache"
|
||||||
|
"lms.de/backend/modules/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BroadcastMessage(organizationId string, sendSocketMessage structs.SendSocketMessage) {
|
||||||
|
for _, client := range cache.GetSocketClients() {
|
||||||
|
if client.OrganizationId == organizationId {
|
||||||
|
client.SendMessage(sendSocketMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BroadcastMessageToTopic(organizationId string, topic string, sendSocketMessage structs.SendSocketMessage) {
|
||||||
|
for _, client := range cache.GetSocketClients() {
|
||||||
|
if hasClientSubscribedToTopic(topic, client.SubscribedTopic) {
|
||||||
|
client.SendMessage(sendSocketMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BroadcastMessageExceptBrowserTabSession(organizationId string, browserTabSession string, sendSocketMessage structs.SendSocketMessage) {
|
||||||
|
for _, client := range cache.GetSocketClients() {
|
||||||
|
if client.OrganizationId == organizationId && client.BrowserTabSession != browserTabSession {
|
||||||
|
client.SendMessage(sendSocketMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BroadcastMessageToTopicExceptBrowserTabSession(organizationId string, topic string, browserTabSession string, sendSocketMessage structs.SendSocketMessage) {
|
||||||
|
for _, client := range cache.GetSocketClients() {
|
||||||
|
if hasClientSubscribedToTopic(topic, client.SubscribedTopic) && client.BrowserTabSession != browserTabSession && client.OrganizationId == organizationId {
|
||||||
|
client.SendMessage(sendSocketMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasClientSubscribedToTopic(topic string, clientTopic string) bool {
|
||||||
|
return clientTopic == topic || strings.HasPrefix(clientTopic, topic)
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
"git.ex.umbach.dev/Alex/roese-utils/rslogger"
|
||||||
|
"git.ex.umbach.dev/LMS/libcore/models"
|
||||||
"github.com/gofiber/websocket/v2"
|
"github.com/gofiber/websocket/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"lms.de/backend/modules/cache"
|
"lms.de/backend/modules/cache"
|
||||||
|
@ -37,22 +38,23 @@ func RunHub() {
|
||||||
newSocketClient.SessionId = sessionId
|
newSocketClient.SessionId = sessionId
|
||||||
newSocketClient.BrowserTabSession = browserTabSession
|
newSocketClient.BrowserTabSession = browserTabSession
|
||||||
newSocketClient.UserId = userId
|
newSocketClient.UserId = userId
|
||||||
|
newSocketClient.OrganizationId = fmt.Sprintf("%v", newSocketClient.Conn.Locals("organizationId"))
|
||||||
|
|
||||||
cache.AddSocketClient(newSocketClient)
|
cache.AddSocketClient(newSocketClient)
|
||||||
|
|
||||||
// check that user session is not expired
|
// check that user session is not expired
|
||||||
var userSession structs.UserSession
|
var userSession models.UserSession
|
||||||
|
|
||||||
database.DB.Select("expires_at").First(&userSession, "session = ?", sessionId)
|
database.DB.Select("expires_at").First(&userSession, "session = ?", sessionId)
|
||||||
|
|
||||||
if !userSession.ExpiresAt.IsZero() && time.Now().After(userSession.ExpiresAt) {
|
if !userSession.ExpiresAt.IsZero() && time.Now().After(userSession.ExpiresAt) {
|
||||||
newSocketClient.SendUnauthorizedCloseMessage()
|
newSocketClient.SendUnauthorizedCloseMessage()
|
||||||
database.DB.Delete(&structs.UserSession{}, "session = ?", sessionId)
|
database.DB.Delete(&models.UserSession{}, "session = ?", sessionId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// update session last used time
|
// update session last used time
|
||||||
database.DB.Model(&structs.UserSession{}).Where("session = ?", sessionId).Updates(structs.UserSession{
|
database.DB.Model(&models.UserSession{}).Where("session = ?", sessionId).Updates(models.UserSession{
|
||||||
LastUsedAt: time.Now(),
|
LastUsedAt: time.Now(),
|
||||||
ExpiresAt: utils.GetSessionExpiresAtTime(),
|
ExpiresAt: utils.GetSessionExpiresAtTime(),
|
||||||
})
|
})
|
||||||
|
@ -72,7 +74,9 @@ func RunHub() {
|
||||||
log.Debug().Msgf("Received message: %v %v", receivedMessage, receivedMessage.Cmd)
|
log.Debug().Msgf("Received message: %v %v", receivedMessage, receivedMessage.Cmd)
|
||||||
|
|
||||||
switch receivedMessage.Cmd {
|
switch receivedMessage.Cmd {
|
||||||
case 1:
|
case utils.ReceivedCmdSubscribeToTopic:
|
||||||
|
cache.SubscribeSocketClientToTopic(receivedMessage.Body["browserTabSession"].(string), receivedMessage.Body["topic"].(string))
|
||||||
|
case 2:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
log.Error().Msgf("Received unknown message: %v", receivedMessage)
|
log.Error().Msgf("Received unknown message: %v", receivedMessage)
|
||||||
|
@ -85,7 +89,7 @@ func RunHub() {
|
||||||
userId := connection.Locals("userId").(string)
|
userId := connection.Locals("userId").(string)
|
||||||
// sessionId := connection.Locals("sessionId").(string)
|
// sessionId := connection.Locals("sessionId").(string)
|
||||||
|
|
||||||
database.DB.Model(&structs.User{}).Where("id = ?", userId).Updates(structs.User{
|
database.DB.Model(&models.User{}).Where("id = ?", userId).Updates(models.User{
|
||||||
LastOnlineAt: time.Now(),
|
LastOnlineAt: time.Now(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue