model

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ActivityResourceRedirect ActivityResource = "REDIRECT"
	ActivityResourcePage     ActivityResource = "PAGE"
	ActivityResourceProject  ActivityResource = "PROJECT"
	// ActivityResourceActivity is the journal itself, which can be cleared.
	ActivityResourceActivity ActivityResource = "ACTIVITY"

	ActivityActionCreate   ActivityAction = "CREATE"
	ActivityActionUpdate   ActivityAction = "UPDATE"
	ActivityActionDelete   ActivityAction = "DELETE"
	ActivityActionImport   ActivityAction = "IMPORT"
	ActivityActionRollback ActivityAction = "ROLLBACK"
	ActivityActionPublish  ActivityAction = "PUBLISH"
	// ActivityActionTruncate wipes every entry of a resource in a project.
	ActivityActionTruncate ActivityAction = "TRUNCATE"
)
View Source
const (
	AdminSectionUsers      SectionType = "users"
	AdminSectionRoles      SectionType = "roles"
	AdminSectionProjects   SectionType = "projects"
	AdminSectionNamespaces SectionType = "namespaces"
	AdminSectionTokens     SectionType = "tokens"
	AdminSectionAll        SectionType = "*"

	ActionRead  ActionType = "read"
	ActionWrite ActionType = "write"
	ActionAll   ActionType = "*"

	ResourceTypeRedirect ResourceType = "redirect"
	ResourceTypePage     ResourceType = "page"
	ResourceTypeAgent    ResourceType = "agent"
	ResourceTypeAll      ResourceType = "*"
	ResourceTypeAny      ResourceType = "any"
)
View Source
const (
	TokenPrefix        = "flecto_"
	TokenNameMaxLength = 300
	TokenPreviewChars  = 4 // Number of characters to show at start and end of preview
)
View Source
const ActivityActorSystem = "system"

ActivityActorSystem is recorded when no authenticated subject is in the context, for instance a CLI command.

View Source
const ActivityImportErrorSampleMax = 20

ActivityImportErrorSampleMax bounds the errors kept in an import payload. The payload keeps an exact ErrorCount, so a truncated sample is detectable with errorCount > len(errorSample).

View Source
const (
	ColumnNamespaceCode = "namespace_code"
)
View Source
const (
	ColumnProjectCode = "project_code"
)

Variables

View Source
var ActivityEventSortableColumns = map[string]string{
	"occurredAt": "occurred_at",
	"resource":   "resource",
	"action":     "action",
	"actor":      "actor",
}
View Source
var AgentSortableColumns = map[string]string{
	"name":      "name",
	"status":    "status",
	"type":      "type",
	"createAt":  "created_at",
	"updatedAt": "updated_at",
	"lastHitAt": "last_hit_at",
}
View Source
var ErrActivityDataReadOnly = errors.New("activity data is read-only")

ErrActivityDataReadOnly is returned when a client tries to supply an activity payload.

View Source
var NamespaceSortableColumns = map[string]string{
	"id":             "id",
	"namespace_code": ColumnNamespaceCode,
	"name":           "name",
	"createdAt":      "created_at",
	"updatedAt":      "updated_at",
}
View Source
var PageSortableColumns = map[string]string{
	"path":        "path",
	"contentType": "content_type",
	"type":        "type",
	"updatedAt":   "updated_at",
}
View Source
var ProjectSortableColumns = map[string]string{
	"id":             "id",
	"namespace_code": ColumnNamespaceCode,
	"code":           ColumnProjectCode,
	"name":           "name",
	"createdAt":      "created_at",
	"updatedAt":      "updated_at",
}
View Source
var RedirectSortableColumns = map[string]string{
	"source":    "source",
	"target":    "target",
	"type":      "type",
	"status":    "status",
	"updatedAt": "updated_at",
}
View Source
var RoleSortableColumns = map[string]string{
	"id":        "id",
	"code":      "code",
	"type":      "type",
	"createdAt": "created_at",
	"updatedAt": "updated_at",
}
View Source
var TokenSortableColumns = map[string]string{
	"id":        "id",
	"name":      "name",
	"createdAt": "created_at",
	"updatedAt": "updated_at",
	"expiresAt": "expires_at",
}
View Source
var UserSortableColumns = map[string]string{
	"id":        "id",
	"username":  "username",
	"lastname":  "lastname",
	"firstname": "firstname",
	"createdAt": "created_at",
	"updatedAt": "updated_at",
}
View Source
var ValidRoleNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)

