e8beb7d4f3
- Navigation: new per-user setting (default "side") renders the page links as a left sidebar while keeping theme/bell/avatar/logout in the top-right; falls back to a top bar under 760px or when set to "top". Profile selector added. - Group conversations: record a creatorId; the creator gets a "Manage members" button to add/remove participants (new GET/POST/DELETE members endpoints, creator-only). Existing groups backfilled. - Customer ticketing: admin can map each assigned consultant to their username in that customer's ticketing system. Per-customer mapping takes precedence over the consultant's global ticketingIdentities during sync. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
85 lines
2.8 KiB
Go
85 lines
2.8 KiB
Go
// Package domain holds the core data model types and business rules shared
|
|
// across handlers, stores, and workers. It has no Mongo or HTTP dependencies
|
|
// beyond bson struct tags.
|
|
package domain
|
|
|
|
import "time"
|
|
|
|
type Roles struct {
|
|
Admin bool `bson:"admin" json:"admin"`
|
|
Consultant bool `bson:"consultant" json:"consultant"`
|
|
Developer bool `bson:"developer" json:"developer"`
|
|
}
|
|
|
|
// Has reports whether the role named r is set. Known names: admin,
|
|
// consultant, developer.
|
|
func (r Roles) Has(role string) bool {
|
|
switch role {
|
|
case "admin":
|
|
return r.Admin
|
|
case "consultant":
|
|
return r.Consultant
|
|
case "developer":
|
|
return r.Developer
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
type LocalAuth struct {
|
|
PasswordHash string `bson:"passwordHash" json:"-"`
|
|
MustChange bool `bson:"mustChange" json:"mustChange"`
|
|
}
|
|
|
|
type OIDCAuth struct {
|
|
Issuer string `bson:"issuer" json:"issuer"`
|
|
Subject string `bson:"subject" json:"subject"`
|
|
}
|
|
|
|
type Auth struct {
|
|
Local *LocalAuth `bson:"local" json:"local,omitempty"`
|
|
OIDC *OIDCAuth `bson:"oidc" json:"oidc,omitempty"`
|
|
}
|
|
|
|
type Contact struct {
|
|
Phone string `bson:"phone" json:"phone"`
|
|
Location string `bson:"location" json:"location"`
|
|
Links []string `bson:"links" json:"links"`
|
|
}
|
|
|
|
type NotificationPrefs struct {
|
|
Email bool `bson:"email" json:"email"`
|
|
InApp bool `bson:"inApp" json:"inApp"`
|
|
}
|
|
|
|
type UserSettings struct {
|
|
Theme string `bson:"theme" json:"theme"`
|
|
NavLayout string `bson:"navLayout" json:"navLayout"` // "side" (default) | "top"
|
|
Notifications NotificationPrefs `bson:"notifications" json:"notifications"`
|
|
LeaderboardOptOut bool `bson:"leaderboardOptOut" json:"leaderboardOptOut"`
|
|
}
|
|
|
|
type User struct {
|
|
ID string `bson:"_id" json:"id"`
|
|
Email string `bson:"email" json:"email"`
|
|
Name string `bson:"name" json:"name"`
|
|
AvatarFileID string `bson:"avatarFileId,omitempty" json:"avatarFileId,omitempty"`
|
|
Bio string `bson:"bio" json:"bio"`
|
|
Contact Contact `bson:"contact" json:"contact"`
|
|
Extra map[string]any `bson:"extra" json:"extra"`
|
|
Roles Roles `bson:"roles" json:"roles"`
|
|
Auth Auth `bson:"auth" json:"-"`
|
|
Settings UserSettings `bson:"settings" json:"settings"`
|
|
Disabled bool `bson:"disabled" json:"disabled"`
|
|
LastSeenAt time.Time `bson:"lastSeenAt" json:"lastSeenAt"`
|
|
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
|
|
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
|
|
Version int64 `bson:"version" json:"version"`
|
|
}
|
|
|
|
// MustChangePassword reports whether the user is locked to the
|
|
// password-change flow.
|
|
func (u *User) MustChangePassword() bool {
|
|
return u.Auth.Local != nil && u.Auth.Local.MustChange
|
|
}
|