Generate presigned URL for email assets at render time

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-05 12:41:35 +04:00
parent a388aa4999
commit 6189a8ed81
18 changed files with 328 additions and 246 deletions

View File

@@ -116,9 +116,9 @@ func (s AccountService) ChangeEmail(ctx context.Context, identityID gid.GID, req
return fmt.Errorf("cannot update identity: %w", err)
}
emailPresenter := emails.NewPresenter(s.baseURL, s.emailStaticAssetURLs, identity.FullName)
emailPresenter := emails.NewPresenter(s.fm, s.bucket, s.baseURL, identity.FullName)
subject, textBody, htmlBody, err := emailPresenter.RenderConfirmEmail("/auth/verify-email", confirmationToken)
subject, textBody, htmlBody, err := emailPresenter.RenderConfirmEmail(ctx, "/auth/verify-email", confirmationToken)
if err != nil {
return fmt.Errorf("cannot render confirmation email: %w", err)
}

View File

@@ -290,9 +290,10 @@ func (s AuthService) SendPasswordResetInstructionByEmail(
return fmt.Errorf("cannot load identity: %w", err)
}
emailPresenter := emails.NewPresenter(s.baseURL, s.emailStaticAssetURLs, identity.FullName)
emailPresenter := emails.NewPresenter(s.fm, s.bucket, s.baseURL, identity.FullName)
subject, textBody, htmlBody, err := emailPresenter.RenderPasswordReset(
ctx,
"/auth/reset-password",
token,
)
@@ -406,9 +407,9 @@ func (s AuthService) CreateIdentityWithPassword(
return nil, nil, fmt.Errorf("cannot generate confirmation token: %w", err)
}
emailPresenter := emails.NewPresenter(s.baseURL, s.emailStaticAssetURLs, req.FullName)
emailPresenter := emails.NewPresenter(s.fm, s.bucket, s.baseURL, req.FullName)
subject, textBody, htmlBody, err := emailPresenter.RenderConfirmEmail("/auth/verify-email", confirmationToken)
subject, textBody, htmlBody, err := emailPresenter.RenderConfirmEmail(ctx, "/auth/verify-email", confirmationToken)
if err != nil {
return nil, nil, fmt.Errorf("cannot render confirmation email: %w", err)
}
@@ -569,7 +570,7 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
return fmt.Errorf("cannot load organization: %w", err)
}
emailPresenterCfg := emails.DefaultPresenterConfig(s.baseURL, s.emailStaticAssetURLs)
emailPresenterCfg := emails.DefaultPresenterConfig(s.bucket, s.baseURL)
if req.CompliancePageID != nil {
var err error
emailPresenterCfg, err = s.CompliancePageService.EmailPresenterConfig(ctx, *req.CompliancePageID)
@@ -578,9 +579,10 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
}
}
emailPresenter := emails.NewPresenterFromConfig(emailPresenterCfg, fullName)
emailPresenter := emails.NewPresenterFromConfig(s.fm, emailPresenterCfg, fullName)
subject, textBody, htmlBody, err := emailPresenter.RenderMagicLink(
ctx,
req.URLPath,
tokenString,
s.magicLinkTokenValidity,

View File

@@ -91,7 +91,7 @@ func (s *CompliancePageService) EmailPresenterConfig(ctx context.Context, compli
organization = &coredata.Organization{}
customDomain *coredata.CustomDomain
logoFile = &coredata.File{}
emailPresenterCfg = emails.DefaultPresenterConfig(s.baseURL, s.emailStaticAssetURLs)
emailPresenterCfg = emails.DefaultPresenterConfig(s.bucket, s.baseURL)
)
scope := coredata.NewScopeFromObjectID(compliancePageID)
@@ -153,13 +153,13 @@ func (s *CompliancePageService) EmailPresenterConfig(ctx context.Context, compli
// If logo exists, then we will brand the emails with the org as a sender
presignedURL, err := s.fm.GenerateFileUrl(ctx, logoFile, 7*24*time.Hour)
if err != nil {
return emailPresenterCfg, fmt.Errorf("cannot generate file URL: %w", err)
emailPresenterCfg.SenderCompanyLogo = emails.Asset{
Name: logoFile.FileName,
ObjectKey: logoFile.FileKey,
BucketName: logoFile.BucketName,
MimeType: logoFile.MimeType,
}
emailPresenterCfg.SenderCompanyLogoURL = presignedURL
emailPresenterCfg.SenderCompanyName = organization.Name
if organization.WebsiteURL != nil {

View File

@@ -464,9 +464,10 @@ func (s *OrganizationService) InviteMember(
return fmt.Errorf("cannot generate invitation token: %w", err)
}
emailPresenter := emails.NewPresenter(s.baseURL, s.emailStaticAssetURLs, identity.FullName)
emailPresenter := emails.NewPresenter(s.fm, s.bucket, s.baseURL, identity.FullName)
subject, textBody, htmlBody, err := emailPresenter.RenderInvitation(
ctx,
"/auth/signup-from-invitation",
invitationToken,
organization.Name,

View File

@@ -11,7 +11,6 @@ import (
"go.gearno.de/kit/log"
"go.gearno.de/kit/pg"
"go.opentelemetry.io/otel/trace"
"go.probo.inc/probo/packages/emails"
"go.probo.inc/probo/pkg/baseurl"
"go.probo.inc/probo/pkg/connector"
"go.probo.inc/probo/pkg/coredata"
@@ -29,7 +28,6 @@ type (
pg *pg.Client
fm *filemanager.Service
hp *passwdhash.Profile
emailStaticAssetURLs emails.StaticAssetURLs
encryptionKey cipher.EncryptionKey
baseURL string
tokenSecret string
@@ -84,7 +82,6 @@ func NewService(
pgClient *pg.Client,
fm *filemanager.Service,
hp *passwdhash.Profile,
emailStaticAssetURLs emails.StaticAssetURLs,
cfg Config,
) (*Service, error) {
if cfg.Bucket == "" {
@@ -107,7 +104,6 @@ func NewService(
pg: pgClient,
fm: fm,
hp: hp,
emailStaticAssetURLs: emailStaticAssetURLs,
baseURL: cfg.BaseURL.String(),
tokenSecret: cfg.TokenSecret,
disableSignup: cfg.DisableSignup,