Functions

func GenerateTokenPreview

func GenerateTokenPreview(plainToken string) string

GenerateTokenPreview creates a preview string like "flecto_abcd...wxyz" from the full token

Types

type ActionType

type ActionType string

type ActivityAction added in v0.2.0

type ActivityAction string

ActivityAction is what was done to the resource. The pair (ActivityResource, ActivityAction) determines the shape of the event payload, see activity_payload.go.

type ActivityChange added in v0.2.0

type ActivityChange[T any] struct {
	Before *T `json:"before,omitempty"`
	After  *T `json:"after,omitempty"`
}

ActivityChange carries the before and after of a single-entry change. Before is nil on a creation, After is nil on a deletion.

type ActivityData added in v0.2.0

type ActivityData json.RawMessage

ActivityData is a raw JSON payload written by the server only. Its shape is given by the (Resource, Action) pair of the event carrying it, and the front-end picks a renderer from that same pair.

func (ActivityData) MarshalGQL added in v0.2.0

func (d ActivityData) MarshalGQL(w io.Writer)

MarshalGQL writes the payload straight into the response: it already holds JSON produced server-side.

func (ActivityData) MarshalJSON added in v0.2.0

func (d ActivityData) MarshalJSON() ([]byte, error)

MarshalJSON emits the payload as-is. Without it the underlying []byte would be base64 encoded.

func (*ActivityData) Scan added in v0.2.0

func (d *ActivityData) Scan(value any) error

func (*ActivityData) UnmarshalGQL added in v0.2.0

func (d *ActivityData) UnmarshalGQL(any) error

UnmarshalGQL always fails: activity payloads are never provided by clients.

func (*ActivityData) UnmarshalJSON added in v0.2.0

func (d *ActivityData) UnmarshalJSON(data []byte) error

func (ActivityData) Value added in v0.2.0

func (d ActivityData) Value() (driver.Value, error)

type ActivityDraftCounts added in v0.2.0

type ActivityDraftCounts struct {
	Create int64 `json:"create"`
	Update int64 `json:"update"`
	Delete int64 `json:"delete"`
}

ActivityDraftCounts breaks discarded drafts down by change type.

func (*ActivityDraftCounts) IsEmpty added in v0.2.0

func (c *ActivityDraftCounts) IsEmpty() bool

IsEmpty reports whether nothing was discarded, in which case the rollback is a no-op and deserves no journal entry.

type ActivityEvent added in v0.2.0

