added questions, replies and likes functionality"

main
alex 2024-09-12 20:06:27 +02:00
parent 437a4f6e63
commit 6a9cde458a
16 changed files with 727 additions and 100 deletions

36
go.mod
View File

@ -4,38 +4,40 @@ go 1.21.0
require (
git.ex.umbach.dev/Alex/roese-utils v1.0.21
git.ex.umbach.dev/LMS/libcore v1.0.6
git.ex.umbach.dev/LMS/libcore v1.0.8
github.com/gofiber/fiber/v2 v2.52.5
github.com/gofiber/websocket/v2 v2.2.1
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/rs/zerolog v1.31.0
golang.org/x/crypto v0.14.0
github.com/rs/zerolog v1.33.0
golang.org/x/crypto v0.27.0
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.11
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/fasthttp/websocket v1.5.10 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
)

47
go.sum
View File

@ -4,20 +4,17 @@ git.ex.umbach.dev/Alex/roese-utils v1.0.21 h1:ae1AHQh8UHJVLbpk5Gf4S5IP0EEWGD1JFI
git.ex.umbach.dev/Alex/roese-utils v1.0.21/go.mod h1:hFcnKQl6nuGFEMCxK/eVQBUD6ixBFlAaiy4E2aQqUL8=
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/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
git.ex.umbach.dev/LMS/libcore v1.0.7 h1:bKt02PY0FffHU79EnW5k6HvySLt4KgyKmb7duulweTM=
git.ex.umbach.dev/LMS/libcore v1.0.7/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
git.ex.umbach.dev/LMS/libcore v1.0.8 h1:6OLKoWEiSy+rm/UdKFASGWzR7hQocBKPA5KR8zsdzLY=
git.ex.umbach.dev/LMS/libcore v1.0.8/go.mod h1:BvbMJWYQ83dblDAB4ooLfwuorjdy6G3txdOrjRfIKPA=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
github.com/fasthttp/websocket v1.5.10 h1:bc7NIGyrg1L6sd5pRzCIbXpro54SZLEluZCu0rOpcN4=
github.com/fasthttp/websocket v1.5.10/go.mod h1:BwHeuXGWzCW1/BIKUKD3+qfCl+cTdsHu/f243NcAI/Q=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -26,11 +23,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
@ -47,12 +41,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -61,63 +51,38 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=

View File

@ -44,6 +44,4 @@ func InitDatabase() {
db.AutoMigrate(&models.LessonContent{})
db.AutoMigrate(&models.Question{})
db.AutoMigrate(&models.QuestionLike{})
db.AutoMigrate(&models.QuestionReply{})
db.AutoMigrate(&models.QuestionReplyLike{})
}

View File

@ -1 +1,18 @@
package structs
import "git.ex.umbach.dev/LMS/libcore/models"
type QuestionsResponse struct {
Questions []models.Question
LikedQuestions []string
}
// swagger:model CreateQuestionRequest
type CreateQuestionRequest struct {
Message string
}
type LessonIdQuestionIdParam struct {
LessonId string
QuestionId string
}

View File

@ -1,7 +1,11 @@
package structs
// swagger:model GetUserResponse
type GetUserResponse struct {
AvatarUrl string
Id string
FirstName string
LastName string
ProfilePictureUrl string
}
// swagger:model UserLoginRequest
@ -53,3 +57,7 @@ type GetUserProfileResponse struct {
Email string
RoleId string
}
type UserIdParam struct {
UserId string
}

View File

@ -97,6 +97,11 @@ const (
SendCmdLessonContentUpdatedPosition = 15
SendCmdLessonContentFileUpdated = 16
SendCmdUserProfilePictureUpdated = 17
SendCmdLessonQuestionCreated = 18
SendCmdLessonQuestionLiked = 19
SendCmdLessonQuestionCountUpLikes = 20
SendCmdLessonQuestionDisliked = 21
SendCmdLessonQuestionCountDownLikes = 22
)
// commands received from websocket clients

View File

