diff --git a/go.mod b/go.mod index a119841..557a76c 100644 --- a/go.mod +++ b/go.mod @@ -5,13 +5,13 @@ go 1.22 toolchain go1.22.1 require ( - git.solsynth.dev/hydrogen/passport v0.0.0-20240504085931-7c418a3cd32f + git.solsynth.dev/hydrogen/paperclip v0.0.0-20240520143155-2b131982b821 + git.solsynth.dev/hydrogen/passport v0.0.0-20240517121420-1e2d5e9f9d87 github.com/go-playground/validator/v10 v10.17.0 github.com/gofiber/contrib/websocket v1.3.0 github.com/gofiber/fiber/v2 v2.52.4 github.com/gofiber/template/html/v2 v2.1.1 github.com/golang-jwt/jwt/v5 v5.2.0 - github.com/google/uuid v1.6.0 github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/json-iterator/go v1.1.12 github.com/livekit/protocol v1.14.0 @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.31.0 github.com/samber/lo v1.39.0 github.com/spf13/viper v1.18.2 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 google.golang.org/grpc v1.63.2 gorm.io/datatypes v1.2.0 gorm.io/driver/postgres v1.5.4 @@ -50,6 +50,7 @@ require ( github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/utils v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -60,7 +61,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jxskiss/base62 v1.1.0 // indirect github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/shortuuid/v4 v4.0.0 // indirect github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 // indirect @@ -124,10 +125,10 @@ require ( go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.2.0 // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 6fb4d05..ef5b947 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ -git.solsynth.dev/hydrogen/passport v0.0.0-20240504085931-7c418a3cd32f h1:sKrQrKZc5C+dwefRsnc0uAGttzpSUWXUBoFaCXLkaTo= -git.solsynth.dev/hydrogen/passport v0.0.0-20240504085931-7c418a3cd32f/go.mod h1:3JRFPtf0dXRk2UQ1yVIgIspNfytM2yLBeBePJChgLZE= +git.solsynth.dev/hydrogen/paperclip v0.0.0-20240520143155-2b131982b821 h1:azsoKVRp239SkfLU6yg6LkMc2t39vRr6aYCEVstKnms= +git.solsynth.dev/hydrogen/paperclip v0.0.0-20240520143155-2b131982b821/go.mod h1:uTNEtJcNdgt7DhOgsewPaLQQ5kTN9H+tGNRT2CshHGs= +git.solsynth.dev/hydrogen/passport v0.0.0-20240517121420-1e2d5e9f9d87 h1:r+x72tRB9LTJFH3F2rIKydQUXREc7lgxITDnjfFWwGw= +git.solsynth.dev/hydrogen/passport v0.0.0-20240517121420-1e2d5e9f9d87/go.mod h1:mEcDEKashAh3jvoGDbNLefK+HgsJaMj4xEc6vkLZ+Zc= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= @@ -121,8 +123,8 @@ github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -330,8 +332,8 @@ golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIi golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -354,8 +356,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -391,8 +393,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -414,8 +416,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/pkg/cmd/main.go b/pkg/cmd/main.go index 87803ff..ffa0d0e 100644 --- a/pkg/cmd/main.go +++ b/pkg/cmd/main.go @@ -1,13 +1,14 @@ package main import ( - "git.solsynth.dev/hydrogen/messaging/pkg/external" - "git.solsynth.dev/hydrogen/messaging/pkg/services" - "github.com/robfig/cron/v3" "os" "os/signal" "syscall" + "git.solsynth.dev/hydrogen/messaging/pkg/external" + "git.solsynth.dev/hydrogen/messaging/pkg/services" + "github.com/robfig/cron/v3" + "git.solsynth.dev/hydrogen/messaging/pkg/grpc" "git.solsynth.dev/hydrogen/messaging/pkg/server" @@ -45,7 +46,10 @@ func main() { // Connect other services external.SetupLiveKit() if err := grpc.ConnectPassport(); err != nil { - log.Fatal().Err(err).Msg("An error occurred when connecting to identity grpc endpoint...") + log.Fatal().Err(err).Msg("An error occurred when connecting to passport...") + } + if err := grpc.ConnectPaperclip(); err != nil { + log.Fatal().Err(err).Msg("An error occurred when connecting to paperclip...") } // Server diff --git a/pkg/database/migrator.go b/pkg/database/migrator.go index 535265f..4903fef 100644 --- a/pkg/database/migrator.go +++ b/pkg/database/migrator.go @@ -12,7 +12,6 @@ var DatabaseAutoActionRange = []any{ &models.ChannelMember{}, &models.Call{}, &models.Message{}, - &models.Attachment{}, } func RunMigration(source *gorm.DB) error { diff --git a/pkg/grpc/client.go b/pkg/grpc/client.go index 3a11c0f..81d2486 100644 --- a/pkg/grpc/client.go +++ b/pkg/grpc/client.go @@ -1,6 +1,7 @@ package grpc import ( + pcpb "git.solsynth.dev/hydrogen/paperclip/pkg/grpc/proto" idpb "git.solsynth.dev/hydrogen/passport/pkg/grpc/proto" "google.golang.org/grpc/credentials/insecure" @@ -8,13 +9,28 @@ import ( "google.golang.org/grpc" ) -var Realms idpb.RealmsClient -var Friendships idpb.FriendshipsClient -var Notify idpb.NotifyClient -var Auth idpb.AuthClient +var Attachments pcpb.AttachmentsClient + +func ConnectPaperclip() error { + addr := viper.GetString("paperclip.grpc_endpoint") + if conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())); err != nil { + return err + } else { + Attachments = pcpb.NewAttachmentsClient(conn) + } + + return nil +} + +var ( + Realms idpb.RealmsClient + Friendships idpb.FriendshipsClient + Notify idpb.NotifyClient + Auth idpb.AuthClient +) func ConnectPassport() error { - addr := viper.GetString("identity.grpc_endpoint") + addr := viper.GetString("passport.grpc_endpoint") if conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())); err != nil { return err } else { diff --git a/pkg/models/accounts.go b/pkg/models/accounts.go index cf03c69..8fef2e8 100644 --- a/pkg/models/accounts.go +++ b/pkg/models/accounts.go @@ -6,14 +6,13 @@ package models type Account struct { BaseModel - Name string `json:"name"` - Nick string `json:"nick"` - Avatar string `json:"avatar"` - Banner string `json:"banner"` - Description string `json:"description"` - EmailAddress string `json:"email_address"` - PowerLevel int `json:"power_level"` - Attachments []Attachment `json:"attachments" gorm:"foreignKey:AuthorID"` - Channels []Channel `json:"channels"` - ExternalID uint `json:"external_id"` + Name string `json:"name"` + Nick string `json:"nick"` + Avatar string `json:"avatar"` + Banner string `json:"banner"` + Description string `json:"description"` + EmailAddress string `json:"email_address"` + PowerLevel int `json:"power_level"` + Channels []Channel `json:"channels"` + ExternalID uint `json:"external_id"` } diff --git a/pkg/models/attachments.go b/pkg/models/attachments.go deleted file mode 100644 index 45ea6e9..0000000 --- a/pkg/models/attachments.go +++ /dev/null @@ -1,41 +0,0 @@ -package models - -import ( - "fmt" - "path/filepath" - - "github.com/spf13/viper" -) - -type AttachmentType = uint8 - -const ( - AttachmentOthers = AttachmentType(iota) - AttachmentPhoto - AttachmentVideo - AttachmentAudio -) - -type Attachment struct { - BaseModel - - FileID string `json:"file_id"` - Filesize int64 `json:"filesize"` - Filename string `json:"filename"` - Mimetype string `json:"mimetype"` - Hashcode string `json:"hashcode"` - Type AttachmentType `json:"type"` - ExternalUrl string `json:"external_url"` - Author Account `json:"author"` - MessageID *uint `json:"message_id"` - AuthorID uint `json:"author_id"` -} - -func (v Attachment) GetStoragePath() string { - basepath := viper.GetString("content") - return filepath.Join(basepath, v.FileID) -} - -func (v Attachment) GetAccessPath() string { - return fmt.Sprintf("/api/attachments/o/%s", v.FileID) -} diff --git a/pkg/models/channels.go b/pkg/models/channels.go index ad14b2c..7dc7ddb 100644 --- a/pkg/models/channels.go +++ b/pkg/models/channels.go @@ -3,8 +3,8 @@ package models type ChannelType = uint8 const ( - ChannelTypeDirect = ChannelType(iota) - ChannelTypeRealm + ChannelTypeCommon = ChannelType(iota) + ChannelTypeDirect ) type Channel struct { @@ -36,11 +36,12 @@ const ( type ChannelMember struct { BaseModel - ChannelID uint `json:"channel_id"` - AccountID uint `json:"account_id"` - Channel Channel `json:"channel"` - Account Account `json:"account"` - Notify NotifyLevel `json:"notify"` + ChannelID uint `json:"channel_id"` + AccountID uint `json:"account_id"` + Channel Channel `json:"channel"` + Account Account `json:"account"` + Notify NotifyLevel `json:"notify"` + PowerLevel int `json:"power_level"` Calls []Call `json:"calls" gorm:"foreignKey:FounderID"` Messages []Message `json:"messages" gorm:"foreignKey:SenderID"` diff --git a/pkg/models/messages.go b/pkg/models/messages.go index 11375e1..b1145e9 100644 --- a/pkg/models/messages.go +++ b/pkg/models/messages.go @@ -1,15 +1,18 @@ package models +import "gorm.io/datatypes" + type Message struct { BaseModel - Content []byte `json:"content"` - Type string `json:"type"` - Attachments []Attachment `json:"attachments"` - Channel Channel `json:"channel"` - Sender ChannelMember `json:"sender"` - ReplyID *uint `json:"reply_id"` - ReplyTo *Message `json:"reply_to" gorm:"foreignKey:ReplyID"` - ChannelID uint `json:"channel_id"` - SenderID uint `json:"sender_id"` + Uuid string `json:"uuid"` + Content datatypes.JSONMap `json:"content"` + Type string `json:"type"` + Attachments datatypes.JSONSlice[uint] `json:"attachments"` + Channel Channel `json:"channel"` + Sender ChannelMember `json:"sender"` + ReplyID *uint `json:"reply_id"` + ReplyTo *Message `json:"reply_to" gorm:"foreignKey:ReplyID"` + ChannelID uint `json:"channel_id"` + SenderID uint `json:"sender_id"` } diff --git a/pkg/server/attachments_api.go b/pkg/server/attachments_api.go deleted file mode 100644 index d1c8daa..0000000 --- a/pkg/server/attachments_api.go +++ /dev/null @@ -1,61 +0,0 @@ -package server - -import ( - "path/filepath" - - "git.solsynth.dev/hydrogen/messaging/pkg/models" - "git.solsynth.dev/hydrogen/messaging/pkg/services" - "github.com/gofiber/fiber/v2" - "github.com/spf13/viper" -) - -func readAttachment(c *fiber.Ctx) error { - id := c.Params("fileId") - basepath := viper.GetString("content") - - return c.SendFile(filepath.Join(basepath, id)) -} - -func uploadAttachment(c *fiber.Ctx) error { - user := c.Locals("principal").(models.Account) - hashcode := c.FormValue("hashcode") - if len(hashcode) != 64 { - return fiber.NewError(fiber.StatusBadRequest, "please provide a SHA256 hashcode, length should be 64 characters") - } - file, err := c.FormFile("attachment") - if err != nil { - return err - } - - attachment, err := services.NewAttachment(user, file, hashcode) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - if err := c.SaveFile(file, attachment.GetStoragePath()); err != nil { - return err - } - - return c.JSON(fiber.Map{ - "info": attachment, - "url": attachment.GetAccessPath(), - }) -} - -func deleteAttachment(c *fiber.Ctx) error { - id, _ := c.ParamsInt("id", 0) - user := c.Locals("principal").(models.Account) - - attachment, err := services.GetAttachmentByID(uint(id)) - if err != nil { - return fiber.NewError(fiber.StatusNotFound, err.Error()) - } else if attachment.AuthorID != user.ID { - return fiber.NewError(fiber.StatusNotFound, "record not created by you") - } - - if err := services.DeleteAttachment(attachment); err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } else { - return c.SendStatus(fiber.StatusOK) - } -} diff --git a/pkg/server/channel_members_api.go b/pkg/server/channel_members_api.go index ce2249c..33adc59 100644 --- a/pkg/server/channel_members_api.go +++ b/pkg/server/channel_members_api.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "git.solsynth.dev/hydrogen/messaging/pkg/database" "git.solsynth.dev/hydrogen/messaging/pkg/models" "git.solsynth.dev/hydrogen/messaging/pkg/services" @@ -43,10 +44,13 @@ func addChannelMember(c *fiber.Ctx) error { var channel models.Channel if err := database.C.Where(&models.Channel{ - Alias: alias, - AccountID: user.ID, + Alias: alias, }).First(&channel).Error; err != nil { return fiber.NewError(fiber.StatusNotFound, err.Error()) + } else if member, err := services.GetChannelMember(user, channel.ID); err != nil { + return fiber.NewError(fiber.StatusForbidden, err.Error()) + } else if member.PowerLevel < 50 { + return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to add member into it") } var account models.Account @@ -81,6 +85,10 @@ func removeChannelMember(c *fiber.Ctx) error { AccountID: user.ID, }).First(&channel).Error; err != nil { return fiber.NewError(fiber.StatusNotFound, err.Error()) + } else if member, err := services.GetChannelMember(user, channel.ID); err != nil { + return fiber.NewError(fiber.StatusForbidden, err.Error()) + } else if member.PowerLevel < 50 { + return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of a channel to remove member into it") } var account models.Account diff --git a/pkg/server/channels_api.go b/pkg/server/channels_api.go index 68597ab..ab5e0d5 100644 --- a/pkg/server/channels_api.go +++ b/pkg/server/channels_api.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "git.solsynth.dev/hydrogen/messaging/pkg/database" "git.solsynth.dev/hydrogen/messaging/pkg/models" "git.solsynth.dev/hydrogen/messaging/pkg/services" @@ -111,22 +112,31 @@ func createChannel(c *fiber.Ctx) error { var realm *models.Realm if val, ok := c.Locals("realm").(models.Realm); ok { if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil { - return fmt.Errorf("you must be a part of that realm then can create channel related to it") + return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it") } else if info.GetPowerLevel() < 50 { - return fmt.Errorf("you must be a moderator of that realm then can create channel related to it") + return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it") } else { realm = &val } } - var err error - var channel models.Channel - if realm != nil { - channel, err = services.NewChannel(user, data.Alias, data.Name, data.Description, data.IsEncrypted, realm.ID) - } else { - channel, err = services.NewChannel(user, data.Alias, data.Name, data.Description, data.IsEncrypted) + channel := models.Channel{ + Alias: data.Alias, + Name: data.Name, + Description: data.Description, + IsEncrypted: data.IsEncrypted, + AccountID: user.ID, + Type: models.ChannelTypeCommon, + Members: []models.ChannelMember{ + {AccountID: user.ID, PowerLevel: 100}, + }, } + if realm != nil { + channel.RealmID = &realm.ID + } + + channel, err := services.NewChannel(channel) if err != nil { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } @@ -153,14 +163,14 @@ func editChannel(c *fiber.Ctx) error { if val, ok := c.Locals("realm").(models.Realm); ok { if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil { - return fmt.Errorf("you must be a part of that realm then can edit channel related to it") + return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can edit channel related to it") } else if info.GetPowerLevel() < 50 { - return fmt.Errorf("you must be a moderator of that realm then can edit channel related to it") + return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can edit channel related to it") } else { tx = tx.Where("realm_id = ?", val.ID) } } else { - tx = tx.Where("account_id = ? AND realm_id IS NULL", user.ID) + tx = tx.Where("realm_id IS NULL") } var channel models.Channel @@ -168,6 +178,14 @@ func editChannel(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusNotFound, err.Error()) } + if channel.RealmID != nil { + if member, err := services.GetChannelMember(user, channel.ID); err != nil { + return fiber.NewError(fiber.StatusForbidden, "you must be a part of this channel to edit it") + } else if member.PowerLevel < 100 { + return fiber.NewError(fiber.StatusForbidden, "you must be channel admin to edit it") + } + } + channel, err := services.EditChannel(channel, data.Alias, data.Name, data.Description, data.IsEncrypted) if err != nil { return fiber.NewError(fiber.StatusBadRequest, err.Error()) diff --git a/pkg/server/direct_channels_api.go b/pkg/server/direct_channels_api.go new file mode 100644 index 0000000..a2194d0 --- /dev/null +++ b/pkg/server/direct_channels_api.go @@ -0,0 +1,68 @@ +package server + +import ( + "git.solsynth.dev/hydrogen/messaging/pkg/database" + "git.solsynth.dev/hydrogen/messaging/pkg/models" + "git.solsynth.dev/hydrogen/messaging/pkg/services" + "github.com/gofiber/fiber/v2" + "github.com/samber/lo" +) + +func createDirectChannel(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + + var data struct { + Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"` + Name string `json:"name" validate:"required"` + Description string `json:"description"` + Members []uint `json:"members"` + IsEncrypted bool `json:"is_encrypted"` + } + + if err := BindAndValidate(c, &data); err != nil { + return err + } else if err = services.GetChannelAliasAvailability(data.Alias); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + var realm *models.Realm + if val, ok := c.Locals("realm").(models.Realm); ok { + if info, err := services.GetRealmMember(val.ExternalID, user.ExternalID); err != nil { + return fiber.NewError(fiber.StatusForbidden, "you must be a part of that realm then can create channel related to it") + } else if info.GetPowerLevel() < 50 { + return fiber.NewError(fiber.StatusForbidden, "you must be a moderator of that realm then can create channel related to it") + } else { + realm = &val + } + } + + var members []models.Account + if err := database.C.Where("id IN ?", data.Members).Find(&members).Error; err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + channel := models.Channel{ + Alias: data.Alias, + Name: data.Name, + Description: data.Description, + IsEncrypted: data.IsEncrypted, + AccountID: user.ID, + Type: models.ChannelTypeDirect, + Members: append([]models.ChannelMember{ + {AccountID: user.ID, PowerLevel: 100}, + }, lo.Map(members, func(item models.Account, idx int) models.ChannelMember { + return models.ChannelMember{AccountID: item.ID, PowerLevel: 100} + })...), + } + + if realm != nil { + channel.RealmID = &realm.ID + } + + channel, err := services.NewChannel(channel) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(channel) +} diff --git a/pkg/server/messages_api.go b/pkg/server/messages_api.go index 597b7ca..a78b3b5 100644 --- a/pkg/server/messages_api.go +++ b/pkg/server/messages_api.go @@ -1,8 +1,8 @@ package server import ( - "encoding/json" "fmt" + "git.solsynth.dev/hydrogen/messaging/pkg/database" "git.solsynth.dev/hydrogen/messaging/pkg/models" "git.solsynth.dev/hydrogen/messaging/pkg/services" @@ -45,16 +45,25 @@ func newMessage(c *fiber.Ctx) error { alias := c.Params("channel") var data struct { - Type string `json:"type" validate:"required"` - Content map[string]any `json:"content"` - Attachments []models.Attachment `json:"attachments"` - ReplyTo *uint `json:"reply_to"` + Uuid string `json:"uuid" validate:"required"` + Type string `json:"type" validate:"required"` + Content map[string]any `json:"content"` + Attachments []uint `json:"attachments"` + ReplyTo *uint `json:"reply_to"` } if err := BindAndValidate(c, &data); err != nil { return err } else if len(data.Attachments) == 0 && len(data.Content) == 0 { - return fmt.Errorf("you must write or upload some content in a single message") + return fiber.NewError(fiber.StatusBadRequest, "you must write or upload some content in a single message") + } else if len(data.Uuid) < 36 { + return fiber.NewError(fiber.StatusBadRequest, "message uuid was not valid") + } + + for _, attachment := range data.Attachments { + if !services.CheckAttachmentByIDExists(attachment, "m.attachment") { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("attachment %d not found", attachment)) + } } var err error @@ -72,13 +81,9 @@ func newMessage(c *fiber.Ctx) error { } } - rawContent, err := json.Marshal(data.Content) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid message content, unable to encode: %v", err)) - } - message := models.Message{ - Content: rawContent, + Uuid: data.Uuid, + Content: data.Content, Sender: member, Channel: channel, ChannelID: channel.ID, @@ -110,16 +115,22 @@ func editMessage(c *fiber.Ctx) error { messageId, _ := c.ParamsInt("messageId", 0) var data struct { - Type string `json:"type" validate:"required"` - Content map[string]any `json:"content"` - Attachments []models.Attachment `json:"attachments"` - ReplyTo *uint `json:"reply_to"` + Type string `json:"type" validate:"required"` + Content map[string]any `json:"content"` + Attachments []uint `json:"attachments"` + ReplyTo *uint `json:"reply_to"` } if err := BindAndValidate(c, &data); err != nil { return err } + for _, attachment := range data.Attachments { + if !services.CheckAttachmentByIDExists(attachment, "m.attachment") { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("attachment %d not found", attachment)) + } + } + var err error var channel models.Channel var member models.ChannelMember @@ -140,13 +151,8 @@ func editMessage(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusNotFound, err.Error()) } - rawContent, err := json.Marshal(data.Content) - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid message content, unable to encode: %v", err)) - } - message.Attachments = data.Attachments - message.Content = rawContent + message.Content = data.Content message.Type = data.Type message, err = services.EditMessage(message) diff --git a/pkg/server/messages_ws.go b/pkg/server/messages_ws.go index 5fdcb58..ac66e1e 100644 --- a/pkg/server/messages_ws.go +++ b/pkg/server/messages_ws.go @@ -5,6 +5,7 @@ import ( "git.solsynth.dev/hydrogen/messaging/pkg/services" "github.com/gofiber/contrib/websocket" jsoniter "github.com/json-iterator/go" + "github.com/rs/zerolog/log" ) func messageGateway(c *websocket.Conn) { @@ -12,6 +13,7 @@ func messageGateway(c *websocket.Conn) { // Push connection services.ClientRegister(user, c) + log.Debug().Uint("user", user.ID).Msg("New websocket connection established...") // Event loop var task models.UnifiedCommand @@ -42,4 +44,5 @@ func messageGateway(c *websocket.Conn) { // Pop connection services.ClientUnregister(user, c) + log.Debug().Uint("user", user.ID).Msg("A websocket connection disconnected...") } diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 8562d19..940fd76 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -3,14 +3,12 @@ package server import ( "net/http" "strings" - "time" "git.solsynth.dev/hydrogen/messaging/pkg" "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2/middleware/favicon" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cache" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/idempotency" "github.com/gofiber/fiber/v2/middleware/logger" @@ -68,22 +66,16 @@ func NewServer() { api.Get("/users/me", authMiddleware, getUserinfo) api.Get("/users/:accountId", getOthersInfo) - api.Get("/attachments/o/:fileId", cache.New(cache.Config{ - Expiration: 365 * 24 * time.Hour, - CacheControl: true, - }), readAttachment) - api.Post("/attachments", authMiddleware, uploadAttachment) - api.Delete("/attachments/:id", authMiddleware, deleteAttachment) - channels := api.Group("/channels/:realm").Use(realmMiddleware).Name("Channels API") { channels.Get("/", listChannel) - channels.Get("/:channel", getChannel) - channels.Get("/:channel/availability", authMiddleware, getChannelAvailability) channels.Get("/me", authMiddleware, listOwnedChannel) channels.Get("/me/available", authMiddleware, listAvailableChannel) + channels.Get("/:channel", getChannel) + channels.Get("/:channel/availability", authMiddleware, getChannelAvailability) channels.Post("/", authMiddleware, createChannel) + channels.Post("/dm", authMiddleware, createDirectChannel) channels.Put("/:channelId", authMiddleware, editChannel) channels.Delete("/:channelId", authMiddleware, deleteChannel) diff --git a/pkg/services/attachments.go b/pkg/services/attachments.go index 9abcf9b..3a19ec5 100644 --- a/pkg/services/attachments.go +++ b/pkg/services/attachments.go @@ -1,127 +1,30 @@ package services import ( - "mime/multipart" - "net/http" - "os" - "path/filepath" - "strings" + "context" - "git.solsynth.dev/hydrogen/messaging/pkg/database" - "git.solsynth.dev/hydrogen/messaging/pkg/models" - "github.com/google/uuid" - "github.com/spf13/viper" + "git.solsynth.dev/hydrogen/messaging/pkg/grpc" + pcpb "git.solsynth.dev/hydrogen/paperclip/pkg/grpc/proto" + "github.com/samber/lo" ) -func GetAttachmentByID(id uint) (models.Attachment, error) { - var attachment models.Attachment - if err := database.C.Where(models.Attachment{ - BaseModel: models.BaseModel{ID: id}, - }).First(&attachment).Error; err != nil { - return attachment, err - } - return attachment, nil +func GetAttachmentByID(id uint) (*pcpb.Attachment, error) { + return grpc.Attachments.GetAttachment(context.Background(), &pcpb.AttachmentLookupRequest{ + Id: lo.ToPtr(uint64(id)), + }) } -func GetAttachmentByUUID(fileId string) (models.Attachment, error) { - var attachment models.Attachment - if err := database.C.Where(models.Attachment{ - FileID: fileId, - }).First(&attachment).Error; err != nil { - return attachment, err - } - return attachment, nil +func GetAttachmentByUUID(uuid string) (*pcpb.Attachment, error) { + return grpc.Attachments.GetAttachment(context.Background(), &pcpb.AttachmentLookupRequest{ + Uuid: &uuid, + }) } -func GetAttachmentByHashcode(hashcode string) (models.Attachment, error) { - var attachment models.Attachment - if err := database.C.Where(models.Attachment{ - Hashcode: hashcode, - }).First(&attachment).Error; err != nil { - return attachment, err - } - return attachment, nil -} - -func NewAttachment(user models.Account, header *multipart.FileHeader, hashcode string) (models.Attachment, error) { - var attachment models.Attachment - existsAttachment, err := GetAttachmentByHashcode(hashcode) - if err != nil { - // Upload the new file - attachment = models.Attachment{ - FileID: uuid.NewString(), - Filesize: header.Size, - Filename: header.Filename, - Hashcode: hashcode, - Mimetype: "unknown/unknown", - Type: models.AttachmentOthers, - AuthorID: user.ID, - } - - // Open file - file, err := header.Open() - if err != nil { - return attachment, err - } - defer file.Close() - - // Detect mimetype - fileHeader := make([]byte, 512) - _, err = file.Read(fileHeader) - if err != nil { - return attachment, err - } - attachment.Mimetype = http.DetectContentType(fileHeader) - - switch strings.Split(attachment.Mimetype, "/")[0] { - case "image": - attachment.Type = models.AttachmentPhoto - case "video": - attachment.Type = models.AttachmentVideo - case "audio": - attachment.Type = models.AttachmentAudio - default: - attachment.Type = models.AttachmentOthers - } - } else { - // Instant upload, build link with the exists file - attachment = models.Attachment{ - FileID: existsAttachment.FileID, - Filesize: header.Size, - Filename: header.Filename, - Hashcode: hashcode, - Mimetype: existsAttachment.Mimetype, - Type: existsAttachment.Type, - AuthorID: user.ID, - } - } - - // Save into database - err = database.C.Save(&attachment).Error - - return attachment, err -} - -func DeleteAttachment(item models.Attachment) error { - var dupeCount int64 - if err := database.C. - Where(&models.Attachment{Hashcode: item.Hashcode}). - Model(&models.Attachment{}). - Count(&dupeCount).Error; err != nil { - dupeCount = -1 - } - - if err := database.C.Delete(&item).Error; err != nil { - return err - } - - if dupeCount != -1 && dupeCount <= 1 { - // Safe for deletion the physics file - basepath := viper.GetString("content") - fullpath := filepath.Join(basepath, item.FileID) - - os.Remove(fullpath) - } - - return nil +func CheckAttachmentByIDExists(id uint, usage string) bool { + _, err := grpc.Attachments.CheckAttachmentExists(context.Background(), &pcpb.AttachmentLookupRequest{ + Id: lo.ToPtr(uint64(id)), + Usage: &usage, + }) + + return err == nil } diff --git a/pkg/services/channel_members.go b/pkg/services/channel_members.go index 7165e8e..ccf3a9c 100644 --- a/pkg/services/channel_members.go +++ b/pkg/services/channel_members.go @@ -2,6 +2,7 @@ package services import ( "fmt" + "git.solsynth.dev/hydrogen/messaging/pkg/database" "git.solsynth.dev/hydrogen/messaging/pkg/models" ) @@ -19,6 +20,18 @@ func ListChannelMember(channelId uint) ([]models.ChannelMember, error) { return members, nil } +func GetChannelMember(user models.Account, channelId uint) (models.ChannelMember, error) { + var member models.ChannelMember + + if err := database.C. + Where(&models.ChannelMember{AccountID: user.ID, ChannelID: channelId}). + Find(&member).Error; err != nil { + return member, err + } + + return member, nil +} + func AddChannelMemberWithCheck(user models.Account, target models.Channel) error { if _, err := GetAccountFriend(user.ID, target.AccountID, 1); err != nil { return fmt.Errorf("you only can invite your friends to your channel") diff --git a/pkg/services/channels.go b/pkg/services/channels.go index a3c189e..17ba455 100644 --- a/pkg/services/channels.go +++ b/pkg/services/channels.go @@ -80,11 +80,9 @@ func GetAvailableChannel(id uint, user models.Account) (models.Channel, models.C func ListChannel(realmId ...uint) ([]models.Channel, error) { var channels []models.Channel - tx := database.C.Preload("Account") + tx := database.C.Preload("Account").Preload("Realm") if len(realmId) > 0 { tx = tx.Where("realm_id = ?", realmId) - } else { - tx = tx.Where("realm_id IS NULL") } if err := tx.Find(&channels).Error; err != nil { return channels, err @@ -95,7 +93,7 @@ func ListChannel(realmId ...uint) ([]models.Channel, error) { func ListChannelWithUser(user models.Account, realmId ...uint) ([]models.Channel, error) { var channels []models.Channel - tx := database.C.Where(&models.Channel{AccountID: user.ID}) + tx := database.C.Where(&models.Channel{AccountID: user.ID}).Preload("Realm") if len(realmId) > 0 { tx = tx.Where("realm_id = ?", realmId) } else { @@ -121,7 +119,7 @@ func ListAvailableChannel(user models.Account, realmId ...uint) ([]models.Channe return item.ChannelID }) - tx := database.C.Where("id IN ?", idx) + tx := database.C.Preload("Realm").Where("id IN ?", idx) if len(realmId) > 0 { tx = tx.Where("realm_id = ?", realmId) } else { @@ -134,23 +132,8 @@ func ListAvailableChannel(user models.Account, realmId ...uint) ([]models.Channe return channels, nil } -func NewChannel(user models.Account, alias, name, description string, isEncrypted bool, realmId ...uint) (models.Channel, error) { - channel := models.Channel{ - Alias: alias, - Name: name, - Description: description, - IsEncrypted: isEncrypted, - AccountID: user.ID, - Members: []models.ChannelMember{ - {AccountID: user.ID}, - }, - } - if len(realmId) > 0 { - channel.RealmID = &realmId[0] - } - +func NewChannel(channel models.Channel) (models.Channel, error) { err := database.C.Save(&channel).Error - return channel, err } diff --git a/pkg/services/cleaner.go b/pkg/services/cleaner.go index 1d49f0a..640140d 100644 --- a/pkg/services/cleaner.go +++ b/pkg/services/cleaner.go @@ -1,10 +1,10 @@ package services import ( - "git.solsynth.dev/hydrogen/messaging/pkg/database" - "git.solsynth.dev/hydrogen/messaging/pkg/models" - "github.com/rs/zerolog/log" "time" + + "git.solsynth.dev/hydrogen/messaging/pkg/database" + "github.com/rs/zerolog/log" ) func DoAutoDatabaseCleanup() { @@ -16,18 +16,10 @@ func DoAutoDatabaseCleanup() { for _, model := range database.DatabaseAutoActionRange { tx := database.C.Unscoped().Delete(model, "deleted_at >= ?", deadline) if tx.Error != nil { - log.Error().Err(tx.Error).Msg("An error occurred when running auth context cleanup...") + log.Error().Err(tx.Error).Msg("An error occurred when running database cleanup...") } count += tx.RowsAffected } - // Clean up outdated chat history - tx := database.C.Unscoped().Delete(&models.Message{}, "created_at < ?", time.Now().Add(30*24*time.Hour)) - if tx.Error != nil { - log.Error().Err(tx.Error).Msg("An error occurred when running auth context cleanup...") - } else { - count += tx.RowsAffected - } - log.Debug().Int64("affected", count).Msg("Clean up entire database accomplished.") } diff --git a/pkg/services/connections.go b/pkg/services/connections.go index 3c413d0..579a19d 100644 --- a/pkg/services/connections.go +++ b/pkg/services/connections.go @@ -1,24 +1,33 @@ package services import ( + "sync" + "git.solsynth.dev/hydrogen/messaging/pkg/models" "github.com/gofiber/contrib/websocket" ) -var wsConn = make(map[uint]map[*websocket.Conn]bool) +var ( + wsMutex sync.Mutex + wsConn = make(map[uint]map[*websocket.Conn]bool) +) func ClientRegister(user models.Account, conn *websocket.Conn) { + wsMutex.Lock() if wsConn[user.ID] == nil { wsConn[user.ID] = make(map[*websocket.Conn]bool) } wsConn[user.ID][conn] = true + wsMutex.Unlock() } func ClientUnregister(user models.Account, conn *websocket.Conn) { + wsMutex.Lock() if wsConn[user.ID] == nil { wsConn[user.ID] = make(map[*websocket.Conn]bool) } delete(wsConn[user.ID], conn) + wsMutex.Unlock() } func PushCommand(userId uint, task models.UnifiedCommand) { diff --git a/pkg/services/messages.go b/pkg/services/messages.go index eaad381..c35863d 100644 --- a/pkg/services/messages.go +++ b/pkg/services/messages.go @@ -2,11 +2,11 @@ package services import ( "fmt" + "git.solsynth.dev/hydrogen/messaging/pkg/database" "git.solsynth.dev/hydrogen/messaging/pkg/models" - "github.com/gofiber/fiber/v2" - jsoniter "github.com/json-iterator/go" "github.com/rs/zerolog/log" + "github.com/samber/lo" ) func CountMessage(channel models.Channel) int64 { @@ -31,7 +31,6 @@ func ListMessage(channel models.Channel, take int, offset int) ([]models.Message ChannelID: channel.ID, }).Limit(take).Offset(offset). Order("created_at DESC"). - Preload("Attachments"). Preload("ReplyTo"). Preload("ReplyTo.Sender"). Preload("ReplyTo.Sender.Account"). @@ -54,7 +53,6 @@ func GetMessage(channel models.Channel, id uint) (models.Message, error) { Preload("ReplyTo"). Preload("ReplyTo.Sender"). Preload("ReplyTo.Sender.Account"). - Preload("Attachments"). Preload("Sender"). Preload("Sender.Account"). First(&message).Error; err != nil { @@ -78,9 +76,6 @@ func GetMessageWithPrincipal(channel models.Channel, member models.ChannelMember } func NewMessage(message models.Message) (models.Message, error) { - var decodedContent fiber.Map - _ = jsoniter.Unmarshal(message.Content, &decodedContent) - var members []models.ChannelMember if err := database.C.Save(&message).Error; err != nil { return message, err @@ -91,20 +86,39 @@ func NewMessage(message models.Message) (models.Message, error) { message, _ = GetMessage(message.Channel, message.ID) for _, member := range members { if member.ID != message.Sender.ID { - // TODO Check the mentioned status - if member.Notify == models.NotifyLevelAll { - displayText := "*encrypted message*" - if decodedContent["algorithm"] == "plain" { - displayText, _ = decodedContent["value"].(string) - } - err = NotifyAccount(member.Account, - fmt.Sprintf("New Message #%s", channel.Alias), - fmt.Sprintf("%s: %s", message.Sender.Account.Name, displayText), - true, - ) - if err != nil { - log.Warn().Err(err).Msg("An error occurred when trying notify user.") + switch member.Notify { + case models.NotifyLevelNone: + continue + case models.NotifyLevelMentioned: + if val, ok := message.Content["metioned_users"]; ok { + if usernames, ok := val.([]string); ok { + if lo.Contains(usernames, member.Account.Name) { + break + } + } } + + continue + } + + var displayText string + if message.Content["algorithm"] == "plain" { + displayText, _ = message.Content["value"].(string) + } else { + displayText = "*encrypted message*" + } + + if len(displayText) == 0 { + displayText = fmt.Sprintf("%d attachment(s)", len(message.Attachments)) + } + + err = NotifyAccount(member.Account, + fmt.Sprintf("New Message #%s", channel.Alias), + fmt.Sprintf("%s: %s", message.Sender.Account.Name, displayText), + true, + ) + if err != nil { + log.Warn().Err(err).Msg("An error occurred when trying notify user.") } } PushCommand(member.AccountID, models.UnifiedCommand{ diff --git a/settings.toml b/settings.toml index c18fa49..a474756 100644 --- a/settings.toml +++ b/settings.toml @@ -10,14 +10,18 @@ secret = "LtTjzAGFLshwXhN4ZD4nG5KlMv1MWcsvfv03TSZYnT1VhiAnLIZFTnHUwR0XhGgi" content = "uploads" [debug] -database = true +database = false print_routes = false -[identity] +[paperclip] +endpoint = "http://localhost:8443" +grpc_endpoint = "localhost:7443" + +[passport] client_id = "solarecho" client_secret = "Z9k9AFTj^p" -endpoint = "https://id.solsynth.dev" -grpc_endpoint = "id.solsynth.dev:7444" +endpoint = "http://localhost:8444" +grpc_endpoint = "localhost:7444" [calling] api_key = "APIZwKRLAWaWa8d" @@ -41,5 +45,5 @@ access_token_duration = 300 refresh_token_duration = 2592000 [database] -dsn = "host=localhost dbname=hy_messaging port=5432 sslmode=disable" +dsn = "host=localhost user=postgres password=password dbname=hy_messaging port=5432 sslmode=disable" prefix = "messaging_"