type ActivityEvent struct {
	ID            int64  `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string `json:"-" gorm:"size:50;not null;index:idx_activity_events_ns_proj,priority:1"`
	ProjectCode   string `json:"-" gorm:"size:50;not null;index:idx_activity_events_ns_proj,priority:2"`

	Resource ActivityResource `json:"resource" gorm:"size:20;not null"`
	Action   ActivityAction   `json:"action" gorm:"size:20;not null"`

	// Actor is a snapshot: the username for a user session, the token name for an
	// API token. It is deliberately not a foreign key, so the trail stays readable
	// after the user is deleted or renamed. UserID is set to NULL by the database
	// when the user is deleted.
	UserID   *int64            `json:"userID" gorm:"index:idx_activity_events_user"`
	Actor    string            `json:"actor" gorm:"size:300;not null"`
	AuthType appTypes.AuthType `json:"authType" gorm:"size:20"`

	// ResourceID is the redirect or page id when the event targets a single entry,
	// nil for aggregated events (import, project rollback, publish).
	ResourceID *int64       `json:"resourceID" gorm:"index:idx_activity_events_resource"`
	Data       ActivityData `json:"data" gorm:"type:longtext"`

	OccurredAt time.Time `json:"occurredAt" gorm:"type:timestamp;not null"`
}

ActivityEvent records one user action on a project. Rows are immutable: they are never updated, only inserted and eventually purged.

type ActivityEventList added in v0.2.0

type ActivityEventList = commonTypes.PaginatedResult[ActivityEvent]

type ActivityImport added in v0.2.0

type ActivityImport struct {
	Filename    string                `json:"filename,omitempty"`
	Overwrite   bool                  `json:"overwrite"`
	TotalLines  int                   `json:"totalLines"`
	Imported    int                   `json:"imported"`
	Skipped     int                   `json:"skipped"`
	ErrorCount  int                   `json:"errorCount"`
	ErrorSample []ActivityImportError `json:"errorSample,omitempty"`
}

ActivityImport summarises a redirect import as a single event, whatever the size of the imported file.

type ActivityImportError added in v0.2.0

type ActivityImportError struct {
	Line   int    `json:"line"`
	Source string `json:"source,omitempty"`
	Reason string `json:"reason"`
}

type ActivityInput added in v0.2.0

type ActivityInput struct {
	NamespaceCode string
	ProjectCode   string
	Resource      ActivityResource
	Action        ActivityAction
	ResourceID    *int64
	// Data is the payload, marshalled to JSON when recorded. Its type must match
	// the (Resource, Action) pair, see the payload types below.
	Data any
}

ActivityInput describes an event to record. The actor and the timestamp are filled in by the activity service from the request context.

It lives in model rather than service so that the generated service mocks stay free of any import back into service, which would make the service tests cycle.

type ActivityPublish added in v0.2.0

type ActivityPublish struct {
	Version   int                   `json:"version"`
	Redirects ActivityPublishCounts `json:"redirects"`
	Pages     ActivityPublishCounts `json:"pages"`
}

ActivityPublish summarises a publish: the version it produced and what it moved.

type ActivityPublishCounts added in v0.2.0

type ActivityPublishCounts struct {
	Created int `json:"created"`
	Updated int `json:"updated"`
	Deleted int `json:"deleted"`
}

type ActivityResource added in v0.2.0

type ActivityResource string

ActivityResource is what an activity event is about.

type ActivityRollback added in v0.2.0

type ActivityRollback[T any] struct {
	Scope      ActivityRollbackScope `json:"scope"`
	ChangeType *DraftChangeType      `json:"changeType,omitempty"`
	Entry      *T                    `json:"entry,omitempty"`
	Discarded  *ActivityDraftCounts  `json:"discarded,omitempty"`
}

ActivityRollback describes discarded pending changes. SINGLE fills ChangeType and Entry, PROJECT fills Discarded.

type ActivityRollbackScope added in v0.2.0

type ActivityRollbackScope string

ActivityRollbackScope tells a rollback of the whole project apart from the cancellation of one pending change.

const (
	ActivityRollbackScopeSingle  ActivityRollbackScope = "SINGLE"
	ActivityRollbackScopeProject ActivityRollbackScope = "PROJECT"
)

type ActivityTruncate added in v0.2.0

type ActivityTruncate struct {
	Published int64 `json:"published"`
	Drafts    int64 `json:"drafts"`
	Version   int   `json:"version,omitempty"`
}

ActivityTruncate summarises wiping a resource of a project. Version is the project version the wipe produced, since removing every redirect or page has to be published for the agents to stop serving what was deleted.

type AdminPermission

type AdminPermission struct {
	ID        int64       `json:"id" gorm:"primaryKey;autoIncrement"`
	Section   SectionType `json:"section" gorm:"size:100;not null;index:idx_admin_perm_section"`
	Action    ActionType  `json:"action" gorm:"size:50;not null"`
	RoleID    int64
	Role      Role      `json:"role,omitempty"`
	CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp"`
}

func (AdminPermission) TableName

func (AdminPermission) TableName() string

type Agent

type Agent struct {
	ID            int64    `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string   `json:"-" gorm:"size:50;index:idx_agents_namespace_project"`
	ProjectCode   string   `json:"-" gorm:"size:50;index:idx_agents_namespace_project"`
	Project       *Project `json:"project" gorm:"foreignKey:NamespaceCode,ProjectCode;references:NamespaceCode,ProjectCode;"`
	commonTypes.Agent
	CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp"`
	LastHitAt time.Time `json:"lastHitAt" gorm:"type:timestamp"`
}

