Integrate email notifications into signup and login (#38)

This commit is contained in:
Stephan D committed 2026-09-12 15:07:00 +02:00
1 parent 326d58f705
commit ad5a061164
55 files changed
+1859 -3464

No files matched your search

+5 -3
View File
@@ -2,13 +2,15 @@ package api
import (
mwa "git.sendico.io/tech/sxchnge/product/auth/config"
"git.sendico.io/tech/sxchnge/product/notifications"
verificationconfig "git.sendico.io/tech/sxchnge/product/verification/config"
)
type Config struct {
Mw *mwa.Config `yaml:"middleware"`
AuthStorage *mwa.AuthStorageConfig `yaml:"auth_storage"`
Verification *verificationconfig.Config `yaml:"verification"`
Notifications *notifications.Config `yaml:"notifications"`
Mw *mwa.Config `yaml:"middleware"`
AuthStorage *mwa.AuthStorageConfig `yaml:"auth_storage"`
Verification *verificationconfig.Config `yaml:"verification"`
}
func (c *Config) SharedConfig() *Config {
+31 -5
View File
@@ -7,9 +7,12 @@ import (
mr "git.sendico.io/tech/sxchnge/product/app/metrics"
"git.sendico.io/tech/sxchnge/product/auth/config"
"git.sendico.io/tech/sxchnge/product/auth/flow"
httprouter "git.sendico.io/tech/sxchnge/product/auth/http"
"git.sendico.io/tech/sxchnge/product/auth/response"
authstorage "git.sendico.io/tech/sxchnge/product/auth/storage"
accountservice "git.sendico.io/tech/sxchnge/product/customers"
"git.sendico.io/tech/sxchnge/product/notifications"
"git.sendico.io/tech/sxchnge/product/sessions/browser"
"git.sendico.io/tech/sxchnge/shared/database"
"git.sendico.io/tech/sxchnge/shared/errors"
@@ -26,11 +29,12 @@ import (
)
type Middleware struct {
logger mlogger.Logger
router *chi.Mux
apiEndpoint string
metrics mr.Metrics
epdispatcher *httprouter.Dispatcher
notifications *notifications.Client
logger mlogger.Logger
router *chi.Mux
apiEndpoint string
metrics mr.Metrics
epdispatcher *httprouter.Dispatcher
}
func (mw *Middleware) Handler(service mservice.Type, endpoint string, method api.HTTPMethod, handler sresponse.HandlerFunc) {
@@ -47,6 +51,9 @@ func (mw *Middleware) PendingAccountHandler(service mservice.Type, endpoint stri
func (mw *Middleware) Finish() {
mw.metrics.Finish()
if mw.notifications != nil {
_ = mw.notifications.Close()
}
}
func (mw *Middleware) installMiddleware(config *middleware.Config, debug bool) {
@@ -107,6 +114,7 @@ func CreateMiddleware(
config *middleware.Config,
authStorage *middleware.AuthStorageConfig,
debug bool,
notificationConfig ...*notifications.Config,
) (*Middleware, error) {
if config == nil {
return nil, merrors.InvalidArgument("middleware configuration is required", "middleware")
@@ -139,6 +147,23 @@ func CreateMiddleware(
return nil, err
}
var authFlow *flow.Service
if len(notificationConfig) > 0 && notificationConfig[0] != nil {
c := notificationConfig[0]
customers, err := accountservice.NewAccountService(p.logger, db, &config.Password, authStorage)
if err != nil {
return nil, err
}
authFlow = &flow.Service{Accounts: stores.Accounts, Customers: customers, Tokens: stores.Verification, Transactions: db.TransactionFactory(), Attempts: stores.LoginAttempts, Secret: middleware.SignatureConf(config).PrivateKey, ActivationURL: c.ActivationURL}
if err = authFlow.Validate(); err != nil {
return nil, err
}
p.notifications, err = notifications.Open(*c)
if err != nil {
return nil, err
}
authFlow.Sender = p.notifications
}
p.epdispatcher = httprouter.NewDispatcher(
p.logger,
p.router,
@@ -148,6 +173,7 @@ func CreateMiddleware(
stores.LoginAttempts,
config,
loginRateLimits,
authFlow,
)
return p, nil
}
+1
View File
@@ -83,6 +83,7 @@ func (a *APIImp) installRuntimeBasics(
a.config.Mw,
a.config.AuthStorage,
debug,
a.config.Notifications,
); err != nil {
a.logger.Error("Failed to create middleware", zap.Error(err))
return err
@@ -2,7 +2,6 @@ package runtime_test
import (
"context"
sharedmodel "git.sendico.io/tech/sxchnge/shared/model"
"net/http"
"net/http/httptest"
"net/url"
@@ -14,6 +13,7 @@ import (
"git.sendico.io/tech/sxchnge/product/app/profile"
sharedruntime "git.sendico.io/tech/sxchnge/product/app/runtime"
"git.sendico.io/tech/sxchnge/shared/database"
sharedmodel "git.sendico.io/tech/sxchnge/shared/model"
"github.com/go-chi/chi/v5"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go/modules/mongodb"
@@ -48,6 +48,8 @@ func TestProfileStartsOnEmptyDatabase(t *testing.T) {
t.Setenv("SXCHNGE_API_PROTOCOL", "http")
t.Setenv("SXCHNGE_API_ENDPOINT", "/api/v1")
t.Setenv("SXCHNGE_JWT_SECRET", "test-only-signing-key-at-least-32-bytes")
t.Setenv("SXCHNGE_NOTIFICATIONS_TOKEN", "test-notification-token-at-least-32-bytes")
example.API.Notifications.CAFile = "" // This startup test makes no notification RPCs.
root, routes := chi.NewRouter(), chi.NewRouter()
runtime, err := sharedruntime.CreateAPI(ctx, zap.NewNop(), &example.API, &example.API, factory, root, routes, false, profile.Runtime())
require.NoError(t, err)