feat: add Winter user groups

- user_groups (seeded with the guest and registered groups) and the
  users_groups pivot, folded from the PHP v1.1.1 and v2.8.0 migrations
  into migration 202610020001_create_user_groups
- models.UserGroup and a User.Groups relation that is never serialized
- classes.UserGroupCodes and classes.HasGroupCode for server-side checks
  such as the site-admin group code admin
- additive only: no route, payload or existing column changes; the user
  payload still answers "groups":[]
This commit is contained in:
Jakub Zych
2026-10-02 11:44:01 +02:00
parent c65ab3cdac
commit 86970074b7
8 changed files with 301 additions and 13 deletions

View File

@@ -30,6 +30,11 @@ type User struct {
CreatedIPAddress *string `gorm:"column:created_ip_address"`
LastIPAddress *string `gorm:"column:last_ip_address"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at"`
// Groups is the PHP belongsToMany groups relation through users_groups.
// It is loaded only on request, written only by a caller that
// associates groups, and never serialized: the user API answers
// "groups":[] from its own payload builder.
Groups []UserGroup `gorm:"many2many:users_groups;joinForeignKey:user_id;joinReferences:user_group_id" json:"-"`
}
func (User) TableName() string { return "users" }

29
models/user_group.go Normal file
View File

@@ -0,0 +1,29 @@
package models
import "time"
// UserGroup is a row of user_groups, the PHP UserGroup model. Membership
// lives in the users_groups pivot (User.Groups). Permissions is the raw
// jsonable text column.
type UserGroup struct {
ID uint `gorm:"column:id;primaryKey"`
Name string `gorm:"column:name"`
Code *string `gorm:"column:code"`
Description *string `gorm:"column:description"`
Permissions *string `gorm:"column:permissions"`
CreatedAt *time.Time `gorm:"column:created_at"`
UpdatedAt *time.Time `gorm:"column:updated_at"`
}
func (UserGroup) TableName() string { return "user_groups" }
// The PHP UserGroup::GROUP_GUEST and GROUP_REGISTERED codes, seeded by the
// create_user_groups migration.
const (
GroupGuest = "guest"
GroupRegistered = "registered"
)
func init() {
Register(UserGroup{})
}