type AgentList

type AgentList = commonTypes.PaginatedResult[Agent]

type DraftChangeType

type DraftChangeType string
const (
	DraftChangeTypeCreate    DraftChangeType = "CREATE"
	DraftChangeTypeUpdate    DraftChangeType = "UPDATE"
	DraftChangeTypeDelete    DraftChangeType = "DELETE"
	DraftChangeTypePublished DraftChangeType = "PUBLISHED"
)

type Namespace

type Namespace struct {
	ID            int64     `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string    `json:"namespace_code" gorm:"size:50;uniqueIndex:idx_namespace_namespace_code;" validate:"required,code"`
	Name          string    `json:"name" validate:"required"`
	CreatedAt     time.Time `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt     time.Time `json:"updatedAt" gorm:"type:timestamp"`
}

type NamespaceList

type NamespaceList = types.PaginatedResult[Namespace]

type Page

type Page struct {
	ID            int64    `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string   `` /* 154-byte string literal not displayed */
	ProjectCode   string   `` /* 154-byte string literal not displayed */
	Project       *Project `json:"project" gorm:"foreignKey:NamespaceCode,ProjectCode;references:NamespaceCode,ProjectCode;"`
	// Same reason as on Redirect: the agent endpoint filters on is_published.
	IsPublished *bool     `json:"is_published" gorm:"default:false;not null;index:idx_pages_namespace_project,priority:3"`
	PublishedAt time.Time `json:"publishedAt" gorm:"type:timestamp"`
	ContentSize int64     `json:"contentSize" gorm:"default:0;not null"`
	*commonTypes.Page
	PageDraft *PageDraft `json:"draft" gorm:"foreignKey:OldPageID;references:ID"`
	CreatedAt time.Time  `json:"createdAt" gorm:"type:timestamp;index:idx_pages_ns_proj_created,priority:3,sort:desc"`
	UpdatedAt time.Time  `json:"updatedAt" gorm:"type:timestamp;index:idx_pages_ns_proj_updated,priority:3,sort:desc"`
}

type PageDraft

type PageDraft struct {
	ID            int64             `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string            `json:"-" gorm:"size:50;index:idx_page_drafts_namespace_project"`
	ProjectCode   string            `json:"-" gorm:"size:50;index:idx_page_drafts_namespace_project"`
	Project       *Project          `json:"project" gorm:"foreignKey:NamespaceCode,ProjectCode;references:NamespaceCode,ProjectCode;"`
	ChangeType    DraftChangeType   `json:"changeType" gorm:"size:50;" validate:"required"`
	OldPageID     *int64            `json:"-" gorm:"index:idx_page_drafts_old_page_id"`
	OldPage       *Page             `json:"oldPage" gorm:"foreignKey:OldPageID;"`
	ContentSize   int64             `json:"contentSize" gorm:"default:0;not null"`
	NewPage       *commonTypes.Page `gorm:"embedded;embeddedPrefix:new_"`
	CreatedAt     time.Time         `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt     time.Time         `json:"updatedAt" gorm:"type:timestamp"`
}

type PageList

type PageList = commonTypes.PaginatedResult[Page]

type PageSnapshot added in v0.2.0

type PageSnapshot struct {
	Type        commonTypes.PageType        `json:"type"`
	Path        string                      `json:"path"`
	ContentType commonTypes.PageContentType `json:"contentType"`
	ContentSize int64                       `json:"contentSize"`
}

PageSnapshot is the activity projection of a page. The page content is deliberately left out: it can reach page.size_limit (1MB by default), which has no place in a journal kept for months.

func NewPageSnapshot added in v0.2.0

func NewPageSnapshot(page *commonTypes.Page, contentSize int64) *PageSnapshot

NewPageSnapshot projects a page for the activity trail, nil in, nil out.

type Project

type Project struct {
	ID            int64      `json:"id" gorm:"primaryKey;autoIncrement"`
	ProjectCode   string     `json:"code" gorm:"size:50;uniqueIndex:idx_project_namespace" validate:"required,code"`
	NamespaceCode string     `json:"-" gorm:"size:50;uniqueIndex:idx_project_namespace;index:idx_namespace"`
	Namespace     *Namespace `json:"namespace" gorm:"foreignKey:NamespaceCode;references:NamespaceCode;"`
	Name          string     `json:"name" validate:"required"`
	Version       int        `json:"version" gorm:"default:1"`
	CreatedAt     time.Time  `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt     time.Time  `json:"UpdatedAt" gorm:"type:timestamp"`
	PublishedAt   time.Time  `json:"publishedAt" gorm:"type:timestamp"`
}