@ -27,13 +27,20 @@ func GetApp(c *fiber.Ctx) error {
var user models.User
database.DB.Model(&models.User{
Id: c.Locals("userId").(string),
}).Select("profile_picture_url").First(&user)
if err := database.DB.Model(&models.User{}).
Where("id = ?", c.Locals("userId").(string)).
Select("profile_picture_url").First(&user).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
var organization models.Organization
database.DB.Model(&models.Organization{}).Select("company_name", "primary_color", "logo_url", "banner_url").Where("id = ?", c.Locals("organizationId").(string)).First(&organization)
if err := database.DB.Model(&models.Organization{}).
Select("company_name", "primary_color", "logo_url", "banner_url").
Where("id = ?", c.Locals("organizationId").(string)).
First(&organization).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(structs.GetAppResponse{
User: structs.AppUser{

View File

@ -35,7 +35,9 @@ func GetLessons(c *fiber.Ctx) error {
var lessons []structs.LessonResponse
if err := database.DB.Model(&models.Lesson{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&lessons).Error; err != nil {
if err := database.DB.Model(&models.Lesson{}).
Where("organization_id = ?", c.Locals("organizationId")).
Find(&lessons).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -116,9 +118,23 @@ func GetLessonContents(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// get lesson contents
var lessonContents []structs.GetLessonContentsResponse
if err := database.DB.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Find(&lessonContents).Error; err != nil {
if err := database.DB.Model(&models.LessonContent{}).
Where("lesson_id = ?", params.LessonId).
Find(&lessonContents).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -160,7 +176,9 @@ func GetLessonSettings(c *fiber.Ctx) error {
var lessonSettings structs.LessonSettings
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lessonSettings).Error; err != nil {
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lessonSettings).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -204,7 +222,9 @@ func UpdateLessonPreviewTitle(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("title", body.Title).Error; err != nil {
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
Update("title", body.Title).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -278,7 +298,9 @@ func UpdateLessonPreviewThumbnail(c *fiber.Ctx) error {
lesson := models.Lesson{}
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).First(&lesson).Error; err != nil {
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -296,7 +318,9 @@ func UpdateLessonPreviewThumbnail(c *fiber.Ctx) error {
thumbnailUrl := databasePath + fileName
if err := database.DB.Model(&models.Lesson{}).Where("id = ?", params.LessonId).Update("thumbnail_url", thumbnailUrl).Error; err != nil {
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
Update("thumbnail_url", thumbnailUrl).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -362,8 +386,7 @@ func UpdateLessonState(c *fiber.Ctx) error {
}
if err := database.DB.Model(&models.Lesson{}).
Where("id = ?", params.LessonId).
Where("organization_id = ?", c.Locals("organizationId").(string)).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
Update("state", body.State).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -428,11 +451,25 @@ func AddLessonContent(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// get last position
var lastContent models.LessonContent
if err := database.DB.Select("position").Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).Order("position desc").First(&lastContent).Error; err != nil {
if err := database.DB.Select("position").
Model(&models.LessonContent{}).
Where("lesson_id = ?", params.LessonId).
Order("position desc").
First(&lastContent).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusNotFound)
}
@ -532,11 +569,24 @@ func UploadLessonContentFile(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// get current file
content := models.LessonContent{}
if err := database.DB.Select("type", "data").Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
if err := database.DB.Select("type", "data").
Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
First(&content).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -552,7 +602,9 @@ func UploadLessonContentFile(c *fiber.Ctx) error {
utils.CreateFolderStructureIfNotExists(publicPath)
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", databasePath+fileName).Error; err != nil {
if err := database.DB.Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
Update("data", databasePath+fileName).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -624,7 +676,21 @@ func UpdateLessonContent(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
if err := database.DB.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("data", body.Data).Error; err != nil {
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// update content
if err := database.DB.Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
Update("data", body.Data).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -692,6 +758,16 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// update position
newPosition := body.Position
@ -704,7 +780,9 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
// Fetch the current position of the content being moved
var currentContent models.LessonContent
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&currentContent).Error; err != nil {
if err := tx.Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
First(&currentContent).Error; err != nil {
tx.Rollback()
return c.SendStatus(fiber.StatusNotFound)
}
@ -720,7 +798,8 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
if oldPosition < newPosition {
// Items between oldPosition and newPosition need to be shifted down
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
if err := tx.Model(&models.LessonContent{}).
Where("lesson_id = ?", params.LessonId).
Where("position > ? AND position <= ?", oldPosition, newPosition).
Update("position", gorm.Expr("position - 1")).Error; err != nil {
tx.Rollback()
@ -728,7 +807,8 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
}
} else {
// Items between newPosition and oldPosition need to be shifted up
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
if err := tx.Model(&models.LessonContent{}).
Where("lesson_id = ?", params.LessonId).
Where("position >= ? AND position < ?", newPosition, oldPosition).
Update("position", gorm.Expr("position + 1")).Error; err != nil {
tx.Rollback()
@ -737,7 +817,9 @@ func UpdateLessonContentPosition(c *fiber.Ctx) error {
}
// Update the position of the moved content
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).Update("position", newPosition).Error; err != nil {
if err := tx.Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
Update("position", newPosition).Error; err != nil {
tx.Rollback()
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -807,9 +889,21 @@ func DeleteLessonContent(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusInternalServerError)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// Fetch the current position of the content being deleted
var content models.LessonContent
if err := tx.Model(&models.LessonContent{}).Where("id = ?", params.ContentId).First(&content).Error; err != nil {
if err := tx.Model(&models.LessonContent{}).
Where("id = ? AND lesson_id = ?", params.ContentId, params.LessonId).
First(&content).Error; err != nil {
tx.Rollback()
return c.SendStatus(fiber.StatusNotFound)
}
@ -821,8 +915,8 @@ func DeleteLessonContent(c *fiber.Ctx) error {
}
// Shift down the positions of all contents with a higher position
if err := tx.Model(&models.LessonContent{}).Where("lesson_id = ?", params.LessonId).
Where("position > ?", content.Position).
if err := tx.Model(&models.LessonContent{}).
Where("lesson_id = ? AND position > ?", params.LessonId, content.Position).
Update("position", gorm.Expr("position - 1")).Error; err != nil {
tx.Rollback()
return c.SendStatus(fiber.StatusInternalServerError)

View File

@ -0,0 +1,452 @@
package lessons
import (
"errors"
"fmt"
"time"
"git.ex.umbach.dev/LMS/libcore/models"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
"lms.de/backend/modules/database"
"lms.de/backend/modules/structs"
"lms.de/backend/modules/utils"
"lms.de/backend/socketclients"
)
func GetQuestions(c *fiber.Ctx) error {
// swagger:operation GET /v1/lessons/{lessonId}/questions questions getQuestions
// ---
// summary: Get questions
// produces:
// - application/json
// responses:
// '200':
// description: Questions
// schema:
// "$ref": "#/definitions/QuestionsResponse"
// '500':
// description: Failed to get questions
var params structs.LessonIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.JSON([]models.Question{})
}
// get questions
var questions []models.Question
if err := database.DB.Model(&models.Question{}).
Where("lesson_id = ?", params.LessonId).
Find(&questions).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusNotFound)
}
}
// get all questions liked by user
var likedQuestions []string
if err := database.DB.Model(&models.QuestionLike{}).
Where("creator_user_id = ?", c.Locals("userId").(string)).
Pluck("question_id", &likedQuestions).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
/*
var questionLikes []models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("creator_user_id = ?", c.Locals("userId").(string)).
Find(&questionLikes).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
var likedQuestions []string
for _, questionLike := range questionLikes {
likedQuestions = append(likedQuestions, questionLike.QuestionId)
} */
return c.JSON(structs.QuestionsResponse{
Questions: questions,
LikedQuestions: likedQuestions,
})
}
func CreateQuestion(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions question createQuestion
// ---
// summary: Create question
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateQuestionRequest"
// responses:
// '200':
// description: Question created successfully
// schema:
// type: string
// '400':
// description: Invalid request body
// '500':
// description: Failed to create question
var params structs.LessonIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
var body structs.CreateQuestionRequest
if err := c.BodyParser(&body); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// create question
question := models.Question{
Id: uuid.New().String(),
LessonId: params.LessonId,
Message: body.Message,
Likes: 0,
CreatorUserId: c.Locals("userId").(string),
CreatedAt: time.Now(),
}
if err := database.DB.Create(&question).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
socketclients.BroadcastMessageToTopic(
c.Locals("organizationId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCreated,
Body: question,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func CreateQuestionReply(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/reply question createQuestionReply
// ---
// summary: Create question reply
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateQuestionRequest"
// responses:
// '200':
// description: Question reply created successfully
// schema:
// type: string
// '400':
// description: Invalid request body
// '500':
// description: Failed to create question reply
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
var body structs.CreateQuestionRequest
if err := c.BodyParser(&body); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// create question reply
question = models.Question{
Id: uuid.New().String(),
LessonId: params.LessonId,
QuestionId: params.QuestionId,
ReplyId: params.QuestionId,
Message: body.Message,
Likes: 0,
CreatorUserId: c.Locals("userId").(string),
}
if err := database.DB.Create(&question).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
socketclients.BroadcastMessageToTopic(
c.Locals("organizationId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCreated,
Body: question,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func LikeQuestion(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/likes question likeQuestion
// ---
// summary: Like question
// produces:
// - application/json
// responses:
// '200':
// description: Question liked successfully
// schema:
// type: string
// '500':
// description: Failed to like question
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
fmt.Println("lesson not found")
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
fmt.Println("question not found")
return c.SendStatus(fiber.StatusNotFound)
}
// check if user already liked question
var questionLike models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
First(&questionLike).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
if questionLike.Id != "" {
fmt.Println("question already liked")
return c.SendStatus(fiber.StatusConflict)
}
// like question
questionLike = models.QuestionLike{
Id: uuid.New().String(),
QuestionId: params.QuestionId,
CreatorUserId: c.Locals("userId").(string),
CreatedAt: time.Now(),
}
if err := database.DB.Create(&questionLike).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// update question likes
if err := database.DB.Model(&models.Question{}).
Where("id = ?", params.QuestionId).
Update("likes", gorm.Expr("likes + 1")).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// send message to user clients who liked the question
socketclients.SendMessageToUserWithTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionLiked,
Body: question.Id,
},
)
// inform all users about the like
socketclients.BroadcastMessageToTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCountUpLikes,
Body: question.Id,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func DislikeQuestion(c *fiber.Ctx) error {
// swagger:operation DELETE /v1/lessons/{lessonId}/questions/{questionId}/likes question dislikeQuestion
// ---
// summary: Dislike question
// produces:
// - application/json
// responses:
// '200':
// description: Question disliked successfully
// schema:
// type: string
// '500':
// description: Failed to dislike question
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if user already liked question
var questionLike models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
First(&questionLike).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
if questionLike.Id == "" {
return c.SendStatus(fiber.StatusConflict)
}
// dislike question
if err := database.DB.Delete(&questionLike).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// update question likes
if err := database.DB.Model(&models.Question{}).
Where("id = ?", params.QuestionId).
Update("likes", gorm.Expr("likes - 1")).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// send message to user clients who disliked the question
socketclients.SendMessageToUserWithTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionDisliked,
Body: question.Id,
},
)
// inform all users about the dislike
socketclients.BroadcastMessageToTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCountDownLikes,
Body: question.Id,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}

View File

@ -65,7 +65,9 @@ func GetRoles(c *fiber.Ctx) error {
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 {
if err := database.DB.Model(&models.User{}).
Where("role_id = ? AND organization_id = ?", key, c.Locals("organizationId").(string)).
Find(&users).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}

View File

@ -73,7 +73,9 @@ func UpdateOrganizationSettings(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(organizationSettings).Error; err != nil {
if err := database.DB.Model(&models.Organization{}).
Where("id = ?", c.Locals("organizationId").(string)).
Updates(organizationSettings).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -150,9 +152,10 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
selectField = "banner_url"
}
if err := database.DB.Model(&models.Organization{
Id: c.Locals("organizationId").(string),
}).Select(selectField).First(&organization).Error; err != nil {
if err := database.DB.Model(&models.Organization{}).
Where("id = ?", c.Locals("organizationId").(string)).
Select(selectField).
First(&organization).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -180,7 +183,9 @@ func UpdateOrganizationFile(c *fiber.Ctx) error {
update.BannerUrl = databasePath + fileName
}
if err := database.DB.Model(&models.Organization{}).Where("id = ?", c.Locals("organizationId").(string)).Updates(update).Error; err != nil {
if err := database.DB.Model(&models.Organization{}).
Where("id = ?", c.Locals("organizationId").(string)).
Updates(update).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -241,7 +246,10 @@ func UpdateSubdomain(c *fiber.Ctx) error {
var organization models.Organization
if err := database.DB.Select("subdomain").Model(organization).Where("id = ?", c.Locals("organizationId").(string)).First(&organization).Error; err != nil {
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)
}
@ -250,7 +258,9 @@ func UpdateSubdomain(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusBadRequest)
}
if err := database.DB.Model(&organization).Where("id = ?", c.Locals("organizationId")).Update("subdomain", params.Subdomain).Error; err != nil {
if err := database.DB.Model(&organization).
Where("id = ?", c.Locals("organizationId")).
Update("subdomain", params.Subdomain).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}

View File

@ -37,7 +37,9 @@ func GetTeamMembers(c *fiber.Ctx) error {
var users []structs.TeamMember
if err := database.DB.Model(&models.User{}).Where("organization_id = ?", c.Locals("organizationId")).Find(&users).Error; err != nil {
if err := database.DB.Model(&models.User{}).
Where("organization_id = ?", c.Locals("organizationId")).
Find(&users).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -176,8 +178,7 @@ func UpdateTeamMemberRole(c *fiber.Ctx) error {
}
if err := database.DB.Model(&models.User{}).
Where("id = ?", params.MemberId).
Where("organization_id = ?", c.Locals("organizationId").(string)).
Where("id = ? AND organization_id = ?", params.MemberId, c.Locals("organizationId").(string)).
Update("role_id", body.RoleId).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}

View File

@ -62,7 +62,8 @@ func UserLogin(c *fiber.Ctx) error {
organizationId := c.Locals("organizationId").(string)
if err := database.DB.Select("id", "disabled", "password").First(&user, "email = ? AND organization_id = ?", body.Email, organizationId).Error; err != nil {
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)
}

View File

@ -32,7 +32,10 @@ func GetUserProfile(c *fiber.Ctx) error {
var user structs.GetUserProfileResponse
database.DB.Model(&models.User{}).Where("id = ?", c.Locals("userId").(string)).First(&user)
database.DB.Model(&models.User{}).
Select("email", "first_name", "last_name", "profile_picture_url", "role_id").
Where("id = ?", c.Locals("userId").(string)).
First(&user)
return c.JSON(user)
}
@ -78,7 +81,10 @@ func UpdateUserProfilePicture(c *fiber.Ctx) error {
user := models.User{}
if err := database.DB.Model(&models.User{}).Select("profile_picture_url").Where("id = ?", c.Locals("userId").(string)).First(&user).Error; err != nil {
if err := database.DB.Model(&models.User{}).
Select("profile_picture_url").
Where("id = ?", c.Locals("userId").(string)).
First(&user).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -96,7 +102,9 @@ func UpdateUserProfilePicture(c *fiber.Ctx) error {
profilePictureUrl := databasePath + fileName
if err := database.DB.Model(&models.User{}).Where("id = ?", c.Locals("userId").(string)).Update("profile_picture_url", profilePictureUrl).Error; err != nil {
if err := database.DB.Model(&models.User{}).
Where("id = ?", c.Locals("userId").(string)).
Update("profile_picture_url", profilePictureUrl).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
@ -120,3 +128,46 @@ func UpdateUserProfilePicture(c *fiber.Ctx) error {
"Data": databasePath + fileName,
})
}
func GetUser(c *fiber.Ctx) error {
// swagger:operation GET /user/{userId} user getUser
// ---
// summary: Get user
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: userId
// in: path
// type: string
// required: true
// responses:
// '200':
// description: User fetched successfully
// schema:
// "$ref": "#/definitions/GetUserResponse"
// '400':
// description: Invalid request body
// '404':
// description: User not found
// '500':
// description: Failed to fetch user
var params structs.UserIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
var user structs.GetUserResponse
if err := database.DB.Model(&models.User{}).
Select("id", "first_name", "last_name", "profile_picture_url").
Where("id = ? AND organization_id = ?", params.UserId, c.Locals("organizationId").(string)).
First(&user).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
return c.JSON(user)
}

View File

@ -37,6 +37,7 @@ func SetupRoutes(app *fiber.App) {
u.Post("/auth/login", handleOrganizationSubdomain, user.UserLogin)
u.Get("/profile", handleOrganizationSubdomain, requestAccessValidation, user.GetUserProfile)
u.Post("/profile/picture", handleOrganizationSubdomain, requestAccessValidation, user.UpdateUserProfilePicture)
u.Get("/:userId", handleOrganizationSubdomain, requestAccessValidation, user.GetUser)
l := v1.Group("/lessons")
l.Get("/", handleOrganizationSubdomain, requestAccessValidation, lessons.GetLessons)
@ -51,6 +52,11 @@ func SetupRoutes(app *fiber.App) {
l.Patch("/:lessonId/contents/:contentId/position", handleOrganizationSubdomain, requestAccessValidation, lessons.UpdateLessonContentPosition)
l.Delete("/:lessonId/contents/:contentId", handleOrganizationSubdomain, requestAccessValidation, lessons.DeleteLessonContent)
l.Post("/:lessonId/contents/:contentId/file/:type", handleOrganizationSubdomain, requestAccessValidation, lessons.UploadLessonContentFile)
l.Get("/:lessonId/questions", handleOrganizationSubdomain, requestAccessValidation, lessons.GetQuestions)
l.Post("/:lessonId/questions", handleOrganizationSubdomain, requestAccessValidation, lessons.CreateQuestion)
l.Post("/:lessonId/questions/:questionId/replies", handleOrganizationSubdomain, requestAccessValidation, lessons.CreateQuestionReply)
l.Post("/:lessonId/questions/:questionId/likes", handleOrganizationSubdomain, requestAccessValidation, lessons.LikeQuestion)
l.Delete("/:lessonId/questions/:questionId/likes", handleOrganizationSubdomain, requestAccessValidation, lessons.DislikeQuestion)
app.Static("/static", config.Cfg.FolderPaths.PublicStatic)
}

View File

@ -72,7 +72,7 @@ func BroadcastMessageToTopics(organizationId string, topics []string, sendSocket
}
func hasClientSubscribedToTopic(topic string, clientTopic string) bool {
return clientTopic == topic || strings.HasPrefix(clientTopic, topic)
return clientTopic == topic /* || strings.HasPrefix(clientTopic, topic) */
}
// Used to determine if a user is connected regardless of the session used
@ -93,3 +93,11 @@ func SendMessageToUserWithTopicExceptBrowserTabSession(userId string, topic stri
}
}
}
func SendMessageToUserWithTopic(userId string, topic string, sendSocketMessage structs.SendSocketMessage) {
for _, client := range cache.GetSocketClients() {
if client.UserId == userId && client.SubscribedTopic == topic {
client.SendMessage(sendSocketMessage)
}
}
}