fix
This commit is contained in:
@@ -59,6 +59,8 @@ type LoginResult struct {
|
||||
User User `json:"user"`
|
||||
CompanyID uuid.UUID `json:"company_id"`
|
||||
Companies []Company `json:"companies"`
|
||||
// ProvisionCompanyIDs are newly created tenants that still need Free-plan billing setup.
|
||||
ProvisionCompanyIDs []uuid.UUID `json:"-"`
|
||||
}
|
||||
|
||||
func (s *Service) Register(ctx context.Context, in RegisterInput) (LoginResult, error) {
|
||||
@@ -100,31 +102,12 @@ func (s *Service) Register(ctx context.Context, in RegisterInput) (LoginResult,
|
||||
return LoginResult{}, err
|
||||
}
|
||||
|
||||
var companyID uuid.UUID
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO companies (name, owner_user_id) VALUES ($1, $2) RETURNING id`,
|
||||
strings.TrimSpace(in.CompanyName), userID).Scan(&companyID)
|
||||
companyID, created, err := ensureOwnedCompanyTx(ctx, tx, userID, strings.TrimSpace(in.CompanyName))
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO memberships (company_id, user_id, role, status)
|
||||
VALUES ($1, $2, 'admin', 'active')`, companyID, userID)
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO company_settings (company_id) VALUES ($1)
|
||||
ON CONFLICT DO NOTHING`, companyID)
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO credit_balances (company_id) VALUES ($1)
|
||||
ON CONFLICT DO NOTHING`, companyID)
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
if !created {
|
||||
return LoginResult{}, errors.New("owned company unexpectedly already existed during register")
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
@@ -136,9 +119,10 @@ func (s *Service) Register(ctx context.Context, in RegisterInput) (LoginResult,
|
||||
return LoginResult{}, err
|
||||
}
|
||||
return LoginResult{
|
||||
User: user,
|
||||
CompanyID: companyID,
|
||||
Companies: []Company{{ID: companyID, Name: strings.TrimSpace(in.CompanyName)}},
|
||||
User: user,
|
||||
CompanyID: companyID,
|
||||
Companies: []Company{{ID: companyID, Name: strings.TrimSpace(in.CompanyName)}},
|
||||
ProvisionCompanyIDs: []uuid.UUID{companyID},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -228,20 +212,20 @@ func (s *Service) AcceptInvite(ctx context.Context, token, password, name string
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
var userID uuid.UUID
|
||||
var existingHash string
|
||||
var existingHash *string
|
||||
var mustSet bool
|
||||
err = tx.QueryRow(ctx, `
|
||||
SELECT id, password_hash, must_set_password FROM users WHERE email = $1`,
|
||||
strings.ToLower(email)).Scan(&userID, &existingHash, &mustSet)
|
||||
displayName := strings.TrimSpace(name)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
hash, herr := HashPassword(password)
|
||||
if herr != nil {
|
||||
return LoginResult{}, herr
|
||||
}
|
||||
var n *string
|
||||
if strings.TrimSpace(name) != "" {
|
||||
nn := strings.TrimSpace(name)
|
||||
n = &nn
|
||||
if displayName != "" {
|
||||
n = &displayName
|
||||
}
|
||||
err = tx.QueryRow(ctx, `
|
||||
INSERT INTO users (email, name, password_hash, must_set_password)
|
||||
@@ -251,26 +235,45 @@ func (s *Service) AcceptInvite(ctx context.Context, token, password, name string
|
||||
}
|
||||
} else if err != nil {
|
||||
return LoginResult{}, err
|
||||
} else if mustSet {
|
||||
// Migration / first-password invites may set a password once.
|
||||
hash, herr := HashPassword(password)
|
||||
if herr != nil {
|
||||
return LoginResult{}, herr
|
||||
}
|
||||
_, err = tx.Exec(ctx, `
|
||||
UPDATE users SET password_hash = $2, must_set_password = false, updated_at = now()
|
||||
WHERE id = $1 AND must_set_password = true`, userID, hash)
|
||||
if err != nil {
|
||||
} else {
|
||||
var activeMemberships int64
|
||||
if err := tx.QueryRow(ctx, `
|
||||
SELECT count(*) FROM memberships
|
||||
WHERE user_id = $1 AND status = 'active'`, userID).Scan(&activeMemberships); err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
} else {
|
||||
// Existing accounts keep their password; invitee must prove ownership.
|
||||
ok, verr := VerifyPassword(existingHash, password)
|
||||
if verr != nil || !ok {
|
||||
return LoginResult{}, ErrInvalidCredentials
|
||||
// New password when must_set, or orphan recovery (removed from all companies; no SMTP reset).
|
||||
if mustSet || activeMemberships == 0 {
|
||||
hash, herr := HashPassword(password)
|
||||
if herr != nil {
|
||||
return LoginResult{}, herr
|
||||
}
|
||||
_, err = tx.Exec(ctx, `
|
||||
UPDATE users SET password_hash = $2, must_set_password = false, updated_at = now()
|
||||
WHERE id = $1`, userID, hash)
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
} else {
|
||||
// Active elsewhere: keep password; invitee must prove ownership.
|
||||
hash := ""
|
||||
if existingHash != nil {
|
||||
hash = *existingHash
|
||||
}
|
||||
ok, verr := VerifyPassword(hash, password)
|
||||
if verr != nil || !ok {
|
||||
return LoginResult{}, ErrInvalidCredentials
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always ensure a personal owned workspace; invite also joins the inviting company.
|
||||
ownedName := defaultOwnedCompanyName(email, displayName)
|
||||
ownedID, ownedCreated, err := ensureOwnedCompanyTx(ctx, tx, userID, ownedName)
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
|
||||
_, err = tx.Exec(ctx, `
|
||||
INSERT INTO memberships (company_id, user_id, role, status)
|
||||
VALUES ($1, $2, $3, 'active')
|
||||
@@ -305,7 +308,11 @@ func (s *Service) AcceptInvite(ctx context.Context, token, password, name string
|
||||
if err != nil {
|
||||
return LoginResult{}, err
|
||||
}
|
||||
return LoginResult{User: user, CompanyID: companyID, Companies: companies}, nil
|
||||
res := LoginResult{User: user, CompanyID: companyID, Companies: companies}
|
||||
if ownedCreated {
|
||||
res.ProvisionCompanyIDs = []uuid.UUID{ownedID}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *Service) SetPassword(ctx context.Context, userID uuid.UUID, password string) error {
|
||||
|
||||
Reference in New Issue
Block a user