type ProjectList

type ProjectList = types.PaginatedResult[Project]

type Redirect

type Redirect struct {
	ID            int64    `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string   `` /* 166-byte string literal not displayed */
	ProjectCode   string   `` /* 166-byte string literal not displayed */
	Project       *Project `json:"project" gorm:"foreignKey:NamespaceCode,ProjectCode;references:NamespaceCode,ProjectCode;"`
	// is_published is part of idx_redirects_namespace_project rather than sitting in
	// its own index: the agent endpoint filters on (namespace, project, is_published),
	// and without it every row of the project has to be read to test the flag.
	IsPublished *bool     `json:"is_published" gorm:"default:false;not null;index:idx_redirects_namespace_project,priority:3"`
	PublishedAt time.Time `json:"publishedAt" gorm:"type:timestamp"`
	*commonTypes.Redirect
	RedirectDraft *RedirectDraft `json:"draft" gorm:"foreignKey:OldRedirectID;references:ID"`
	CreatedAt     time.Time      `json:"createdAt" gorm:"type:timestamp;index:idx_redirects_ns_proj_created,priority:3,sort:desc"`
	UpdatedAt     time.Time      `json:"updatedAt" gorm:"type:timestamp;index:idx_redirects_ns_proj_updated,priority:3,sort:desc"`
}

type RedirectDraft

type RedirectDraft struct {
	ID            int64                 `json:"id" gorm:"primaryKey;autoIncrement"`
	NamespaceCode string                `json:"-" gorm:"size:50;index:idx_redirect_drafts_namespace_project"`
	ProjectCode   string                `json:"-" gorm:"size:50;index:idx_redirect_drafts_namespace_project"`
	Project       *Project              `json:"project" gorm:"foreignKey:NamespaceCode,ProjectCode;references:NamespaceCode,ProjectCode;"`
	ChangeType    DraftChangeType       `json:"changeType" gorm:"size:50;" validate:"required"`
	OldRedirectID *int64                `json:"-" gorm:"index:idx_redirect_drafts_old_redirect_id"`
	OldRedirect   *Redirect             `json:"oldRedirect" gorm:"foreignKey:OldRedirectID;"`
	NewRedirect   *commonTypes.Redirect `gorm:"embedded;embeddedPrefix:new_"`
	CreatedAt     time.Time             `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt     time.Time             `json:"updatedAt" gorm:"type:timestamp"`
}

type RedirectList

type RedirectList = commonTypes.PaginatedResult[Redirect]

type RedirectSnapshot added in v0.2.0

type RedirectSnapshot struct {
	Type   commonTypes.RedirectType   `json:"type"`
	Source string                     `json:"source"`
	Target string                     `json:"target"`
	Status commonTypes.RedirectStatus `json:"status"`
}

RedirectSnapshot is the activity projection of a redirect.

func NewRedirectSnapshot added in v0.2.0

func NewRedirectSnapshot(redirect *commonTypes.Redirect) *RedirectSnapshot

NewRedirectSnapshot projects a redirect for the activity trail, nil in, nil out.

type ResourcePermission

