✨ Friends api
This commit is contained in:
		@@ -68,6 +68,7 @@ func main() {
 | 
			
		||||
	quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
 | 
			
		||||
	quartz.AddFunc("@every 60m", services.DoAutoSignoff)
 | 
			
		||||
	quartz.AddFunc("@every 60m", services.DoAutoAuthCleanup)
 | 
			
		||||
	quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
 | 
			
		||||
	quartz.Run()
 | 
			
		||||
 | 
			
		||||
	// Messages
 | 
			
		||||
 
 | 
			
		||||
@@ -5,21 +5,24 @@ import (
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var DatabaseAutoActionRange = []any{
 | 
			
		||||
	&models.Account{},
 | 
			
		||||
	&models.AuthFactor{},
 | 
			
		||||
	&models.AccountProfile{},
 | 
			
		||||
	&models.AccountPage{},
 | 
			
		||||
	&models.AccountContact{},
 | 
			
		||||
	&models.AccountFriendship{},
 | 
			
		||||
	&models.AuthSession{},
 | 
			
		||||
	&models.AuthChallenge{},
 | 
			
		||||
	&models.MagicToken{},
 | 
			
		||||
	&models.ThirdClient{},
 | 
			
		||||
	&models.ActionEvent{},
 | 
			
		||||
	&models.Notification{},
 | 
			
		||||
	&models.NotificationSubscriber{},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RunMigration(source *gorm.DB) error {
 | 
			
		||||
	if err := source.AutoMigrate(
 | 
			
		||||
		&models.Account{},
 | 
			
		||||
		&models.AuthFactor{},
 | 
			
		||||
		&models.AccountProfile{},
 | 
			
		||||
		&models.AccountPage{},
 | 
			
		||||
		&models.AccountContact{},
 | 
			
		||||
		&models.AuthSession{},
 | 
			
		||||
		&models.AuthChallenge{},
 | 
			
		||||
		&models.MagicToken{},
 | 
			
		||||
		&models.ThirdClient{},
 | 
			
		||||
		&models.ActionEvent{},
 | 
			
		||||
		&models.Notification{},
 | 
			
		||||
		&models.NotificationSubscriber{},
 | 
			
		||||
	); err != nil {
 | 
			
		||||
	if err := source.AutoMigrate(DatabaseAutoActionRange...); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								pkg/grpc/friendships.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/grpc/friendships.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
package grpc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/grpc/proto"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/models"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/services"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (v *Server) ListFriendship(_ context.Context, request *proto.FriendshipLookupRequest) (*proto.ListFriendshipResponse, error) {
 | 
			
		||||
	account, err := services.GetAccount(uint(request.GetAccountId()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	friends, err := services.ListFriend(account, models.FriendshipStatus(request.GetStatus()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &proto.ListFriendshipResponse{
 | 
			
		||||
		Data: lo.Map(friends, func(item models.AccountFriendship, index int) *proto.FriendshipResponse {
 | 
			
		||||
			return &proto.FriendshipResponse{
 | 
			
		||||
				AccountId: uint64(item.AccountID),
 | 
			
		||||
				RelatedId: uint64(item.RelatedID),
 | 
			
		||||
				Status:    uint32(item.Status),
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Server) GetFriendship(ctx context.Context, request *proto.FriendshipTwoSideLookupRequest) (*proto.FriendshipResponse, error) {
 | 
			
		||||
	friend, err := services.GetFriendWithTwoSides(uint(request.GetAccountId()), uint(request.GetRelatedId()))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else if friend.Status != models.FriendshipStatus(request.GetStatus()) {
 | 
			
		||||
		return nil, fmt.Errorf("status mismatch")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &proto.FriendshipResponse{
 | 
			
		||||
		AccountId: uint64(friend.AccountID),
 | 
			
		||||
		RelatedId: uint64(friend.RelatedID),
 | 
			
		||||
		Status:    uint32(friend.Status),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								pkg/grpc/proto/friendships.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								pkg/grpc/proto/friendships.proto
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
 | 
			
		||||
option go_package = ".;proto";
 | 
			
		||||
 | 
			
		||||
package proto;
 | 
			
		||||
 | 
			
		||||
service Friendships {
 | 
			
		||||
  rpc ListFriendship(FriendshipLookupRequest) returns (ListFriendshipResponse) {}
 | 
			
		||||
  rpc GetFriendship(FriendshipTwoSideLookupRequest) returns (FriendshipResponse) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message FriendshipLookupRequest {
 | 
			
		||||
  uint64 account_id = 1;
 | 
			
		||||
  uint32 status = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message FriendshipTwoSideLookupRequest {
 | 
			
		||||
  uint64 account_id = 1;
 | 
			
		||||
  uint64 related_id = 2;
 | 
			
		||||
  uint32 status = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message ListFriendshipResponse {
 | 
			
		||||
  repeated FriendshipResponse data = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message FriendshipResponse {
 | 
			
		||||
  uint64 account_id = 1;
 | 
			
		||||
  uint64 related_id = 2;
 | 
			
		||||
  uint32 status = 3;
 | 
			
		||||
}
 | 
			
		||||
@@ -12,6 +12,7 @@ import (
 | 
			
		||||
type Server struct {
 | 
			
		||||
	proto.UnimplementedAuthServer
 | 
			
		||||
	proto.UnimplementedNotifyServer
 | 
			
		||||
	proto.UnimplementedFriendshipsServer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func StartGrpc() error {
 | 
			
		||||
@@ -24,6 +25,7 @@ func StartGrpc() error {
 | 
			
		||||
 | 
			
		||||
	proto.RegisterAuthServer(server, &Server{})
 | 
			
		||||
	proto.RegisterNotifyServer(server, &Server{})
 | 
			
		||||
	proto.RegisterFriendshipsServer(server, &Server{})
 | 
			
		||||
 | 
			
		||||
	reflection.Register(server)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,24 +11,32 @@ import (
 | 
			
		||||
type Account struct {
 | 
			
		||||
	BaseModel
 | 
			
		||||
 | 
			
		||||
	Name              string                   `json:"name" gorm:"uniqueIndex"`
 | 
			
		||||
	Nick              string                   `json:"nick"`
 | 
			
		||||
	Description       string                   `json:"description"`
 | 
			
		||||
	Avatar            string                   `json:"avatar"`
 | 
			
		||||
	Banner            string                   `json:"banner"`
 | 
			
		||||
	Profile           AccountProfile           `json:"profile"`
 | 
			
		||||
	PersonalPage      AccountPage              `json:"personal_page"`
 | 
			
		||||
	Sessions          []AuthSession            `json:"sessions"`
 | 
			
		||||
	Challenges        []AuthChallenge          `json:"challenges"`
 | 
			
		||||
	Factors           []AuthFactor             `json:"factors"`
 | 
			
		||||
	Contacts          []AccountContact         `json:"contacts"`
 | 
			
		||||
	Events            []ActionEvent            `json:"events"`
 | 
			
		||||
	MagicTokens       []MagicToken             `json:"-" gorm:"foreignKey:AssignTo"`
 | 
			
		||||
	ThirdClients      []ThirdClient            `json:"clients"`
 | 
			
		||||
	Name        string     `json:"name" gorm:"uniqueIndex"`
 | 
			
		||||
	Nick        string     `json:"nick"`
 | 
			
		||||
	Description string     `json:"description"`
 | 
			
		||||
	Avatar      string     `json:"avatar"`
 | 
			
		||||
	Banner      string     `json:"banner"`
 | 
			
		||||
	ConfirmedAt *time.Time `json:"confirmed_at"`
 | 
			
		||||
	PowerLevel  int        `json:"power_level"`
 | 
			
		||||
 | 
			
		||||
	Profile      AccountProfile   `json:"profile"`
 | 
			
		||||
	PersonalPage AccountPage      `json:"personal_page"`
 | 
			
		||||
	Contacts     []AccountContact `json:"contacts"`
 | 
			
		||||
 | 
			
		||||
	Sessions   []AuthSession   `json:"sessions"`
 | 
			
		||||
	Challenges []AuthChallenge `json:"challenges"`
 | 
			
		||||
	Factors    []AuthFactor    `json:"factors"`
 | 
			
		||||
 | 
			
		||||
	Events      []ActionEvent `json:"events"`
 | 
			
		||||
	MagicTokens []MagicToken  `json:"-" gorm:"foreignKey:AssignTo"`
 | 
			
		||||
 | 
			
		||||
	ThirdClients []ThirdClient `json:"clients"`
 | 
			
		||||
 | 
			
		||||
	Notifications     []Notification           `json:"notifications" gorm:"foreignKey:RecipientID"`
 | 
			
		||||
	NotifySubscribers []NotificationSubscriber `json:"notify_subscribers"`
 | 
			
		||||
	ConfirmedAt       *time.Time               `json:"confirmed_at"`
 | 
			
		||||
	PowerLevel        int                      `json:"power_level"`
 | 
			
		||||
 | 
			
		||||
	Friendships        []AccountFriendship `json:"friendships" gorm:"foreignKey:AccountID"`
 | 
			
		||||
	RelatedFriendships []AccountFriendship `json:"related_friendships" gorm:"foreignKey:RelatedID"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v Account) GetPrimaryEmail() AccountContact {
 | 
			
		||||
@@ -64,3 +72,21 @@ type AccountContact struct {
 | 
			
		||||
	VerifiedAt *time.Time `json:"verified_at"`
 | 
			
		||||
	AccountID  uint       `json:"account_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FriendshipStatus = int8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FriendshipPending = FriendshipStatus(iota)
 | 
			
		||||
	FriendshipActive
 | 
			
		||||
	FriendshipBlocked
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AccountFriendship struct {
 | 
			
		||||
	BaseModel
 | 
			
		||||
 | 
			
		||||
	AccountID uint             `json:"account_id"`
 | 
			
		||||
	RelatedID uint             `json:"related_id"`
 | 
			
		||||
	Account   Account          `json:"account"`
 | 
			
		||||
	Related   Account          `json:"related"`
 | 
			
		||||
	Status    FriendshipStatus `json:"status"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										103
									
								
								pkg/server/friendships_api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								pkg/server/friendships_api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/models"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/services"
 | 
			
		||||
	"github.com/gofiber/fiber/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func listFriendship(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	status := c.QueryInt("status", int(models.FriendshipActive))
 | 
			
		||||
 | 
			
		||||
	if friends, err := services.ListFriend(user, models.FriendshipStatus(status)); err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 | 
			
		||||
	} else {
 | 
			
		||||
		return c.JSON(friends)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getFriendship(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	relatedId, _ := c.ParamsInt("relatedId", 0)
 | 
			
		||||
 | 
			
		||||
	related, err := services.GetAccount(uint(relatedId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if friend, err := services.GetFriendWithTwoSides(user.ID, related.ID); err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	} else {
 | 
			
		||||
		return c.JSON(friend)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeFriendship(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	relatedId, _ := c.ParamsInt("relatedId", 0)
 | 
			
		||||
 | 
			
		||||
	related, err := services.GetAccount(uint(relatedId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	friend, err := services.NewFriend(user, related, models.FriendshipPending)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
			
		||||
	} else {
 | 
			
		||||
		return c.JSON(friend)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func editFriendship(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	relatedId, _ := c.ParamsInt("relatedId", 0)
 | 
			
		||||
 | 
			
		||||
	var data struct {
 | 
			
		||||
		Status uint8 `json:"status"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := BindAndValidate(c, &data); err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	related, err := services.GetAccount(uint(relatedId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	friendship, err := services.GetFriendWithTwoSides(user.ID, related.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	} else if friendship.Status == models.FriendshipPending || data.Status == uint8(models.FriendshipPending) {
 | 
			
		||||
		if friendship.RelatedID != user.ID {
 | 
			
		||||
			return fiber.NewError(fiber.StatusNotFound, "only related person can accept or revoke accept friendship")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if friendship, err := services.EditFriend(friendship); err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
			
		||||
	} else {
 | 
			
		||||
		return c.JSON(friendship)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteFriendship(c *fiber.Ctx) error {
 | 
			
		||||
	user := c.Locals("principal").(models.Account)
 | 
			
		||||
	relatedId, _ := c.ParamsInt("relatedId", 0)
 | 
			
		||||
 | 
			
		||||
	related, err := services.GetAccount(uint(relatedId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	friendship, err := services.GetFriendWithTwoSides(user.ID, related.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := services.DeleteFriend(friendship); err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
			
		||||
	} else {
 | 
			
		||||
		return c.JSON(friendship)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -86,6 +86,15 @@ func NewServer() {
 | 
			
		||||
			me.Delete("/sessions/:sessionId", authMiddleware, killSession)
 | 
			
		||||
 | 
			
		||||
			me.Post("/confirm", doRegisterConfirm)
 | 
			
		||||
 | 
			
		||||
			friends := me.Group("/friends").Name("Friends")
 | 
			
		||||
			{
 | 
			
		||||
				friends.Get("/", authMiddleware, listFriendship)
 | 
			
		||||
				friends.Get("/:relatedId", authMiddleware, getFriendship)
 | 
			
		||||
				friends.Post("/:relatedId", authMiddleware, makeFriendship)
 | 
			
		||||
				friends.Put("/:relatedId", authMiddleware, editFriendship)
 | 
			
		||||
				friends.Delete("/:relatedId", authMiddleware, deleteFriendship)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		directory := api.Group("/users/:alias").Name("User Directory")
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ func getOtherUserinfo(c *fiber.Ctx) error {
 | 
			
		||||
	if err := database.C.
 | 
			
		||||
		Where(&models.Account{Name: alias}).
 | 
			
		||||
		Omit("sessions", "challenges", "factors", "events", "clients", "notifications", "notify_subscribers").
 | 
			
		||||
		Preload("Profile").
 | 
			
		||||
		First(&account).Error; err != nil {
 | 
			
		||||
		return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								pkg/services/cleaner.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								pkg/services/cleaner.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/database"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func DoAutoDatabaseCleanup() {
 | 
			
		||||
	deadline := time.Now().Add(60 * time.Minute)
 | 
			
		||||
	log.Debug().Time("deadline", deadline).Msg("Now cleaning up entire database...")
 | 
			
		||||
 | 
			
		||||
	var count int64
 | 
			
		||||
	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...")
 | 
			
		||||
		}
 | 
			
		||||
		count += tx.RowsAffected
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debug().Int64("affected", count).Msg("Clean up entire database accomplished.")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								pkg/services/friendships.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								pkg/services/friendships.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/database"
 | 
			
		||||
	"git.solsynth.dev/hydrogen/identity/pkg/models"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ListFriend(anyside models.Account, status models.FriendshipStatus) ([]models.AccountFriendship, error) {
 | 
			
		||||
	var relationships []models.AccountFriendship
 | 
			
		||||
	if err := database.C.
 | 
			
		||||
		Where(&models.AccountFriendship{Status: status}).
 | 
			
		||||
		Where(&models.AccountFriendship{AccountID: anyside.ID}).
 | 
			
		||||
		Or(&models.AccountFriendship{RelatedID: anyside.ID}).
 | 
			
		||||
		Find(&relationships).Error; err != nil {
 | 
			
		||||
		return relationships, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return relationships, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetFriend(anysideId uint) (models.AccountFriendship, error) {
 | 
			
		||||
	var relationship models.AccountFriendship
 | 
			
		||||
	if err := database.C.
 | 
			
		||||
		Where(&models.AccountFriendship{AccountID: anysideId}).
 | 
			
		||||
		Or(&models.AccountFriendship{RelatedID: anysideId}).
 | 
			
		||||
		Preload("Account").
 | 
			
		||||
		Preload("Related").
 | 
			
		||||
		First(&relationship).Error; err != nil {
 | 
			
		||||
		return relationship, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return relationship, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetFriendWithTwoSides(userId, relatedId uint, noPreload ...bool) (models.AccountFriendship, error) {
 | 
			
		||||
	var tx *gorm.DB
 | 
			
		||||
	if len(noPreload) > 0 && noPreload[0] {
 | 
			
		||||
		tx = database.C
 | 
			
		||||
	} else {
 | 
			
		||||
		tx = database.C.Preload("Account").Preload("Related")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var relationship models.AccountFriendship
 | 
			
		||||
	if err := tx.
 | 
			
		||||
		Where(&models.AccountFriendship{AccountID: userId, RelatedID: relatedId}).
 | 
			
		||||
		Or(&models.AccountFriendship{RelatedID: userId, AccountID: relatedId}).
 | 
			
		||||
		First(&relationship).Error; err != nil {
 | 
			
		||||
		return relationship, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return relationship, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFriend(user models.Account, related models.Account, status models.FriendshipStatus) (models.AccountFriendship, error) {
 | 
			
		||||
	relationship := models.AccountFriendship{
 | 
			
		||||
		AccountID: user.ID,
 | 
			
		||||
		RelatedID: related.ID,
 | 
			
		||||
		Status:    status,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if user.ID == related.ID {
 | 
			
		||||
		return relationship, fmt.Errorf("you cannot make friendship with yourself")
 | 
			
		||||
	} else if _, err := GetFriendWithTwoSides(user.ID, related.ID, true); err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
 | 
			
		||||
		return relationship, fmt.Errorf("you already have a friendship with him or her")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := database.C.Save(&relationship).Error; err != nil {
 | 
			
		||||
		return relationship, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return relationship, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EditFriend(relationship models.AccountFriendship) (models.AccountFriendship, error) {
 | 
			
		||||
	if err := database.C.Save(&relationship).Error; err != nil {
 | 
			
		||||
		return relationship, err
 | 
			
		||||
	}
 | 
			
		||||
	return relationship, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DeleteFriend(relationship models.AccountFriendship) error {
 | 
			
		||||
	if err := database.C.Delete(&relationship).Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -27,7 +27,7 @@ func DoAutoSignoff() {
 | 
			
		||||
	duration := time.Duration(viper.GetInt64("security.auto_signoff_duration")) * time.Second
 | 
			
		||||
	divider := time.Now().Add(-duration)
 | 
			
		||||
 | 
			
		||||
	log.Debug().Time("before", divider).Msg("Now auto signing off sessions...")
 | 
			
		||||
	log.Debug().Time("before", divider).Msg("Now signing off sessions...")
 | 
			
		||||
 | 
			
		||||
	if tx := database.C.
 | 
			
		||||
		Where("last_grant_at < ?", divider).
 | 
			
		||||
@@ -39,7 +39,7 @@ func DoAutoSignoff() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DoAutoAuthCleanup() {
 | 
			
		||||
	log.Debug().Msg("Now auto cleaning up cached auth context...")
 | 
			
		||||
	log.Debug().Msg("Now cleaning up cached auth context...")
 | 
			
		||||
 | 
			
		||||
	count := 0
 | 
			
		||||
	err := database.B.Batch(func(tx *bbolt.Tx) error {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user