add email templating

Signed-off-by: Christian Richter <crichter@owncloud.com>
Co-authored-by: Jörn Dreyer <jfd@butonic.de>
This commit is contained in:
Christian Richter
2022-09-12 16:59:49 +02:00
parent 131988ac6a
commit acdfbabd47
5 changed files with 159 additions and 23 deletions
+7 -1
View File
@@ -5,6 +5,7 @@ import (
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/events/server"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/go-micro/plugins/v4/events/natsjs"
"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
"github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
@@ -47,7 +48,12 @@ func Server(cfg *config.Config) *cli.Command {
if err != nil {
return err
}
svc := service.NewEventsNotifier(evts, channel, logger)
gwclient, err := pool.GetGatewayServiceClient(cfg.Notifications.RevaGateway)
if err != nil {
logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva client")
}
svc := service.NewEventsNotifier(evts, channel, logger, gwclient, cfg.Commons.MachineAuthAPIKey)
return svc.Run()
},
}
+25
View File
@@ -0,0 +1,25 @@
package email
import (
"bytes"
"html/template"
"os"
"path/filepath"
)
const templatePath string = "../../email/templates"
// RenderEmailTemplate renders the email template for a new share
func RenderEmailTemplate(templateName string, templateVariables map[string]string) (string, error) {
content, err := os.ReadFile(filepath.Join(templatePath, templateName))
if err != nil {
return "", err
}
tpl := template.Must(template.New("").Parse(string(content)))
writer := bytes.NewBufferString("")
err = tpl.Execute(writer, templateVariables)
if err != nil {
return "", err
}
return writer.String(), nil
}
@@ -0,0 +1 @@
{{ShareSharer}} has shared {{ShareFolder}} with you.
+126 -22
View File
@@ -1,33 +1,47 @@
package service
import (
"context"
"os"
"os/signal"
"syscall"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/notifications/pkg/channels"
"github.com/owncloud/ocis/v2/services/notifications/pkg/email"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
type Service interface {
Run() error
}
func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger) Service {
func NewEventsNotifier(events <-chan interface{}, channel channels.Channel, logger log.Logger, gwClient gateway.GatewayAPIClient, machineAuthAPIKey string) Service {
return eventsNotifier{
logger: logger,
channel: channel,
events: events,
signals: make(chan os.Signal, 1),
logger: logger,
channel: channel,
events: events,
signals: make(chan os.Signal, 1),
gwClient: gwClient,
machineAuthAPIKey: machineAuthAPIKey,
}
}
type eventsNotifier struct {
logger log.Logger
channel channels.Channel
events <-chan interface{}
signals chan os.Signal
logger log.Logger
channel channels.Channel
events <-chan interface{}
signals chan os.Signal
gwClient gateway.GatewayAPIClient
machineAuthAPIKey string
}
func (s eventsNotifier) Run() error {
@@ -39,20 +53,10 @@ func (s eventsNotifier) Run() error {
case evt := <-s.events:
go func() {
switch e := evt.(type) {
case events.SpaceCreated:
s.handleSpaceCreated(e)
case events.ShareCreated:
msg := "You got a share!"
var err error
if e.GranteeUserID != nil {
err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg)
} else if e.GranteeGroupID != nil {
err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg)
}
if err != nil {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("failed to send a message")
}
s.handleShareCreated(e)
}
}()
case <-s.signals:
@@ -62,3 +66,103 @@ func (s eventsNotifier) Run() error {
}
}
}
func (s eventsNotifier) handleSpaceCreated(e events.SpaceCreated) {
// TODO: implement me
}
func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
userResponse, err := s.gwClient.GetUser(context.Background(), &userv1beta1.GetUserRequest{
UserId: e.Sharer,
})
if err != nil || userResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("Could not get user response from gatway client")
return
}
// Get auth context
ownerCtx := ctxpkg.ContextSetUser(context.Background(), userResponse.User)
authRes, err := s.gwClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
Type: "machine",
ClientId: "userid:" + e.Sharer.OpaqueId,
ClientSecret: s.machineAuthAPIKey,
})
if err != nil || authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("Could not impersonate sharer")
return
}
if authRes.GetStatus().GetCode() != rpcv1beta1.Code_CODE_OK {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("could not get authenticated context for user")
return
}
ownerCtx = metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token)
resourceID, err := storagespace.ParseID(e.ItemID.OpaqueId)
if err != nil {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Str("itemid", e.ItemID.OpaqueId).
Msg("could not parse resourceid from ItemID ")
return
}
// TODO: maybe cache this stat to reduce storage iops
md, err := s.gwClient.Stat(ownerCtx, &providerv1beta1.StatRequest{
Ref: &providerv1beta1.Reference{
ResourceId: &resourceID,
},
FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}},
})
if err != nil || md.Status.Code != rpcv1beta1.Code_CODE_OK {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Str("itemid", e.ItemID.OpaqueId).
Msg("could not stat resource")
return
}
if md.Status.Code != rpcv1beta1.Code_CODE_OK {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Str("itemid", e.ItemID.OpaqueId).
Str("rpc status", md.Status.Code.String()).
Msg("could not stat resource")
return
}
msg, err := email.RenderEmailTemplate("shareCreated.email.tmpl", map[string]string{
"ShareSharer": userResponse.User.DisplayName,
"ShareFolder": md.Info.Name,
})
if err != nil {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("Could not render E-Mail template for shares")
}
if e.GranteeUserID != nil {
err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg)
} else if e.GranteeGroupID != nil {
err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg)
}
if err != nil {
s.logger.Error().
Err(err).
Str("event", "ShareCreated").
Msg("failed to send a message")
}
}