type ResourcePermission struct {
	ID        int64        `json:"id" gorm:"primaryKey;autoIncrement"`
	Namespace string       `json:"namespace" gorm:"size:50;not null;index:idx_res_perm_namespace"`
	Project   string       `json:"project" gorm:"size:50;index:idx_res_perm_project"`
	Resource  ResourceType `json:"resource" gorm:"size:50;not null"`
	Action    ActionType   `json:"action" gorm:"size:50;not null"`
	RoleID    int64
	Role      Role      `json:"role,omitempty"`
	CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp"`
}

func (ResourcePermission) TableName

func (ResourcePermission) TableName() string

type ResourceType

type ResourceType string

type Role

type Role struct {
	ID        int64     `json:"id" gorm:"primaryKey;autoIncrement"`
	Code      string    `json:"code" gorm:"uniqueIndex:idx_role_code_type;size:100;not null" validate:"required,code"`
	Type      RoleType  `json:"type" gorm:"uniqueIndex:idx_role_code_type;size:100;not null"`
	CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp"`

	Users []User `json:"users,omitempty" gorm:"many2many:user_roles;"`

	Resources []ResourcePermission `json:"resources,omitempty" gorm:"foreignKey:RoleID;constraint:OnDelete:CASCADE;"`
	Admin     []AdminPermission    `json:"admin,omitempty" gorm:"foreignKey:RoleID;constraint:OnDelete:CASCADE;"`
}

type RoleList

type RoleList = types.PaginatedResult[Role]

type RoleType

type RoleType string
const (
	RoleTypeUser  RoleType = "user"
	RoleTypeRole  RoleType = "role"
	RoleTypeToken RoleType = "token"
)

type SectionType

type SectionType string

type SubjectPermissions

type SubjectPermissions struct {
	Resources []ResourcePermission `json:"resources,omitempty"`
	Admin     []AdminPermission    `json:"admin,omitempty"`
}

func (*SubjectPermissions) Append

func (s *SubjectPermissions) Append(permission *SubjectPermissions)

type Token

type Token struct {
	ID           int64      `json:"id" gorm:"primaryKey;autoIncrement"`
	Name         string     `json:"name" gorm:"uniqueIndex;size:300;not null" validate:"required,max=300"`
	TokenHash    string     `json:"-" gorm:"uniqueIndex;size:64;not null"`
	TokenPreview string     `json:"tokenPreview" gorm:"size:30;not null"` // e.g., "flecto_abcd...wxyz"
	ExpiresAt    *time.Time `json:"expiresAt" gorm:"type:timestamp"`
	CreatedAt    time.Time  `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt    time.Time  `json:"updatedAt" gorm:"type:timestamp"`
}

func (*Token) GetRoleCode

func (t *Token) GetRoleCode() string

GetRoleCode returns the role code for this token's personal role

func (*Token) IsExpired

func (t *Token) IsExpired() bool

IsExpired checks if the token has expired

type TokenList

type TokenList = types.PaginatedResult[Token]

type User

type User struct {
	ID               int64     `json:"id" gorm:"primaryKey;autoIncrement"`
	Username         string    `json:"username" gorm:"unique;size:100;not null" validate:"required,username"`
	Password         string    `json:"-" gorm:"size:255"`
	Lastname         string    `json:"lastname"  validate:"required"`
	Firstname        string    `json:"firstname"  validate:"required"`
	Active           *bool     `json:"active" gorm:"default:true;not null"`
	RefreshTokenHash string    `json:"-" gorm:"size:255"`
	CreatedAt        time.Time `json:"createdAt" gorm:"type:timestamp"`
	UpdatedAt        time.Time `json:"updatedAt" gorm:"type:timestamp"`
}

func (*User) HasPassword

func (u *User) HasPassword() bool

HasPassword returns true if the user can use basic auth

func (*User) IsActive

func (u *User) IsActive() bool

type UserList

type UserList = types.PaginatedResult[User]

type UserRole

type UserRole struct {
	UserID    int64     `json:"userId" gorm:"primaryKey"`
	RoleID    int64     `json:"roleId" gorm:"primaryKey"`
	CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp"`

	User User `json:"user" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE;"`
	Role Role `json:"role" gorm:"foreignKey:RoleID;constraint:OnDelete:CASCADE;"`
}

func (UserRole) TableName

func (UserRole) TableName() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL