feat: custom content type icons, built-in types, user lookup

This commit is contained in:
Florian Sylvain committed 2026-10-19 18:22:55 +02:00
1 parent e6e088c963
commit 73a75d5cb9
31 files changed
+1611 -36

No files matched your search

@@ -15,6 +15,7 @@ func ContentTypeToDomain(m models.ContentType) (contenttype.ContentType, error)
ID: m.ID,
Key: m.Key,
Name: m.Name,
Icon: m.Icon,
Fields: fields,
CreatedAt: m.CreatedAt,
UpdatedAt: m.UpdatedAt,
@@ -30,6 +31,7 @@ func ContentTypeToModel(c contenttype.ContentType) (models.ContentType, error) {
ID: c.ID,
Key: c.Key,
Name: c.Name,
Icon: c.Icon,
Fields: string(fields),
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
@@ -6,6 +6,7 @@ type ContentType struct {
ID uint32 `gorm:"primaryKey;autoIncrement"`
Key string `gorm:"column:type_key;size:40;not null;uniqueIndex"`
Name string `gorm:"size:100;not null"`
Icon *string `gorm:"size:40"`
Fields string `gorm:"type:text;not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
@@ -60,7 +60,7 @@ func (c *ContentTypeRepository) Update(contentType domain.ContentType) (domain.C
if err != nil {
return domain.ContentType{}, err
}
result := c.db.Model(&entity.ContentType{}).Where("id = ?", model.ID).Select("Name", "Fields", "UpdatedAt").Updates(&model)
result := c.db.Model(&entity.ContentType{}).Where("id = ?", model.ID).Select("Name", "Icon", "Fields", "UpdatedAt").Updates(&model)
if err := deleteOrNotFound(result, domain.ErrContentTypeNotFound); err != nil {
return domain.ContentType{}, mapKeyTakenErr(err)
}
@@ -62,6 +62,18 @@ func (u *UserRepository) GetAll() []domainUser.User {
return domainUsers
}
func (u *UserRepository) FindByIDs(ids []uint32) ([]domainUser.User, error) {
var users []entity.User
if err := u.db.Select("id", "username", "display_name").Where("id IN ?", ids).Find(&users).Error; err != nil {
return nil, err
}
found := make([]domainUser.User, 0, len(users))
for _, localUser := range users {
found = append(found, mappers.UserToDomain(localUser))
}
return found, nil
}
func (u *UserRepository) GetByUsername(username string) (domainUser.User, error) {
var localUser entity.User
err := u.db.Model(&entity.User{}).Where("username = ?", username).First(&localUser).Error
@@ -14,6 +14,7 @@ type CreateContentTypeUseCase struct {
type CreateContentTypeCommand struct {
Key string
Name string
Icon *string
Fields []contenttype.Field
}
@@ -28,6 +29,10 @@ func (u *CreateContentTypeUseCase) Create(command CreateContentTypeCommand) (con
if err := contenttype.ValidateName(command.Name); err != nil {
return contenttype.ContentType{}, err
}
icon, err := contenttype.ValidateIcon(command.Icon)
if err != nil {
return contenttype.ContentType{}, err
}
fields, err := contenttype.ValidateFields(command.Fields)
if err != nil {
return contenttype.ContentType{}, err
@@ -39,6 +44,7 @@ func (u *CreateContentTypeUseCase) Create(command CreateContentTypeCommand) (con
return u.contentTypes.Create(contenttype.ContentType{
Key: command.Key,
Name: command.Name,
Icon: icon,
Fields: fields,
CreatedAt: created,
UpdatedAt: created,
@@ -0,0 +1,13 @@
package useCases
import "RenewCMS/internal/domain/builtintype"
type GetBuiltinTypeUseCase struct{}
func NewGetBuiltinTypeUseCase() *GetBuiltinTypeUseCase {
return &GetBuiltinTypeUseCase{}
}
func (u *GetBuiltinTypeUseCase) Get(key string) (builtintype.Type, error) {
return builtintype.Find(key)
}
@@ -5,7 +5,6 @@ import (
"RenewCMS/internal/domain/user"
"errors"
"sort"
"strings"
"time"
)
@@ -62,7 +61,7 @@ func (l *ListAPIKeysUseCase) creatorNamesFor(requester user.User, keys []apikey.
if requester.Role != user.RoleOwner {
return names, nil
}
names[requester.ID] = displayNameOrUsername(requester)
names[requester.ID] = requester.NameOrUsername()
for _, key := range keys {
if _, resolved := names[key.UserID]; resolved {
continue
@@ -75,18 +74,11 @@ func (l *ListAPIKeysUseCase) creatorNamesFor(requester user.User, keys []apikey.
if err != nil {
return nil, err
}
names[key.UserID] = displayNameOrUsername(creator)
names[key.UserID] = creator.NameOrUsername()
}
return names, nil
}
func displayNameOrUsername(account user.User) string {
if trimmed := strings.TrimSpace(account.DisplayName); trimmed != "" {
return trimmed
}
return account.Username
}
func (l *ListAPIKeysUseCase) keysVisibleTo(requester user.User) ([]apikey.APIKey, error) {
if requester.Role == user.RoleOwner {
return l.apiKeyRepository.ListAll()
@@ -0,0 +1,13 @@
package useCases
import "RenewCMS/internal/domain/builtintype"
type ListBuiltinTypesUseCase struct{}
func NewListBuiltinTypesUseCase() *ListBuiltinTypesUseCase {
return &ListBuiltinTypesUseCase{}
}
func (u *ListBuiltinTypesUseCase) List() []builtintype.Type {
return builtintype.Catalogue()
}
@@ -0,0 +1,48 @@
package useCases
import (
"RenewCMS/internal/domain/user"
"sort"
)
type LookupUserNamesUseCase struct {
users user.Repository
}
type UserName struct {
ID uint32
DisplayName string
}
func NewLookupUserNamesUseCase(users user.Repository) *LookupUserNamesUseCase {
return &LookupUserNamesUseCase{users}
}
func (u *LookupUserNamesUseCase) Lookup(ids []uint32) ([]UserName, error) {
distinct := distinctIDs(ids)
if len(distinct) == 0 || len(distinct) > user.MaxLookupIDs {
return nil, user.ErrInvalidLookup
}
found, err := u.users.FindByIDs(distinct)
if err != nil {
return nil, err
}
sort.Slice(found, func(i, j int) bool { return found[i].ID < found[j].ID })
names := make([]UserName, 0, len(found))
for _, record := range found {
names = append(names, UserName{ID: record.ID, DisplayName: record.NameOrUsername()})
}
return names, nil
}
func distinctIDs(ids []uint32) []uint32 {
seen := make(map[uint32]bool, len(ids))
distinct := make([]uint32, 0, len(ids))
for _, id := range ids {
if !seen[id] {
seen[id] = true
distinct = append(distinct, id)
}
}
return distinct
}
@@ -15,6 +15,7 @@ type UpdateContentTypeUseCase struct {
type UpdateContentTypeCommand struct {
Key string
Name string
Icon *string
Fields []contenttype.Field
}
@@ -30,6 +31,10 @@ func (u *UpdateContentTypeUseCase) Update(command UpdateContentTypeCommand) (con
if err := contenttype.ValidateName(command.Name); err != nil {
return contenttype.ContentType{}, err
}
icon, err := contenttype.ValidateIcon(command.Icon)
if err != nil {
return contenttype.ContentType{}, err
}
fields, err := contenttype.ValidateFields(command.Fields)
if err != nil {
return contenttype.ContentType{}, err
@@ -44,6 +49,7 @@ func (u *UpdateContentTypeUseCase) Update(command UpdateContentTypeCommand) (con
}
}
current.Name = command.Name
current.Icon = icon
current.Fields = fields
current.UpdatedAt = u.now()
return u.contentTypes.Update(current)