You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
5.8 KiB
241 lines
5.8 KiB
package services
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"git.aiterp.net/rpdata/api/models"
|
|
"git.aiterp.net/rpdata/api/models/changekeys"
|
|
"git.aiterp.net/rpdata/api/repositories"
|
|
"git.aiterp.net/rpdata/api/services/loaders"
|
|
"log"
|
|
"sort"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type CharacterService struct {
|
|
characters repositories.CharacterRepository
|
|
loader *loaders.CharacterLoader
|
|
changeService *ChangeService
|
|
authService *AuthService
|
|
logService *LogService
|
|
|
|
refreshSoftLock uint32
|
|
}
|
|
|
|
// Find uses the loader to find the character by the ID.
|
|
func (s *CharacterService) Find(ctx context.Context, id string) (*models.Character, error) {
|
|
return s.loader.Load(id)
|
|
}
|
|
|
|
// Find uses the loader to find the character by the ID.
|
|
func (s *CharacterService) FindNick(ctx context.Context, nick string) (*models.Character, error) {
|
|
return s.characters.FindNick(ctx, nick)
|
|
}
|
|
|
|
// List lists the characters. If the only filter active is `IDs`, the loader is used to batch together requests.
|
|
func (s *CharacterService) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
|
|
if len(filter.IDs) > 0 && len(filter.Names) == 0 && len(filter.Nicks) == 0 && filter.Author == nil && filter.Search == nil {
|
|
characters, errs := s.loader.LoadAll(filter.IDs)
|
|
if len(characters) == 0 && len(errs) > 0 {
|
|
if errs[0] == repositories.ErrNotFound {
|
|
return []*models.Character{}, nil
|
|
} else {
|
|
return nil, errs[0]
|
|
}
|
|
}
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var badIndices []int
|
|
for i, character := range characters {
|
|
if character == nil {
|
|
badIndices = append(badIndices, i-len(badIndices))
|
|
}
|
|
}
|
|
for _, index := range badIndices {
|
|
characters = append(characters[:index], characters[index+1:]...)
|
|
}
|
|
|
|
sort.Slice(characters, func(i, j int) bool {
|
|
return strings.Compare(characters[i].ID, characters[j].ID) < 0
|
|
})
|
|
|
|
return characters, nil
|
|
}
|
|
|
|
go s.refreshLogs()
|
|
|
|
return s.characters.List(ctx, filter)
|
|
}
|
|
|
|
func (s *CharacterService) Create(ctx context.Context, nick, name, shortName, author, description string) (*models.Character, error) {
|
|
token := s.authService.TokenFromContext(ctx)
|
|
if token == nil {
|
|
return nil, ErrUnauthenticated
|
|
}
|
|
|
|
if name == "" {
|
|
return nil, errors.New("name cannot be empty")
|
|
}
|
|
|
|
// Insert nick into existing if character already exists.
|
|
if character, err := s.characters.FindName(ctx, name); err == nil && character.Name == name {
|
|
return s.AddNick(ctx, character.ID, nick)
|
|
}
|
|
|
|
if author == "" {
|
|
author = token.UserID
|
|
}
|
|
|
|
if shortName == "" {
|
|
split := strings.SplitN(name, " ", 2)
|
|
shortName = split[0]
|
|
}
|
|
|
|
character := &models.Character{
|
|
Name: name,
|
|
ShortName: shortName,
|
|
Author: author,
|
|
Nicks: []string{nick},
|
|
Description: description,
|
|
}
|
|
|
|
err := s.authService.CheckPermission(ctx, "add", character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
character, err = s.characters.Insert(ctx, *character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.changeService.Submit(ctx, "Character", "add", true, changekeys.Listed(character), character)
|
|
|
|
go s.refreshLogs()
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (s *CharacterService) Update(ctx context.Context, id string, name, shortName, description *string) (*models.Character, error) {
|
|
character, err := s.characters.Find(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.authService.CheckPermission(ctx, "edit", character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
character, err = s.characters.Update(ctx, *character, models.CharacterUpdate{
|
|
Name: name,
|
|
ShortName: shortName,
|
|
Description: description,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.loader.Clear(character.ID)
|
|
s.loader.Prime(character.ID, character)
|
|
|
|
s.changeService.Submit(ctx, "Character", "edit", true, changekeys.Listed(character), character)
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (s *CharacterService) AddNick(ctx context.Context, id string, nick string) (*models.Character, error) {
|
|
character, err := s.characters.Find(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.authService.CheckPermission(ctx, "edit", character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
character, err = s.characters.AddNick(ctx, *character, nick)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.loader.Clear(character.ID)
|
|
s.loader.Prime(character.ID, character)
|
|
|
|
s.changeService.Submit(ctx, "Character", "edit", true, changekeys.Listed(character), character)
|
|
|
|
go s.refreshLogs()
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (s *CharacterService) RemoveNick(ctx context.Context, id string, nick string) (*models.Character, error) {
|
|
character, err := s.characters.Find(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.authService.CheckPermission(ctx, "edit", character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
character, err = s.characters.RemoveNick(ctx, *character, nick)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.loader.Clear(character.ID)
|
|
s.loader.Prime(character.ID, character)
|
|
|
|
s.changeService.Submit(ctx, "Character", "edit", true, changekeys.Listed(character), character)
|
|
|
|
go s.refreshLogs()
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (s *CharacterService) Delete(ctx context.Context, id string) (*models.Character, error) {
|
|
character, err := s.characters.Find(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.authService.CheckPermission(ctx, "edit", character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = s.characters.Delete(ctx, *character)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
s.loader.Clear(character.ID)
|
|
|
|
s.changeService.Submit(ctx, "Character", "remove", true, changekeys.Listed(character), character)
|
|
|
|
go s.refreshLogs()
|
|
|
|
return character, nil
|
|
}
|
|
|
|
func (s *CharacterService) refreshLogs() {
|
|
if !atomic.CompareAndSwapUint32(&s.refreshSoftLock, 0, 1) {
|
|
return
|
|
}
|
|
time.Sleep(time.Second * 60)
|
|
atomic.StoreUint32(&s.refreshSoftLock, 0)
|
|
|
|
err := s.logService.RefreshAllLogCharacters(context.Background())
|
|
if err != nil {
|
|
log.Println("Failed to refersh log characters:", err)
|
|
return
|
|
}
|
|
}
|