feat: complete registration invitation and auth redesign

This commit is contained in:
richjohnrambo
2026-08-19 00:30:11 +08:00
parent e063e7879e
commit 219ecadf4c
47 changed files with 9014 additions and 224 deletions
+39 -24
View File
@@ -354,17 +354,17 @@ func (m *MockUsersPersistenceInterface) EXPECT() *MockUsersPersistenceInterfaceM
}
// AddAuthRecord mocks base method.
func (m *MockUsersPersistenceInterface) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error {
func (m *MockUsersPersistenceInterface) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte, expires time.Time) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddAuthRecord", tenantID, uid, authLvl, scheme, unique, secret, expires)
ret := m.ctrl.Call(m, "AddAuthRecord", tenantID, uid, authLvl, secret, expires)
ret0, _ := ret[0].(error)
return ret0
}
// AddAuthRecord indicates an expected call of AddAuthRecord.
func (mr *MockUsersPersistenceInterfaceMockRecorder) AddAuthRecord(tenantID, uid, authLvl, scheme, unique, secret, expires interface{}) *gomock.Call {
func (mr *MockUsersPersistenceInterfaceMockRecorder) AddAuthRecord(tenantID, uid, authLvl, secret, expires interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).AddAuthRecord), tenantID, uid, authLvl, scheme, unique, secret, expires)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).AddAuthRecord), tenantID, uid, authLvl, secret, expires)
}
// ConfirmCred mocks base method.
@@ -396,18 +396,18 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) Create(tenantID, user, priv
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).Create), tenantID, user, private)
}
// DelAuthRecords mocks base method.
func (m *MockUsersPersistenceInterface) DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error {
// DeleteAuthRecord mocks base method.
func (m *MockUsersPersistenceInterface) DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DelAuthRecords", tenantID, uid, scheme)
ret := m.ctrl.Call(m, "DeleteAuthRecord", tenantID, uid)
ret0, _ := ret[0].(error)
return ret0
}
// DelAuthRecords indicates an expected call of DelAuthRecords.
func (mr *MockUsersPersistenceInterfaceMockRecorder) DelAuthRecords(tenantID, uid, scheme interface{}) *gomock.Call {
// DeleteAuthRecord indicates an expected call of DeleteAuthRecord.
func (mr *MockUsersPersistenceInterfaceMockRecorder) DeleteAuthRecord(tenantID, uid interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelAuthRecords", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).DelAuthRecords), tenantID, uid, scheme)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).DeleteAuthRecord), tenantID, uid)
}
// DelCred mocks base method.
@@ -532,6 +532,21 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAll(tenantID interface{}
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAll), varargs...)
}
// GetByPhone mocks base method.
func (m *MockUsersPersistenceInterface) GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetByPhone", tenantID, phone)
ret0, _ := ret[0].(types.Uid)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetByPhone indicates an expected call of GetByPhone.
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetByPhone(tenantID, phone interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByPhone", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetByPhone), tenantID, phone)
}
// GetAllCreds mocks base method.
func (m *MockUsersPersistenceInterface) GetAllCreds(tenantID types.TenantID, id types.Uid, method string, validatedOnly bool) ([]types.Credential, error) {
m.ctrl.T.Helper()
@@ -548,9 +563,9 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAllCreds(tenantID, id, m
}
// GetAuthRecord mocks base method.
func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error) {
func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAuthRecord", tenantID, user, scheme)
ret := m.ctrl.Call(m, "GetAuthRecord", tenantID, user)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(auth.Level)
ret2, _ := ret[2].([]byte)
@@ -560,15 +575,15 @@ func (m *MockUsersPersistenceInterface) GetAuthRecord(tenantID types.TenantID, u
}
// GetAuthRecord indicates an expected call of GetAuthRecord.
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthRecord(tenantID, user, scheme interface{}) *gomock.Call {
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthRecord(tenantID, user interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthRecord), tenantID, user, scheme)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthRecord), tenantID, user)
}
// GetAuthUniqueRecord mocks base method.
func (m *MockUsersPersistenceInterface) GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error) {
// GetAuthByLogin mocks base method.
func (m *MockUsersPersistenceInterface) GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAuthUniqueRecord", tenantID, scheme, unique)
ret := m.ctrl.Call(m, "GetAuthByLogin", tenantID, login)
ret0, _ := ret[0].(types.Uid)
ret1, _ := ret[1].(auth.Level)
ret2, _ := ret[2].([]byte)
@@ -577,10 +592,10 @@ func (m *MockUsersPersistenceInterface) GetAuthUniqueRecord(tenantID types.Tenan
return ret0, ret1, ret2, ret3, ret4
}
// GetAuthUniqueRecord indicates an expected call of GetAuthUniqueRecord.
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthUniqueRecord(tenantID, scheme, unique interface{}) *gomock.Call {
// GetAuthByLogin indicates an expected call of GetAuthByLogin.
func (mr *MockUsersPersistenceInterfaceMockRecorder) GetAuthByLogin(tenantID, login interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthUniqueRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthUniqueRecord), tenantID, scheme, unique)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAuthByLogin", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).GetAuthByLogin), tenantID, login)
}
// GetByCred mocks base method.
@@ -723,17 +738,17 @@ func (mr *MockUsersPersistenceInterfaceMockRecorder) Update(tenantID, uid, updat
}
// UpdateAuthRecord mocks base method.
func (m *MockUsersPersistenceInterface) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error {
func (m *MockUsersPersistenceInterface) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string, secret []byte, expires time.Time) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateAuthRecord", tenantID, uid, authLvl, scheme, unique, secret, expires)
ret := m.ctrl.Call(m, "UpdateAuthRecord", tenantID, uid, authLvl, username, secret, expires)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateAuthRecord indicates an expected call of UpdateAuthRecord.
func (mr *MockUsersPersistenceInterfaceMockRecorder) UpdateAuthRecord(tenantID, uid, authLvl, scheme, unique, secret, expires interface{}) *gomock.Call {
func (mr *MockUsersPersistenceInterfaceMockRecorder) UpdateAuthRecord(tenantID, uid, authLvl, username, secret, expires interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).UpdateAuthRecord), tenantID, uid, authLvl, scheme, unique, secret, expires)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAuthRecord", reflect.TypeOf((*MockUsersPersistenceInterface)(nil).UpdateAuthRecord), tenantID, uid, authLvl, username, secret, expires)
}
// UpdateLastSeen mocks base method.
+172 -28
View File
@@ -137,6 +137,24 @@ type tenantsMapper struct{}
// Tenants is the tenant registry access object.
var Tenants TenantsPersistenceInterface
// RegistrationConfigPersistenceInterface exposes global registration policy.
type RegistrationConfigPersistenceInterface interface {
SMSEnabled() (bool, error)
}
type registrationConfigMapper struct{}
// RegistrationConfig reads registration policy from the control database.
var RegistrationConfig RegistrationConfigPersistenceInterface
func (registrationConfigMapper) SMSEnabled() (bool, error) {
configAdp, ok := adp.(adapter.RegistrationConfigAdapter)
if !ok {
return true, types.ErrUnsupported
}
return configAdp.RegistrationSMSEnabled()
}
// GetByCode resolves a tenant by its public enterprise code.
func (tenantsMapper) GetByCode(code string) (*types.Tenant, error) {
tenantAdp, ok := adp.(adapter.TenantAdapter)
@@ -368,13 +386,14 @@ func (s storeObj) DbStats() func() any {
// UsersPersistenceInterface is an interface which defines methods for persistent storage of user records.
type UsersPersistenceInterface interface {
Create(tenantID types.TenantID, user *types.User, private any) (*types.User, error)
GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error)
GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error)
AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error
UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte, expires time.Time) error
DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error
GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error)
GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error)
AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte, expires time.Time) error
UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string, secret []byte, expires time.Time) error
DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error
Get(tenantID types.TenantID, uid types.Uid) (*types.User, error)
GetAll(tenantID types.TenantID, uid ...types.Uid) ([]types.User, error)
GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error)
GetByCred(tenantID types.TenantID, method, value string) (types.Uid, error)
Delete(tenantID types.TenantID, id types.Uid, hard bool) error
UpdateLastSeen(tenantID types.TenantID, uid types.Uid, userAgent string, when time.Time) error
@@ -455,61 +474,50 @@ func (usersMapper) Create(tenantID types.TenantID, user *types.User, private any
// GetAuthRecord takes a user ID and a authentication scheme name, fetches unique scheme-dependent identifier and
// authentication secret.
func (usersMapper) GetAuthRecord(tenantID types.TenantID, user types.Uid, scheme string) (string, auth.Level, []byte, time.Time, error) {
func (usersMapper) GetAuthRecord(tenantID types.TenantID, user types.Uid) (string, auth.Level, []byte, time.Time, error) {
tenantAdp, err := tenantAuthAdapter(tenantID)
if err != nil {
return "", auth.LevelNone, nil, time.Time{}, err
}
unique, authLvl, secret, expires, err := tenantAdp.TenantAuthGetRecord(tenantID, user, scheme)
if err == nil {
parts := strings.Split(unique, ":")
if len(parts) > 1 {
unique = parts[1]
} else {
err = types.ErrInternal
}
}
return unique, authLvl, secret, expires, err
return tenantAdp.TenantAuthGetRecord(tenantID, user)
}
// GetAuthUniqueRecord takes a unique identifier and a authentication scheme name, fetches user ID and
// authentication secret.
func (usersMapper) GetAuthUniqueRecord(tenantID types.TenantID, scheme, unique string) (types.Uid, auth.Level, []byte, time.Time, error) {
// GetAuthByLogin fetches a Basic authentication record by username or phone.
func (usersMapper) GetAuthByLogin(tenantID types.TenantID, login string) (types.Uid, auth.Level, []byte, time.Time, error) {
tenantAdp, err := tenantAuthAdapter(tenantID)
if err != nil {
return types.ZeroUid, auth.LevelNone, nil, time.Time{}, err
}
return tenantAdp.TenantAuthGetUniqueRecord(tenantID, scheme+":"+unique)
return tenantAdp.TenantAuthGetByLogin(tenantID, login)
}
// AddAuthRecord creates a new authentication record for the given user.
func (usersMapper) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string, secret []byte,
func (usersMapper) AddAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, secret []byte,
expires time.Time) error {
tenantAdp, err := tenantAuthAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantAuthAddRecord(tenantID, uid, scheme, scheme+":"+unique, authLvl, secret, expires)
return tenantAdp.TenantAuthAddRecord(tenantID, uid, authLvl, secret, expires)
}
// UpdateAuthRecord updates authentication record with a new secret and expiration time.
func (usersMapper) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, scheme, unique string,
func (usersMapper) UpdateAuthRecord(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, username string,
secret []byte, expires time.Time) error {
tenantAdp, err := tenantAuthAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantAuthUpdRecord(tenantID, uid, scheme, scheme+":"+unique, authLvl, secret, expires)
return tenantAdp.TenantAuthUpdateRecord(tenantID, uid, username, authLvl, secret, expires)
}
// DelAuthRecords deletes user's auth records of the given scheme.
func (usersMapper) DelAuthRecords(tenantID types.TenantID, uid types.Uid, scheme string) error {
// DeleteAuthRecord deletes the user's Basic authentication record.
func (usersMapper) DeleteAuthRecord(tenantID types.TenantID, uid types.Uid) error {
tenantAdp, err := tenantAuthAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantAuthDelScheme(tenantID, uid, scheme)
return tenantAdp.TenantAuthDeleteRecord(tenantID, uid)
}
// Get returns a user object for the given user ID or nil if the user is not found.
@@ -552,6 +560,16 @@ func (usersMapper) GetByCred(tenantID types.TenantID, method, value string) (typ
return tenantAdp.TenantUserGetByCred(tenantID, method, value)
}
// GetByPhone resolves an account by the phone identity stored on im_users.
// It intentionally does not require a completed tel credential.
func (usersMapper) GetByPhone(tenantID types.TenantID, phone string) (types.Uid, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return types.ZeroUid, err
}
return tenantAdp.TenantUserGetByPhone(tenantID, phone)
}
// Delete deletes user records.
func (usersMapper) Delete(tenantID types.TenantID, id types.Uid, hard bool) error {
tenantAdp, err := tenantBusinessAdapter(tenantID)
@@ -3062,6 +3080,25 @@ type verificationMapper struct{}
// Verifications is a singleton for product verification challenges.
var Verifications VerificationPersistenceInterface
// RegistrationInvitePersistenceInterface manages tenant-scoped registration invitations.
type RegistrationInvitePersistenceInterface interface {
Create(tenantID types.TenantID, code *types.RegistrationInviteCode) error
CreateBatch(tenantID types.TenantID, batch *types.RegistrationInviteBatch, codes []types.RegistrationInviteCode) error
GetByCode(tenantID types.TenantID, code string) (*types.RegistrationInviteCode, error)
List(tenantID types.TenantID, opts types.RegistrationInviteListOptions) (*types.RegistrationInvitePage, error)
GetBatch(tenantID types.TenantID, batchID int64) (*types.RegistrationInviteBatch, error)
GetBatchByIdempotency(tenantID types.TenantID, key string) (*types.RegistrationInviteBatch, error)
Revoke(tenantID types.TenantID, codeID int64, now time.Time) error
RevokeBatch(tenantID types.TenantID, batchID int64, now time.Time) (int, error)
RecordUse(tenantID types.TenantID, code string, uid types.Uid, requestID string, now time.Time) error
SyncUsage(tenantID types.TenantID, codeID int64) error
}
type registrationInviteMapper struct{}
// RegistrationInvites is the tenant-scoped registration invitation store.
var RegistrationInvites RegistrationInvitePersistenceInterface
// UserSecurityPersistenceInterface defines user security state used to
// invalidate previously issued login tokens.
type UserSecurityPersistenceInterface interface {
@@ -3267,6 +3304,111 @@ func (verificationMapper) Consume(tenantID types.TenantID, challengeID, tokenHas
return tenantAdp.TenantVerificationConsume(tenantID, challengeID, tokenHash, consumedAt)
}
func tenantRegistrationInviteAdapter(tenantID types.TenantID) (adapter.TenantRegistrationInviteAdapter, error) {
if !tenantID.IsValid() {
return nil, types.ErrMalformed
}
if router, ok := adp.(adapter.TenantRoutedAdapter); ok {
business, _, err := router.TenantAdapterFor(tenantID)
if err != nil {
return nil, err
}
if inviteAdp, ok := business.(adapter.TenantRegistrationInviteAdapter); ok {
return inviteAdp, nil
}
}
if inviteAdp, ok := adp.(adapter.TenantRegistrationInviteAdapter); ok {
return inviteAdp, nil
}
return nil, types.ErrUnsupported
}
func (registrationInviteMapper) Create(tenantID types.TenantID, code *types.RegistrationInviteCode) error {
if code == nil {
return types.ErrMalformed
}
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return err
}
return inviteAdp.TenantRegistrationInviteCreate(tenantID, code)
}
func (registrationInviteMapper) CreateBatch(tenantID types.TenantID, batch *types.RegistrationInviteBatch, codes []types.RegistrationInviteCode) error {
if batch == nil || len(codes) == 0 {
return types.ErrMalformed
}
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return err
}
return inviteAdp.TenantRegistrationInviteCreateBatch(tenantID, batch, codes)
}
func (registrationInviteMapper) GetByCode(tenantID types.TenantID, code string) (*types.RegistrationInviteCode, error) {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return nil, err
}
return inviteAdp.TenantRegistrationInviteGetByCode(tenantID, code)
}
func (registrationInviteMapper) List(tenantID types.TenantID, opts types.RegistrationInviteListOptions) (*types.RegistrationInvitePage, error) {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return nil, err
}
return inviteAdp.TenantRegistrationInviteList(tenantID, opts)
}
func (registrationInviteMapper) GetBatch(tenantID types.TenantID, batchID int64) (*types.RegistrationInviteBatch, error) {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return nil, err
}
return inviteAdp.TenantRegistrationInviteBatchGet(tenantID, batchID)
}
func (registrationInviteMapper) GetBatchByIdempotency(tenantID types.TenantID, key string) (*types.RegistrationInviteBatch, error) {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return nil, err
}
return inviteAdp.TenantRegistrationInviteBatchGetByIdempotency(tenantID, key)
}
func (registrationInviteMapper) Revoke(tenantID types.TenantID, codeID int64, now time.Time) error {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return err
}
return inviteAdp.TenantRegistrationInviteRevoke(tenantID, codeID, now)
}
func (registrationInviteMapper) RevokeBatch(tenantID types.TenantID, batchID int64, now time.Time) (int, error) {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return 0, err
}
return inviteAdp.TenantRegistrationInviteBatchRevoke(tenantID, batchID, now)
}
func (registrationInviteMapper) RecordUse(tenantID types.TenantID, code string, uid types.Uid, requestID string, now time.Time) error {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return err
}
return inviteAdp.TenantRegistrationInviteRecordUse(tenantID, code, uid, requestID, now)
}
func (registrationInviteMapper) SyncUsage(tenantID types.TenantID, codeID int64) error {
inviteAdp, err := tenantRegistrationInviteAdapter(tenantID)
if err != nil {
return err
}
return inviteAdp.TenantRegistrationInviteSyncUsage(tenantID, codeID)
}
// Get reads a persistent cache entry.
func (pcacheMapper) Get(tenantID types.TenantID, key string) (string, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
@@ -3310,6 +3452,7 @@ func SetTestUidGenerator(g types.UidGenerator) {
func init() {
Store = storeObj{}
Tenants = tenantsMapper{}
RegistrationConfig = registrationConfigMapper{}
Users = usersMapper{}
Topics = topicsMapper{}
Subs = subsMapper{}
@@ -3330,6 +3473,7 @@ func init() {
Files = fileMapper{}
PCache = pcacheMapper{}
Verifications = verificationMapper{}
RegistrationInvites = registrationInviteMapper{}
Security = userSecurityMapper{}
Relations = relationsMapper{}
Favorites = favoritesMapper{}
+70
View File
@@ -0,0 +1,70 @@
package types
import (
"errors"
"time"
)
var (
ErrInviteInvalid = errors.New("registration invite invalid")
ErrInviteExpired = errors.New("registration invite expired")
ErrInviteRevoked = errors.New("registration invite revoked")
ErrInviteIdempotencyConflict = errors.New("registration invite idempotency conflict")
)
type RegistrationInviteStatus string
const (
RegistrationInviteActive RegistrationInviteStatus = "active"
RegistrationInviteExpired RegistrationInviteStatus = "expired"
RegistrationInviteRevoked RegistrationInviteStatus = "revoked"
)
type RegistrationInviteBatch struct {
ID int64 `db:"id" json:"batch_id"`
TenantID TenantID `db:"tenant_id" json:"tenant_id"`
Quantity int `db:"quantity" json:"quantity"`
IdempotencyKey string `db:"idempotency_key" json:"-"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedBy int64 `db:"created_by" json:"created_by"`
Remark string `db:"remark" json:"remark,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
ActiveCount int `db:"active_count" json:"active_count"`
UsageCount int `db:"usage_count" json:"usage_count"`
ExpiredCount int `db:"expired_count" json:"expired_count"`
RevokedCount int `db:"revoked_count" json:"revoked_count"`
}
type RegistrationInviteCode struct {
ID int64 `db:"id" json:"id"`
TenantID TenantID `db:"tenant_id" json:"tenant_id"`
BatchID *int64 `db:"batch_id" json:"batch_id,omitempty"`
Code string `db:"code" json:"code"`
Status RegistrationInviteStatus `db:"status" json:"status"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
UsageCount int `db:"usage_count" json:"usage_count"`
LastUsedAt *time.Time `db:"last_used_at" json:"last_used_at,omitempty"`
CreatedBy int64 `db:"created_by" json:"created_by"`
Remark string `db:"remark" json:"remark,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
type RegistrationInviteListOptions struct {
Code string
Status RegistrationInviteStatus
BatchID int64
CreatedBy int64
CreatedAtStart *time.Time
CreatedAtEnd *time.Time
ExpiresAtStart *time.Time
ExpiresAtEnd *time.Time
Offset int
Limit int
}
type RegistrationInvitePage struct {
Items []RegistrationInviteCode
Total int
}
+2
View File
@@ -521,6 +521,8 @@ type User struct {
UserAgent string
// Canonical mobile number used for registration and account recovery.
Phone string
// Primary login account. Ordinary accounts are lowercase ASCII names; phone accounts use E.164.
Username string
Public any
Trusted any