feat(19-04): add users.handle, HandleGenerator and user.Created

Generate a unique mention handle on API register and admin create, and fire user.Created from both paths so listeners run in the same write.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-17 21:44:59 +02:00
parent 569768e657
commit 15b19c7021
12 changed files with 349 additions and 19 deletions

View File

@@ -302,23 +302,33 @@ func (c usersAdminController) storePassword(ctx context.Context, user *models.Us
return true, nil
}
// FormAfterCreate sends the invitation when the send_invite checkbox was
// set: an activation code is issued inside the create's transaction, and
// exactly one golem15.user::mail.invite message with the activation link
// leaves after the commit. A create that rolls back sends nothing.
// FormAfterCreate assigns a unique handle, fires user.Created
// (CreatedEventName) inside the create transaction, and sends the
// invitation when the send_invite checkbox was set: an activation code is
// issued inside the same transaction, and exactly one
// golem15.user::mail.invite message with the activation link leaves after
// the commit. A create that rolls back sends nothing.
func (c usersAdminController) FormAfterCreate(ctx context.Context, model any) error {
user, ok := model.(*models.User)
if !ok || user == nil {
return errors.New("users admin: form model is not a user")
}
values, _ := cabana.VirtualFieldsFromContext(ctx)
if invite, _ := values["send_invite"].(bool); !invite {
return nil
}
tx, ok := cabana.TxFromContext(ctx)
if !ok {
return errors.New("users admin: a create needs its transaction")
}
if err := classes.AssignHandle(ctx, tx.Session(&gorm.Session{NewDB: true}), user); err != nil {
return err
}
if app := c.application(); app != nil && app.Events != nil {
if err := app.Events.Fire(ctx, &classes.CreatedEvent{User: user, Tx: tx}); err != nil {
return fmt.Errorf("%s: %w", classes.CreatedEventName, err)
}
}
values, _ := cabana.VirtualFieldsFromContext(ctx)
if invite, _ := values["send_invite"].(bool); !invite {
return nil
}
code, err := classes.IssueActivationCode(ctx, tx.Session(&gorm.Session{NewDB: true}), user)
if err != nil {
return err