From e4c35b404ef024d88291a5ca264aa233935ff0b1 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:24:00 +0530 Subject: [PATCH 01/12] [server] Support for sending mail with base template --- server/pkg/utils/email/email.go | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/server/pkg/utils/email/email.go b/server/pkg/utils/email/email.go index 9586f5451e..ca6d825a19 100644 --- a/server/pkg/utils/email/email.go +++ b/server/pkg/utils/email/email.go @@ -167,6 +167,15 @@ func SendTemplatedEmail(to []string, fromName string, fromEmail string, subject return Send(to, fromName, fromEmail, subject, body, inlineImages) } +func SendTemplatedEmailV2(to []string, fromName string, fromEmail string, subject string, baseTemplate, templateName string, templateData map[string]interface{}, inlineImages []map[string]interface{}) error { + body, err := getMailBodyWithBase(baseTemplate, templateName, templateData) + if err != nil { + return stacktrace.Propagate(err, "") + } + + return Send(to, fromName, fromEmail, subject, body, inlineImages) +} + func GetMaskedEmail(email string) string { at := strings.LastIndex(email, "@") if at >= 0 { @@ -192,3 +201,30 @@ func getMailBody(templateName string, templateData map[string]interface{}) (stri } return htmlbody.String(), nil } + +// getMailBody generates the mail HTML body from the provided template and data, supporting inheritance +func getMailBodyWithBase(baseTemplateName, templateName string, templateData map[string]interface{}) (string, error) { + htmlBody := new(bytes.Buffer) + + // Define paths for the base template and the specific template + baseTemplate := "mail-templates/" + baseTemplateName + specificTemplate := "mail-templates/" + templateName + + parts := strings.Split(baseTemplate, "/") + lastPart := parts[len(parts)-1] + baseTemplateID := strings.TrimSuffix(lastPart, path.Ext(lastPart)) + + // Parse the base and specific templates together + t, err := template.ParseFiles(baseTemplate, specificTemplate) + if err != nil { + return "", stacktrace.Propagate(err, "failed to parse templates") + } + + // Execute the base template with the provided data + err = t.ExecuteTemplate(htmlBody, baseTemplateID, templateData) + if err != nil { + return "", stacktrace.Propagate(err, "failed to execute template") + } + + return htmlBody.String(), nil +} From 92208b7d219a94b5639527993828aed66590ab3b Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:25:54 +0530 Subject: [PATCH 02/12] [server] Send legacy invites --- server/mail-templates/legacy/legacy_base.html | 139 ++++++++++++++++++ .../mail-templates/legacy/legacy_invite.html | 9 ++ .../legacy/legacy_invite_accepted.html | 7 + .../legacy/legacy_invite_rejected.html | 7 + server/mail-templates/legacy/legacy_left.html | 7 + .../mail-templates/legacy/legacy_removed.html | 7 + .../legacy/recovery_cancelled.html | 7 + .../legacy/recovery_completed.html | 10 ++ .../legacy/recovery_ready_legacy.html | 10 ++ .../legacy/recovery_ready_trusted.html | 10 ++ .../legacy/recovery_rejected.html | 10 ++ .../legacy/recovery_reminder.html | 10 ++ .../legacy/recovery_started.html | 10 ++ .../pkg/controller/emergency/account_owner.go | 5 +- server/pkg/controller/emergency/controller.go | 2 + server/pkg/controller/emergency/email.go | 79 ++++++++++ 16 files changed, 326 insertions(+), 3 deletions(-) create mode 100644 server/mail-templates/legacy/legacy_base.html create mode 100644 server/mail-templates/legacy/legacy_invite.html create mode 100644 server/mail-templates/legacy/legacy_invite_accepted.html create mode 100644 server/mail-templates/legacy/legacy_invite_rejected.html create mode 100644 server/mail-templates/legacy/legacy_left.html create mode 100644 server/mail-templates/legacy/legacy_removed.html create mode 100644 server/mail-templates/legacy/recovery_cancelled.html create mode 100644 server/mail-templates/legacy/recovery_completed.html create mode 100644 server/mail-templates/legacy/recovery_ready_legacy.html create mode 100644 server/mail-templates/legacy/recovery_ready_trusted.html create mode 100644 server/mail-templates/legacy/recovery_rejected.html create mode 100644 server/mail-templates/legacy/recovery_reminder.html create mode 100644 server/mail-templates/legacy/recovery_started.html create mode 100644 server/pkg/controller/emergency/email.go diff --git a/server/mail-templates/legacy/legacy_base.html b/server/mail-templates/legacy/legacy_base.html new file mode 100644 index 0000000000..5e0629fc04 --- /dev/null +++ b/server/mail-templates/legacy/legacy_base.html @@ -0,0 +1,139 @@ +{{define "legacy_base"}} + + + + + + +
+Hey {{.TrustedUser}}!
+ +{{.LegacyUser}} has invited you as a trusted contact.Please open our mobile app to accept or reject their invite.
+ +Navigate to Settings -> Accounts -> Legacy to proceed further.
+ +If you need help with anything, please write back!
+{{end}} diff --git a/server/mail-templates/legacy/legacy_invite_accepted.html b/server/mail-templates/legacy/legacy_invite_accepted.html new file mode 100644 index 0000000000..87cde6f366 --- /dev/null +++ b/server/mail-templates/legacy/legacy_invite_accepted.html @@ -0,0 +1,7 @@ +{{define "content"}} +Hey {{.LegacyUser}}!
+ +{{.TrustedUser}} has accepted your invite to be a trusted contact.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_invite_rejected.html b/server/mail-templates/legacy/legacy_invite_rejected.html new file mode 100644 index 0000000000..d786e0ff45 --- /dev/null +++ b/server/mail-templates/legacy/legacy_invite_rejected.html @@ -0,0 +1,7 @@ +{{define "content"}} +Hey {{.LegacyUser}}!
+ +{{.TrustedUser}} has declined your invite to be a trusted contact.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_left.html b/server/mail-templates/legacy/legacy_left.html new file mode 100644 index 0000000000..fce8cecba0 --- /dev/null +++ b/server/mail-templates/legacy/legacy_left.html @@ -0,0 +1,7 @@ +{{define "content"}} +Hey {{.LegacyUser}}!
+ +{{.TrustedUser}} has stopped being your trusted contact. You can invite them back again or add more trusted contacts.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_removed.html b/server/mail-templates/legacy/legacy_removed.html new file mode 100644 index 0000000000..4e6c5fb90f --- /dev/null +++ b/server/mail-templates/legacy/legacy_removed.html @@ -0,0 +1,7 @@ +{{define "content"}} +Hey {{.TrustedUser}}!
+ +{{.LegacyUser}} has removed you as trusted contact.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_cancelled.html b/server/mail-templates/legacy/recovery_cancelled.html new file mode 100644 index 0000000000..0e2a2828fd --- /dev/null +++ b/server/mail-templates/legacy/recovery_cancelled.html @@ -0,0 +1,7 @@ +{{define "content"}} +Hey {{.LegacyUser}}!
+ +{{.TrustedUser}} has cancelled the process of recovering your account
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_completed.html b/server/mail-templates/legacy/recovery_completed.html new file mode 100644 index 0000000000..566549bdf8 --- /dev/null +++ b/server/mail-templates/legacy/recovery_completed.html @@ -0,0 +1,10 @@ + + + + +Hey {{.LegacyUser}}!
- - \ No newline at end of file +{{.TrustedUser}} has successfully changed password of your account.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_ready_legacy.html b/server/mail-templates/legacy/recovery_ready_legacy.html index 566549bdf8..738fa7e4fb 100644 --- a/server/mail-templates/legacy/recovery_ready_legacy.html +++ b/server/mail-templates/legacy/recovery_ready_legacy.html @@ -1,10 +1,7 @@ - - - - -Hey {{.LegacyUser}}!
- - \ No newline at end of file +{{.TrustedUser}} can now change the password of your account.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_ready_trusted.html b/server/mail-templates/legacy/recovery_ready_trusted.html index 566549bdf8..e26ab20208 100644 --- a/server/mail-templates/legacy/recovery_ready_trusted.html +++ b/server/mail-templates/legacy/recovery_ready_trusted.html @@ -1,10 +1,7 @@ - - - - -Hey {{.TrustedUser}}!
- - \ No newline at end of file +You can now change the password for {{.LegacyUser}}.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_rejected.html b/server/mail-templates/legacy/recovery_rejected.html index 566549bdf8..3735b79109 100644 --- a/server/mail-templates/legacy/recovery_rejected.html +++ b/server/mail-templates/legacy/recovery_rejected.html @@ -1,10 +1,7 @@ - - - - -Hey {{.TrustedUser}}!
- - \ No newline at end of file +{{.LegacyUser}} has rejected your attempt to recovery their account.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_reminder.html b/server/mail-templates/legacy/recovery_reminder.html index 566549bdf8..5ed9b41f30 100644 --- a/server/mail-templates/legacy/recovery_reminder.html +++ b/server/mail-templates/legacy/recovery_reminder.html @@ -1,10 +1,9 @@ - - - - -Hey {{.LegacyUser}}!
- - \ No newline at end of file +{{.TrustedUser}} had started the process to recover your account.
+ +They will be able to change your password after {{.TimeDuration}}.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_started.html b/server/mail-templates/legacy/recovery_started.html index 566549bdf8..5136abc455 100644 --- a/server/mail-templates/legacy/recovery_started.html +++ b/server/mail-templates/legacy/recovery_started.html @@ -1,10 +1,7 @@ - - - - -Hey {{.LegacyUser}}!
- - \ No newline at end of file +{{.TrustedUser}} has started the process to recover your account.
+ +If you need help with anything, please write back!
+{{end}} \ No newline at end of file diff --git a/server/pkg/controller/emergency/account_owner.go b/server/pkg/controller/emergency/account_owner.go index 502ed40c7e..a857ccd866 100644 --- a/server/pkg/controller/emergency/account_owner.go +++ b/server/pkg/controller/emergency/account_owner.go @@ -27,7 +27,7 @@ func (c *Controller) AddContact(ctx *gin.Context, userID int64, request ente.Add return stacktrace.Propagate(err, "") } if hasUpdated { - go c.sendNotification(ctx, userID, emergencyContactID, ente.UserInvitedContact, nil) + go c.sendNotification(ctx, userID, emergencyContactID, ente.UserInvitedContact) } return nil } diff --git a/server/pkg/controller/emergency/controller.go b/server/pkg/controller/emergency/controller.go index e32a13cc94..a29a14e575 100644 --- a/server/pkg/controller/emergency/controller.go +++ b/server/pkg/controller/emergency/controller.go @@ -28,7 +28,7 @@ func (c *Controller) UpdateContact(ctx *gin.Context, log.WithField("userID", userID).WithField("req", req). Warn("No update applied for emergency contact") } else { - go c.sendNotification(ctx, req.UserID, req.EmergencyContactID, req.State, nil) + go c.sendNotification(ctx, req.UserID, req.EmergencyContactID, req.State) } recoverStatus := getNextRecoveryStatusFromContactState(req.State) if recoverStatus != nil { diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index 810d200f3f..88875f6582 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -16,11 +16,21 @@ const ( LeftTemplate string = "legacy/legacy_left.html" AcceptedTemplate string = "legacy/legacy_invite_accepted.html" RejectedInviteTemplate string = "legacy/legacy_invite_rejected.html" - HappyHeaderImage = "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" - SadHeaderImage = "iVBORw0KGgoAAAANSUhEUgAAAN4AAADeCAYAAABSZ763AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAABjjSURBVHgB7Z1bbBzXecc/7lLkcilblOXKVqGYK6OODSSxqKdcAFcrC2hefH0pnMKCKMB20db1BYH7UAmQBFh9SBBYbpIWUFyYgoLEaB58UV4SQNZaRmv7SbQco3ZdxENbjWxFEimLXFKUdp3vP5yhZod7nTkzc2bO9wNWe+Hyot3z3+96vtNHQqJ8+eWXI1euXCnhgtv5fL6Uy+VG8bV6vV7q6+sbwePuc3G/zY+z8A8/Z4afO4NrXPjnTDn3J/nnz6xatcrC4yQkRh8JsTA/P19iAYzxgi/x4t/MQhhzhUUJ4ArREeYbuM0PW0NDQxYJkSPCiwBYpmq1WubFXIb14oVdTkpgveIKkm++wZdKoVCYFOuoHhGeAnxC2wprRhkCQoRV5P9XpVgsVkSI4RHhBQSuIy/EB3gR3s93y2QWFf6/v9rf318ZGBiYJKFnRHg9wGIr89VWvozzpUQCsBwRTogIu0eE1wFYNr7aSSK2brBFyF7AQUnStEeE1wTEbCy4cUPdSFVU+HKYBThBwgpEeB5mZ2fHODkyzjd3piULmQIsWhLhfrGC1xDh0XLstpfEukVNhZYEWCHDMVp4cCdpKX4rkxAnFi0JcIIMxUjhOYKDhSuRkCQWGSpAo4QngtMWiwwToBHCkxguNVhkiAAzLTynBvciieDSxgRlPAuaSeGhDnf58uW9fP0UCamF37+DXNZ5PosCzJzwWHAP1Gq1F6UOlxksyqD7mRnhiVuZeSYoQ+5njjLAwsLCk+yWnCQRXZZBC9/JarWaifAh1RZPrJyxVPiyK83WL7UWb3FxcadYOWMp8+W4U5dNJamzeJKxFLwg88mWb3/adsWnSniOa3mcpPNEaMTiy7Y0uZ6pcTU9rmWJBKGRUtoSL6mweJy1fE5cS6EbcrncvsHBwf2kOVoLD/Eci+5lkgSK0BsV0jzrqa3wJJ4TQmKRxnGfljEeRjCQiE4IR4kvxzk3oOWMU+2EhyQK++kiOkEFGJt/fG5u7gHSDK1cTYiuVqtNkCAohvMFTxeLxYOkCdpYPBTFRXRCVHC54TmsMdIELSweXhB2CfaRIESMLuWGxIUnohPiRgfxJSo8EZ2QFEmLLzHhieiEpElSfIkIz9m4qk2GSTCXfD4/PjAwcJhiJnbhYSYKW7qXSRA0IQnxxSo851CQ4zKISNAJ7OXjdbktzvP9YhOe9F4KmmNRjL2dsQhPRKeGN2d/R6/NvEMnZt+ji7U5+7HRgfX2Zce67XTX6q+TEAqrUChsiWM3e1zCg+jKJAQCgnt06nmaWjzb9nkQ4J4N36OHb7ibhMBU2Opto4iJvGXMadMpkxCIA2d+SX/10e6OogN4DgSK7xECU65Wq89RxERq8aRsEA4I6NnPXqIg7Ln5IdrN1k8IRtSZzsiEh7gOM1IkgxmMn1943bZeYfjtbQck7gsI4jxev1uiSrZE4mpiZANfSdkgBM8qcBfDCtdk3DXsXCsnEuE5cV2JhEDA2nUT03UCPwM/SwhMiT23SLYSKXc1nem+L5IQmDvef1SJ8AAynR987WckBCeKeE+pxXPqddpsNkwjqqydC34WyhFCcOr1+kFnbStDtasJS1ciITDPRlAKeFbKC6Fw4jylXpwy4aF0QFKvC4Vqa+dygi2eWL3QlFVOqlYiPJhh2VsXniPnj1FUiNULTy6X26vK5VQiPIhOSgfhgFU6EaFVEqsXHpUuZ2jhIYvJnwQ7SQhFHG1eYvWUUFYxpzOU8CSLqQbEdSdisEZi9dTA5YUXwxbWQwmP3UskVEokhOLAmWD9mEEQqxceiC5sYT1wAd2xdh+TEApYOxTM40R6OJWxKWgvZ2CLJ1lMNcRp7Vx+fPY1EpQQONESSHiSUFEDrN2RC9GVEFpx9OI7kdQLDaTMWihTAIJaPEmoKCAJa3ftd0usp4hAWuhZeE4TdImEUCRl7VyOXHidZpy5LUIoyo4meiKIxRNrp4AkrZ3LT/94lAQl9KyJnoQn1k4NSVs7FyRZxOopodSr1evV4om1U4AO1g5gRKBYPWX0pI2uhSfWTg1LXSrvkS6I1VNGT1avF4sn1k4BPz9/TKtUvlg9pXRdYutKeE6tokRCKLDIj2g4A0WsnjK6rut1Jby+vj6xdgrQtXAtVk8pXWmlY6+m9GSqQ+UQI9WsyQ/bQ5FG+FoIRz6f39Lp5KGOFk96MtUQ1VgHVYjVU8fVq1fHOz2nG4sHa1ciIRQ6WzsXWL3P7vwFCeHAFOrBwcFN7U4damvxpISgBt2tnQusngzADY+zX2+83XPaCo8VKzsQFJCmzaeyUVYNrJ372329pfCcQ0fKJIQiLdbOBX8rsq9CaMrtJpK1FB6LTtkMQVNBbSyNFuSZ0y9IXU8N462+0FJ4nUyl0Jm/7eIUVx3B3/xPLD4hNC1DtabCW1xcHCNJqgQG1uIxFt1rKXbZ0GHzmBzzFZZSq06WpsLrpg4hNAfj87770W4tW8N6Bf8HlEFkJGAoys0ebFrHk9pd98C6feKcyPPazNuxzMdMgr9c/XXasW473Tm0yb4IXWMNDa18wVYID25mrVY7ScIyiHlQ4zrFn0e4bV3+3L6Nx0wdGoTWMggQRffNxVvt23jsG8610MCKMYArhIcTUTix8hwZBgQEMX1iC+ss378mLsnw9QaEd8vAevtQzNGBm6g0uH5ZpCZaS64QPF0sFg96H1shvIWFheNZrN+5LqHXauH+lHMR4mPUESXEWRq8yQRrWWGLt837QIPwpqenRwqFwjSlFNdqwUq9W/2YxTa7bMXEaqWDrFpL1tVab+9mg/BwCkoul3uZUgLE9NOzr0V+xJWgD26SByPoIc60UK/XHxweHn7Fvd/v/SIrskwp4ScsuAOfvSSWzDDcD1mI7vH199Hjf3YvpQFHW8vCa7B4nFg5yU8YI81BYTcLdTIhPP/IwvvBxkdIdzhvMskJli3u/eUCOuK7NIgOfYQiOsHlx388mor2NmjLe6besvA4+NNedOj0/4nskhZ8QHxp6K5hj7Ls3va2jJVJc2SvmNCKR1PQV+rNoeQ8D24ljZGjpYR2TDlte5pTcm8sC4/TnVq7mkdnZHOm0B7dd4NwqW7ZuNnCw05ZtnihDlOPGrRwCUI73rykz2j8ZjizWEq47Vq8EmmOuJlCJ9JQ03U9S1t4rETtM5qCkAXYsyzh2rV4ZdKcNLUHCcmQhjWSz+c349oWHgd9a0hz7izeSoLQjjSskVqtds3V1D2jCe5d800ShHbcl4I1wkauZF87rWJaZzQButJxEYRmYG3clYL1gcwmLrnh4eESpYQfbnxExgoITTk0+iSlhStXrpRy7HNqb+1csBEyDZ3oQrzgAzlNyTdbeGkrJey44W4Rn7AMLF1a9uS5cJw3kktDfOcHe7DS5FoI0YA1gA/iFFJCVrNEKQQvuIjPTBDnp1h0sHij/ciwsNWjNIIXfjPHfZjcrEO70E1HL9G616tUG+qjj7+/jhbX5ds+f+B8jTZOzNDw/y7S2XtW0+f3Xke9sva/q7ThV5fs33n2nuto+jtDHb9n9N+n6frJBZr76gBN/d1aqhU7HgysDRDdb247kPoxgbk0FM/bgTcAb0TS2c7VLJ71v56lfLW+LKhOrP/1JVt0S7dnaejTK9Qr3t+54Vdf2Lfbsfatqi06gN994+vpmVmTFdFx3byU439SF+P5ccWXZGYLFscLFrUrqlYMf9j49dz8l9Qrec/3QHSdhNTpb9IVvLdZEB2w63hpTK40I2nxzX9lle26eYHr2QpYSFgpF7h7/u/vhvPbiw331x2rtrV6frHPfnWQdCdLogPQXHqc+y5w36CkxOeP0WBdXLfOzwi7fF6CiA6cu3u4IUaD6Db85xdNn4u/xSv2KxyDBv29cZH0exoVqc1qtiLJNwqL2L+QNx6+2LDYAe77Lc/0t4sUBIgOiRkva9+ab+pS3nis0Q2dFdElQuYsnkuSb9jp8ZEVFmj03xqn4kMAfsvzxVhwl+/c9mFa2Ngwm9jOXHp/xxq2dn4xnt+ub/tdVkUH7BiPMkpSbxxKCH4LVDh9hW790XlbCEj/r/NZns/v6b2M4OfTXY2hOgS/iX8nMqX4vTf73M+52wfsuFRHdEiWRU3f/HyAVFqKwAEmqPO9a5+1GR8QWjcZRFi7D/5FzQKDJW0V3/n5kH9npzpjEuhSHoqazFo8lzVO7WdzzBkxFKavdLGwPx1Xl1SGy3muC/cRSSARXbJkXnjAFV+cm2kR5/3+++tWxF5eIDrVWcUzf3192w4YCNPvCusATgAyRXQg866mn8em/pWOXDhGcYIs443HZqnw6VWqsyDnv9JvC2R+Y3QxFmI7uJ1Dp69SjuM9xHTT3yp21VIWNxDdoVueIJPoq1ar01kpondLEuITmmOi6EDOe0qlKRwafYL2bPgeCcmC98BE0TGWETFeM3bf/JCIL0Hw2uM9MBXsQDfO4rmI+JLBdNFBc9gWZKzwgIgvXkwXHUB4B4s3RYYj4osHEd0SrLmL/STYuAtCDr+MBkwCS9tQoqjI5/PTSK5YJNhAfI+vv48EtcDSieiuUa/Xp2DxLBKW2cPiOzrztjbHgmHD7HWTC7T6w0VadaG2vMkVnTHoikGj86WxgrZbfNDoLO5lI0iu9LP6kGEhYQm0l8HqPXP6BUoSzEZZf3R2xV4+FwjQHS+B5mh3V8Tc7YNa9WHulth5BZxcmexbXFwcq9VqJ0lYBjsa7nj/0UQml3knjwXBFeD0d4JtrFXNmTt/IWP3fXCMtyU3NzdnkdAArF4Se8GwV+8vnj0XaiCRLdzDF+n2fz7b0lrGBQ4SEdGtZNWqVVb/2rVrZ6rV6oxp/ZqdwFlrce7hw2AkjOrzg6ZqxG/T3x6y4zl3qxE21w6cq9H17y6wYOdXfB9EBxHD+nWzVSgKRgflMFE/qOHh4pYTLL7IccwJ0Up0X3DSBLsYmsVsCxtX2Rc8B4NsR96apxvYYq7yWDl38BGugwzLFdTDiZVJXNtZFVbguyQ0MHM1nvgOk7/8ooOV+wMLDptpu0mUuHEd9v812/aDn48ZLJ2G3QrRg+I5rnPOnUkSGrhYm6WoceMxL3V7A+0NgQYRQYCnd47YVtI/lh0Cv/VHF2IV36lqvOM2UkIF/7jCs0ho4MTs7yhqMIzILwSILuwGWcR0/7fnxhWjJ5aGLsUnPkuTWqhOoJSAa1t4XMcTi+chjqQK4jp/1vEPCnelw/rB9WwlvjhAWUaXRgSNsPCPLbyhoSHL5O1Bfj6JeLFAcP64DllL1XMuIb6P2PL5575AfN0cqqKCN2PwHNICspnQGm57A4E3SLA5FbHFwylBXmCVzkaUdWw1dAlzYNqd7aCKuMcq6ow3l+IVnkWCzYlL71FUoPfSX3fDQNso27xc8fndTlhd/1h31Yir2cCycVsWHpvBV0iwiXKxrPdZGXv6VwyTv9qJrxDgXL5uOVX9PQnLVNwby8JbWFiQBAtFmxBY3eTMPKT/4wJW1fr7tSvPdoiwxofXUofTenWgUCisdDXROib1vGhjEv/RXEioxL2TAN0uqPN5QbIH4ouK9yTOs+M770Q//34g4xMsUSVWlg4saYztktpBAMH7+zfd7UVRIAkWmwZtNQiPVVkhw4mq2wLNzF6SPhQSVs//+9HXGUW8d2pe4jy/thqEt7i4WCHDmVr8nKJg7X81upkqjuYKi/8sP/DnXZ421AvSOkZULBYr3vsNrzriPPZDK2QwUbWKFU5fbbiPbGbSLPV2rml4LMxewFZI6xhV/BPbV8x8qNfrr5KhRBmLeGMq3NZlPAO2Ffn/NtWY3jrGbuYKTa0Y7+fU854jA4myVQwxFRY5mNNsMFEcfxtax0ZvuJtMpL+/v+J/bIXFc3rJLDKQqHckYFHrJjqXqP82gzOb1sDAwIoyXavxYofJQKTLIjpMdTWbuZmglfAqZCCnpN4UGaZ+qLGbOdHs8abCY3ezQoa5m0gASGtTdBjaOtbUzQTtJtka5W5Kd0X0mNY61srNBO2EN0EGIW5m9Jj24cYVgoOtvtZSeMhumlRMP3FJdkpHjWGtYxV3t3kz2h6aYFIx/ZOIWsWEaxjWOtY2VGsrvMuXL0+YMotFYrzoMah1zGJrN9HuCW2Fh95NMiDJIqKLB1Nax3K5XKXTczqeCIs6RK1We5IyzCcpWQw4BOSu675Bm4c22YequOl5tGMdOX8sFYvahNYxDtH2d3pOR+GhDrGwsFBhl7NMGeWE5iPoIDicM4frll+/+SH7//HY1PNaCxDexcOUadomVVy6OpGSRddRwWlG164KWLXf3HbAvrQSnRc854Ov/YwOjT6ZyDFj3WCAq9mVVvqoS9jqHc+q1dtw6m+06qrAmXK7FZwbfuTC63TgzC+1Wuz4QMCHQ0ZBUmVTN0/s+gxmFl0mkyw6tYq5gvsfXphhRQd2cCyFRb6Hf6YuFjDjrWNde4ZdWzwwP2+n/0qUIRAXffej3ZQkENw/rL/PFltUJ6hiwR/47CU7CZM0v2XX+a4uXOeU0bW1A11bPIfMxXpJZzR3rLub3r7jIO3h5EiUxxbD4h265QnbAu5Yt52SJKNxXk/a6El4TlHQIiE0SIQgaXLolngTIToJMEN0LJj76dXigUxZvTX5eM8HdwXXbaYyKlwB4u+IO/6L+zWPgZ410VOM55KlDCfcnjvef5SiBov7BxsfofvWfJN0JM4MKKytruWOAPQU27kEsXiZquthAURpeRC3/ZAFh8Wmq+iAmwGNugbodt1kiEBaCCQ87FDP0pahhyOIdVSXBuICAnwHyZ6IShDI3maFXC430Wts5xLI1QRcWijxVWa6i1FSUNU65ha/R1Iey6guQWSweL6pm/awZgQWHqhWqwfZ8mWigRqLDOILE+OgNLD7Zn2K1apQIUC3/S0rrw2HW88Xi8WnKCChhDc9PT0yODj4MYsvvkPeIiSo+Do1MWcFvC7P/P9/0NGZt3v6vqyJjpZKatuCWjsQSnhgbm7uAfZ1X6aM0MunuymC83P04jv0zOkXuvqAwmujc9N2QHYFje1cQgsPZLGB2hUgzkP3LjDEbfdwdhLFZ9ME5wcCxAcU9th5+y8hsntHvkX38uuUtdcICRX28nZRSJQID4kWFt7JrLicfrCo0EyNwm/aEyZRYcJrhDEovMa3hHExXZQID7DVe4r/MCMPOxHMgNf305xQOUgKUCY8kOU9e4LZoG5dKBS2kSICFdBbwaLbZcpUMsEoLKxtUohS4TlDcJ8mQcgW+1XEdV6UCg8gzYriIglCBsBaDls6aIbSGM8FhXX2h09SxnarC8Zh8Tre4j+/XAXKLR5wBuFuk3hPSCvO2t0WhehAJMIDEu8JKUd5XOclMuEBifeElLJfVb2uFZHEeH6kviekBdX1ulZEavFc5ufnHyQZkiToj/J6XStisXjA2Th7nCTTKeiJRSG3+vRCLBYP4D9Uq9UelEynoBtYk1ibcYkOxCY8sHr16knJdAq6kc/nd2FtUozEKjzgdAHE4kcLQiew42BwcPAVipnYhQcc8WX66C8hFUReNmhFbMmVZnDCZR9f7SVBiB8UyPdRQiQqPCDiExIgUdGBxIUHRHxCjCQuOqCF8ICIT4gBLUQHtBEekLktQoSEHsmnkkSymq0oFAoH6/W6FNkFZThrSSvRAa0snsvs7OwYFzUxJLdEghAcu1sq7uJ4N2gpPCC9nUJILIqx97JXtHI1vTgv2LYsHQcmxIOztWeLrqID2goP4IVz9kZJl4vQFdh4jTUT1cgGVWjravpBxpMTL3uzOiZeCIczXv1p3ZIorUiN8IDEfUILLNI4nmuG1q6mH7ywbPm2yBwXwcVxLbWO55qRKovnha3fOC11upRIMI60uZZ+Uis8ANeTX/wXZZCSWSBridkoabNyXlLlavpxs5745JNul+yD9xgbV/Gep1l0INUWz4uTeNnHl50kZI4sWDkvmRGei8R+2QJWDjNRkhjPECWZEx5wjoZ+ij8lnyQhtTgn9ezTvRgehEwKz0Xcz3SSNbeyGZkWnou4n+nAERw2q1Yo4xghPBcRoLZYtLQ7fIIMwSjhuYgAtcEiwwTnYqTwXESAyeC4lIdNFJyL0cJzYQGWeTHslQ6YaDEphuuECM+DJwu6lcQKKgF1uFwud5gvEwMDA9qNYEgKEV4L4IbyJ/ROsYLBgHWr1+uvwp3MYh0uLCK8DjiN2Bg7eD+JFeyExZfDfJnIcg1OBSK8HsD0s/7+/nERYQMWLYmtIrFb94jwAgIRctxS5sv9prmjrhvJ16+IZQuGCE8BLLyRarVazufzZb67le+PUYZggSEp8katVqsUi8WKxGzhEeFFAIS4sLAA8aFMsZWtw1hahjQ5WcgK35zi268UCoVJEZp6RHgx4ZQqsGsCLirKFSNJCtIRmAVrxpbsXb5v8f1JcR3jQYSXMLCOc3NzJXZTRyBKCJEFMIrH+cv2xbkNSm1+zoxrmZxr3LdwnwU+xeKCyLCD2xoeHrbEiiXLnwDnkx8SBNVTNQAAAABJRU5ErkJggg==" + + RecoveryStartedTemplate string = "legacy/recovery_started.html" + RecoveryRejectedTemplate string = "legacy/recovery_rejected.html" + RecoveryCancelledTemplate string = "legacy/recovery_cancelled.html" + RecoveryReminderTemplate string = "legacy/recovery_reminder.html" + + RecoveryCompletedTemplate string = "legacy/recovery_completed.html" + RecoveryReadyLegacyTemplate string = "legacy/recovery_ready_legacy.html" + RecoveryReadyTrustedTemplate string = "legacy/recovery_ready_trusted.html" + + HappyHeaderImage = "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" + SadHeaderImage = "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" ) -func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.ContactState, inviteToken *string) error { +func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.ContactState) error { legacyUser, err := c.UserRepo.Get(legacyUserID) if err != nil { return stacktrace.Propagate(err, "") @@ -30,8 +40,8 @@ func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, t return stacktrace.Propagate(err, "") } templateData := map[string]interface{}{ - "LegacyUser": trustedUser.Email, - "TrustedUser": legacyUser.Email, + "LegacyUser": legacyUser.Email, + "TrustedUser": trustedUser.Email, } var templateName, emailTo, title string @@ -77,3 +87,77 @@ func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, t } return nil } + +func (c *Controller) sendRecoveryNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.RecoveryStatus) error { + legacyUser, err := c.UserRepo.Get(legacyUserID) + if err != nil { + return stacktrace.Propagate(err, "") + } + trustedUser, err := c.UserRepo.Get(trustedUserID) + if err != nil { + return stacktrace.Propagate(err, "") + } + templateData := map[string]interface{}{ + "LegacyUser": legacyUser.Email, + "TrustedUser": trustedUser.Email, + } + + var templateName, emailTo, title string + var inlineImages []map[string]interface{} + inlineImage := make(map[string]interface{}) + inlineImage["mime_type"] = "image/png" + inlineImage["cid"] = "header-image" + + if newStatus == ente.RecoveryStatusInitiated { + templateName = RecoveryStartedTemplate + title = fmt.Sprintf("CRITICAL: %s has initiated recovery process for your account", trustedUser.Email) + emailTo = legacyUser.Email + inlineImage["content"] = HappyHeaderImage + } else if newStatus == ente.RecoveryStatusRecovered { + emailTo = legacyUser.Email + templateName = RecoveryCompletedTemplate + title = fmt.Sprintf("Your account has been successfully recovered by %s", trustedUser.Email) + inlineImage["content"] = SadHeaderImage + } else if newStatus == ente.RecoveryStatusStopped { + emailTo = legacyUser.Email + templateName = RecoveryCancelledTemplate + title = fmt.Sprintf("%s has cancelled recovery process", trustedUser.Email) + inlineImage["content"] = SadHeaderImage + } else if newStatus == ente.RecoveryStatusRejected { + emailTo = trustedUser.Email + templateName = RecoveryRejectedTemplate + title = fmt.Sprintf("%s has declined your recovery attempt", legacyUser.Email) + inlineImage["content"] = SadHeaderImage + } else if newStatus == ente.RecoveryStatusWaiting { + emailTo = legacyUser.Email + templateName = RecoveryReminderTemplate + title = fmt.Sprintf("%s is recoverying your account!", trustedUser.Email) + inlineImage["content"] = HappyHeaderImage + } else if newStatus == ente.RecoveryStatusReady { + emailTo = trustedUser.Email + templateName = RecoveryReadyTrustedTemplate + title = fmt.Sprintf("You can now change password for %s", legacyUser.Email) + inlineImage["content"] = HappyHeaderImage + } else { + return stacktrace.Propagate(fmt.Errorf("unsupported status %s", newStatus), "") + } + inlineImages = append(inlineImages, inlineImage) + err = emailUtil.SendTemplatedEmailV2([]string{emailTo}, "Ente", "legacy@ente.io", + title, BaseTemplate, templateName, templateData, inlineImages) + if err != nil { + log.WithError(err).WithField("state", newStatus).Error("failed to send email") + return stacktrace.Propagate(err, "") + } + if newStatus == ente.RecoveryStatusReady { + emailTo = legacyUser.Email + templateName = RecoveryReadyLegacyTemplate + title = fmt.Sprintf("%s can now change password for your account", trustedUser.Email) + err = emailUtil.SendTemplatedEmailV2([]string{emailTo}, "Ente", "legacy@ente.io", + title, BaseTemplate, templateName, templateData, inlineImages) + if err != nil { + log.WithError(err).WithField("state", newStatus).Error("failed to send email") + return stacktrace.Propagate(err, "") + } + } + return nil +} diff --git a/server/pkg/controller/emergency/recovery.go b/server/pkg/controller/emergency/recovery.go index 4983693048..4b726911d7 100644 --- a/server/pkg/controller/emergency/recovery.go +++ b/server/pkg/controller/emergency/recovery.go @@ -59,6 +59,8 @@ func (c *Controller) ChangePassword(ctx *gin.Context, userID int64, request ente if !hasUpdate { log.WithField("userID", userID).WithField("req", request). Warn("no row updated while rejecting recovery") + } else { + go c.sendRecoveryNotification(ctx, contact.UserID, contact.EmergencyContactID, ente.RecoveryStatusRecovered) } return resp, nil diff --git a/server/pkg/controller/emergency/recovery_contact.go b/server/pkg/controller/emergency/recovery_contact.go index 039050f1be..c2d763d061 100644 --- a/server/pkg/controller/emergency/recovery_contact.go +++ b/server/pkg/controller/emergency/recovery_contact.go @@ -26,6 +26,8 @@ func (c *Controller) StartRecovery(ctx *gin.Context, if !hasUpdate { log.WithField("userID", actorUserID).WithField("req", req). Warn("No need to send email") + } else { + go c.sendRecoveryNotification(ctx, req.UserID, req.EmergencyContactID, ente.RecoveryStatusInitiated) } if err != nil { return stacktrace.Propagate(err, "") @@ -46,6 +48,8 @@ func (c *Controller) RejectRecovery(ctx *gin.Context, if !hasUpdate { log.WithField("userID", userID).WithField("req", req). Warn("no row updated while rejecting recovery") + } else { + go c.sendRecoveryNotification(ctx, req.UserID, req.EmergencyContactID, ente.RecoveryStatusRejected) } if err != nil { return stacktrace.Propagate(err, "") @@ -66,6 +70,8 @@ func (c *Controller) ApproveRecovery(ctx *gin.Context, if !hasUpdate { log.WithField("userID", userID).WithField("req", req). Warn("no row updated while rejecting recovery") + } else { + go c.sendRecoveryNotification(ctx, req.UserID, req.EmergencyContactID, ente.RecoveryStatusReady) } if err != nil { return stacktrace.Propagate(err, "") @@ -83,11 +89,14 @@ func (c *Controller) StopRecovery(ctx *gin.Context, return stacktrace.Propagate(ente.ErrPermissionDenied, "only the emergency contact can stop recovery") } hasUpdate, err := c.Repo.UpdateRecoveryStatusForID(ctx, req.ID, ente.RecoveryStatusStopped) - if !hasUpdate && err == nil { + if err != nil { + return stacktrace.Propagate(err, "") + } + if !hasUpdate { log.WithField("userID", userID).WithField("req", req). Warn("no row updated while stopping recovery") } else { - log.WithField("userID", userID).WithField("req", req).Info("stopped recovery") + go c.sendRecoveryNotification(ctx, req.UserID, req.EmergencyContactID, ente.RecoveryStatusStopped) } return stacktrace.Propagate(err, "") } From b6d9527f1df516abe86cb71a690f741cf935f658 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:23:40 +0530 Subject: [PATCH 04/12] [server] Update template --- server/mail-templates/legacy/legacy_invite.html | 9 +++++---- server/pkg/controller/emergency/email.go | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/mail-templates/legacy/legacy_invite.html b/server/mail-templates/legacy/legacy_invite.html index ecb4f8d097..dc59ad1637 100644 --- a/server/mail-templates/legacy/legacy_invite.html +++ b/server/mail-templates/legacy/legacy_invite.html @@ -1,9 +1,10 @@ {{define "content"}}Hey {{.TrustedUser}}!
-{{.LegacyUser}} has invited you as a trusted contact.Please open our mobile app to accept or reject their invite.
+{{.LegacyUser}} has invited you to be their trusted contact.
-Navigate to Settings -> Accounts -> Legacy to proceed further.
+As a trusted contact, you can recover {{.LegacyUser}}'s account in their absence.
+To accept the invite, please open the Ente Photos app, and navigate to Settings -> Account -> Legacy .
-If you need help with anything, please write back!
-{{end}} +If you need any help, please reply to this email or write to support@ente.io
+{{end}} \ No newline at end of file diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index 88875f6582..af6a3c649a 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -52,7 +52,7 @@ func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, t if newStatus == ente.UserInvitedContact { templateName = InviteTemplate - title = "You've been invited to join as trusted contact on Ente!" + title = fmt.Sprintf("%s has added you as a Trusted Contact", legacyUser.Email) emailTo = trustedUser.Email inlineImage["content"] = HappyHeaderImage } else if newStatus == ente.UserRevokedContact { From 35de8876247124586cbf0ccc0bbee9779585bc70 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:27:52 +0530 Subject: [PATCH 05/12] [server] Update contact emails --- server/mail-templates/legacy/legacy_invite_sent.html | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 server/mail-templates/legacy/legacy_invite_sent.html diff --git a/server/mail-templates/legacy/legacy_invite_sent.html b/server/mail-templates/legacy/legacy_invite_sent.html new file mode 100644 index 0000000000..baf38445f8 --- /dev/null +++ b/server/mail-templates/legacy/legacy_invite_sent.html @@ -0,0 +1,10 @@ +{{define "content"}} +Hey {{.LegacyUser}}!
+ +You have invited {{.TrustedyUser}} to be your trusted contact.
+ +As a trusted contact, {{.TrustedyUser}} can recover your account in your absence.
+If you want to cancel the invite, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
+ +If you need any help, please reply to this email or write to support@ente.io.
+{{end}} \ No newline at end of file From 9c0426d71679d394290999b55297b116c101e4da Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:27:58 +0530 Subject: [PATCH 06/12] [server] Update contact emails --- .../mail-templates/legacy/legacy_invite.html | 2 +- .../legacy/legacy_invite_accepted.html | 6 +- .../legacy/legacy_invite_rejected.html | 4 +- server/mail-templates/legacy/legacy_left.html | 4 +- .../mail-templates/legacy/legacy_removed.html | 4 +- .../pkg/controller/emergency/account_owner.go | 2 +- server/pkg/controller/emergency/controller.go | 2 +- server/pkg/controller/emergency/email.go | 130 ++++++++++++------ 8 files changed, 100 insertions(+), 54 deletions(-) diff --git a/server/mail-templates/legacy/legacy_invite.html b/server/mail-templates/legacy/legacy_invite.html index dc59ad1637..4f3e0eadb5 100644 --- a/server/mail-templates/legacy/legacy_invite.html +++ b/server/mail-templates/legacy/legacy_invite.html @@ -6,5 +6,5 @@As a trusted contact, you can recover {{.LegacyUser}}'s account in their absence.
To accept the invite, please open the Ente Photos app, and navigate to Settings -> Account -> Legacy .
-If you need any help, please reply to this email or write to support@ente.io
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_invite_accepted.html b/server/mail-templates/legacy/legacy_invite_accepted.html index 87cde6f366..95624b8810 100644 --- a/server/mail-templates/legacy/legacy_invite_accepted.html +++ b/server/mail-templates/legacy/legacy_invite_accepted.html @@ -1,7 +1,9 @@ {{define "content"}}Hey {{.LegacyUser}}!
-{{.TrustedUser}} has accepted your invite to be a trusted contact.
+{{.TrustedUser}} has accepted your request to be your trusted contact.
-If you need help with anything, please write back!
+As a trusted contact, {{.TrustedUser}} can recover your account in your absence.
+ +If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_invite_rejected.html b/server/mail-templates/legacy/legacy_invite_rejected.html index d786e0ff45..971e4c853c 100644 --- a/server/mail-templates/legacy/legacy_invite_rejected.html +++ b/server/mail-templates/legacy/legacy_invite_rejected.html @@ -1,7 +1,7 @@ {{define "content"}}Hey {{.LegacyUser}}!
-{{.TrustedUser}} has declined your invite to be a trusted contact.
+{{.TrustedUser}} has rejected your request to be your trusted contact.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_left.html b/server/mail-templates/legacy/legacy_left.html index fce8cecba0..72d03dd95b 100644 --- a/server/mail-templates/legacy/legacy_left.html +++ b/server/mail-templates/legacy/legacy_left.html @@ -1,7 +1,7 @@ {{define "content"}}Hey {{.LegacyUser}}!
-{{.TrustedUser}} has stopped being your trusted contact. You can invite them back again or add more trusted contacts.
+{{.TrustedUser}} has removed themselves from being your trusted contact.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/legacy_removed.html b/server/mail-templates/legacy/legacy_removed.html index 4e6c5fb90f..160dba288d 100644 --- a/server/mail-templates/legacy/legacy_removed.html +++ b/server/mail-templates/legacy/legacy_removed.html @@ -1,7 +1,7 @@ {{define "content"}}Hey {{.TrustedUser}}!
-{{.LegacyUser}} has removed you as trusted contact.
+{{.LegacyUser}} has removed you as their trusted contact.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/pkg/controller/emergency/account_owner.go b/server/pkg/controller/emergency/account_owner.go index a857ccd866..27a747d1c5 100644 --- a/server/pkg/controller/emergency/account_owner.go +++ b/server/pkg/controller/emergency/account_owner.go @@ -27,7 +27,7 @@ func (c *Controller) AddContact(ctx *gin.Context, userID int64, request ente.Add return stacktrace.Propagate(err, "") } if hasUpdated { - go c.sendNotification(ctx, userID, emergencyContactID, ente.UserInvitedContact) + go c.sendContactNotification(ctx, userID, emergencyContactID, ente.UserInvitedContact) } return nil } diff --git a/server/pkg/controller/emergency/controller.go b/server/pkg/controller/emergency/controller.go index a29a14e575..ed7c5cb396 100644 --- a/server/pkg/controller/emergency/controller.go +++ b/server/pkg/controller/emergency/controller.go @@ -28,7 +28,7 @@ func (c *Controller) UpdateContact(ctx *gin.Context, log.WithField("userID", userID).WithField("req", req). Warn("No update applied for emergency contact") } else { - go c.sendNotification(ctx, req.UserID, req.EmergencyContactID, req.State) + go c.sendContactNotification(ctx, req.UserID, req.EmergencyContactID, req.State) } recoverStatus := getNextRecoveryStatusFromContactState(req.State) if recoverStatus != nil { diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index af6a3c649a..90dd6e0442 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -12,6 +12,7 @@ import ( const ( BaseTemplate string = "legacy/legacy_base.html" InviteTemplate string = "legacy/legacy_invite.html" + InviteSentTemplate string = "legacy/legacy_invite_sent.html" RemovedTemplate string = "legacy/legacy_removed.html" LeftTemplate string = "legacy/legacy_left.html" AcceptedTemplate string = "legacy/legacy_invite_accepted.html" @@ -30,7 +31,76 @@ const ( SadHeaderImage = "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" ) -func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.ContactState) error { +type emailData struct { + title string + templateName string + emailTo string + templateData map[string]interface{} + inlineImages []map[string]interface{} +} + +func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatus ente.ContactState) ([]emailData, error) { + templateData := map[string]interface{}{ + "LegacyUser": legacyUser.Email, + "TrustedUser": trustedUser.Email, + } + + var emailContent []emailData + switch newStatus { + case ente.UserInvitedContact: + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("%s has added you as a Trusted Contact", legacyUser.Email), + templateName: InviteTemplate, + emailTo: trustedUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("You have added %s as a Trusted Contact", trustedUser.Email), + templateName: InviteSentTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + case ente.UserRevokedContact: + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("%s has removed you as a Trusted Contact", legacyUser.Email), + templateName: RemovedTemplate, + emailTo: trustedUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + case ente.ContactLeft: + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("%s has removed themselves as a Trusted Contact", trustedUser.Email), + templateName: LeftTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + case ente.ContactDenied: + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("%s has rejected your request to be a Trusted Contact", trustedUser.Email), + templateName: RejectedInviteTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + case ente.ContactAccepted: + emailContent = append(emailContent, emailData{ + title: fmt.Sprintf("%s has accepted your request to be a Trusted Contact", trustedUser.Email), + templateName: AcceptedTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{}, + }) + default: + return nil, fmt.Errorf("unsupported status %s", newStatus) + } + return emailContent, nil +} + +func (c *Controller) sendContactNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.ContactState) error { legacyUser, err := c.UserRepo.Get(legacyUserID) if err != nil { return stacktrace.Propagate(err, "") @@ -39,52 +109,26 @@ func (c *Controller) sendNotification(ctx context.Context, legacyUserID int64, t if err != nil { return stacktrace.Propagate(err, "") } - templateData := map[string]interface{}{ - "LegacyUser": legacyUser.Email, - "TrustedUser": trustedUser.Email, - } - var templateName, emailTo, title string - var inlineImages []map[string]interface{} - inlineImage := make(map[string]interface{}) - inlineImage["mime_type"] = "image/png" - inlineImage["cid"] = "header-image" - - if newStatus == ente.UserInvitedContact { - templateName = InviteTemplate - title = fmt.Sprintf("%s has added you as a Trusted Contact", legacyUser.Email) - emailTo = trustedUser.Email - inlineImage["content"] = HappyHeaderImage - } else if newStatus == ente.UserRevokedContact { - emailTo = trustedUser.Email - templateName = RemovedTemplate - title = "You have been removed as a trusted contact on Ente" - inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.ContactLeft { - emailTo = legacyUser.Email - templateName = LeftTemplate - title = fmt.Sprintf("%s has left as trusted contact", trustedUser.Email) - inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.ContactDenied { - emailTo = legacyUser.Email - templateName = RejectedInviteTemplate - title = fmt.Sprintf("%s has declined your invite for trusted contact", trustedUser.Email) - inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.ContactAccepted { - emailTo = legacyUser.Email - templateName = AcceptedTemplate - title = fmt.Sprintf("%s has accepted your invitation!", trustedUser.Email) - inlineImage["content"] = HappyHeaderImage - } else { - return stacktrace.Propagate(fmt.Errorf("unsupported status %s", newStatus), "") - } - inlineImages = append(inlineImages, inlineImage) - err = emailUtil.SendTemplatedEmailV2([]string{emailTo}, "Ente", "legacy@ente.io", - title, BaseTemplate, templateName, templateData, inlineImages) + emailDatas, err := c.createEmailData(legacyUser, trustedUser, newStatus) if err != nil { - log.WithError(err).WithField("state", newStatus).Error("failed to send email") return stacktrace.Propagate(err, "") } + + for _, data := range emailDatas { + content := data + err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "legacy@ente.io", + content.title, BaseTemplate, content.templateName, content.templateData, content.inlineImages) + if err != nil { + log.WithError(err).WithFields(log.Fields{ + "state": newStatus, + "to": content.emailTo, + "template": content.templateName, + }).Error("failed to send email") + return stacktrace.Propagate(err, "") + } + } + return nil } From cbe105020b52d6e7b5c62454b6d0e799144f9ff8 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:21:32 +0530 Subject: [PATCH 07/12] Update emails --- .../mail-templates/legacy/legacy_invite.html | 10 +- ...ed.html => recovery_completed_legacy.html} | 0 .../legacy/recovery_completed_trusted.html | 8 + .../legacy/recovery_ready_legacy.html | 4 +- .../legacy/recovery_ready_trusted.html | 5 +- .../legacy/recovery_rejected.html | 4 +- .../legacy/recovery_reminder.html | 6 +- server/pkg/controller/emergency/email.go | 159 +++++++++++------- server/pkg/utils/email/email.go | 1 + 9 files changed, 124 insertions(+), 73 deletions(-) rename server/mail-templates/legacy/{recovery_completed.html => recovery_completed_legacy.html} (100%) create mode 100644 server/mail-templates/legacy/recovery_completed_trusted.html diff --git a/server/mail-templates/legacy/legacy_invite.html b/server/mail-templates/legacy/legacy_invite.html index 4f3e0eadb5..2f5f597657 100644 --- a/server/mail-templates/legacy/legacy_invite.html +++ b/server/mail-templates/legacy/legacy_invite.html @@ -1,10 +1,10 @@ {{define "content"}} -Hey {{.TrustedUser}}!
+Hello,
-{{.LegacyUser}} has invited you to be their trusted contact.
+{{.LegacyContact}} has invited you to be their trusted contact.
-As a trusted contact, you can recover {{.LegacyUser}}'s account in their absence.
-To accept the invite, please open the Ente Photos app, and navigate to Settings -> Account -> Legacy .
+As a trusted contact, you can recover {{.LegacyContact}}'s account in their absence.
+To accept the invite, please open the Ente Photos app, and navigate to Settings > Account > Legacy.
-If you need any help, please reply to this email or write to support@ente.io.
+If you need help, please reply to this email.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_completed.html b/server/mail-templates/legacy/recovery_completed_legacy.html similarity index 100% rename from server/mail-templates/legacy/recovery_completed.html rename to server/mail-templates/legacy/recovery_completed_legacy.html diff --git a/server/mail-templates/legacy/recovery_completed_trusted.html b/server/mail-templates/legacy/recovery_completed_trusted.html new file mode 100644 index 0000000000..1dc9a9bf9b --- /dev/null +++ b/server/mail-templates/legacy/recovery_completed_trusted.html @@ -0,0 +1,8 @@ +{{define "content"}} +Hey {{.TrustedContact}}!
+ +You can now access {{.LegacyContact}}'s account with the new password you setup on the Ente Photos app.
+Please save the new password so that you can access the account in the future.
+ +If you need any help, please reply to this email or write to support@ente.io.
+{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_ready_legacy.html b/server/mail-templates/legacy/recovery_ready_legacy.html index 738fa7e4fb..0b32083511 100644 --- a/server/mail-templates/legacy/recovery_ready_legacy.html +++ b/server/mail-templates/legacy/recovery_ready_legacy.html @@ -1,7 +1,7 @@ {{define "content"}}Hey {{.LegacyUser}}!
-{{.TrustedUser}} can now change the password of your account.
+{{.TrustedUser}} can now recover your account by changing the password. -
If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_ready_trusted.html b/server/mail-templates/legacy/recovery_ready_trusted.html index e26ab20208..2eef93d54c 100644 --- a/server/mail-templates/legacy/recovery_ready_trusted.html +++ b/server/mail-templates/legacy/recovery_ready_trusted.html @@ -1,7 +1,8 @@ {{define "content"}}Hey {{.TrustedUser}}!
-You can now change the password for {{.LegacyUser}}.
+You can now recover {{.LegacyUser}}'s account.
+To change the password to {{.LegacyUser}}'s account, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_rejected.html b/server/mail-templates/legacy/recovery_rejected.html index 3735b79109..c2d7e62ebf 100644 --- a/server/mail-templates/legacy/recovery_rejected.html +++ b/server/mail-templates/legacy/recovery_rejected.html @@ -1,7 +1,7 @@ {{define "content"}}Hey {{.TrustedUser}}!
-{{.LegacyUser}} has rejected your attempt to recovery their account.
+{{.LegacyUser}} has blocked your request to recover their account as their trusted contact.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/mail-templates/legacy/recovery_reminder.html b/server/mail-templates/legacy/recovery_reminder.html index 5ed9b41f30..dc74893bae 100644 --- a/server/mail-templates/legacy/recovery_reminder.html +++ b/server/mail-templates/legacy/recovery_reminder.html @@ -1,9 +1,9 @@ {{define "content"}}Hey {{.LegacyUser}}!
-{{.TrustedUser}} had started the process to recover your account.
+{{.TrustedUser}} has initiated recovery on your account. After 2 days, they would be able to change the password and access your account.
-They will be able to change your password after {{.TimeDuration}}.
+If you want to block the recovery, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
-If you need help with anything, please write back!
+If you need any help, please reply to this email or write to support@ente.io.
{{end}} \ No newline at end of file diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index 90dd6e0442..5cdc71daf4 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -12,21 +12,23 @@ import ( const ( BaseTemplate string = "legacy/legacy_base.html" InviteTemplate string = "legacy/legacy_invite.html" - InviteSentTemplate string = "legacy/legacy_invite_sent.html" - RemovedTemplate string = "legacy/legacy_removed.html" - LeftTemplate string = "legacy/legacy_left.html" AcceptedTemplate string = "legacy/legacy_invite_accepted.html" RejectedInviteTemplate string = "legacy/legacy_invite_rejected.html" + InviteSentTemplate string = "legacy/legacy_invite_sent.html" + LeftTemplate string = "legacy/legacy_left.html" + RemovedTemplate string = "legacy/legacy_removed.html" - RecoveryStartedTemplate string = "legacy/recovery_started.html" - RecoveryRejectedTemplate string = "legacy/recovery_rejected.html" - RecoveryCancelledTemplate string = "legacy/recovery_cancelled.html" - RecoveryReminderTemplate string = "legacy/recovery_reminder.html" + RecoveryCancelledTemplate string = "legacy/recovery_cancelled.html" + RecoveryCompletedTemplate string = "legacy/recovery_completed_trusted.html" + RecoveryCompletedLegacyTemplate string = "legacy/recovery_completed_legacy.html" - RecoveryCompletedTemplate string = "legacy/recovery_completed.html" RecoveryReadyLegacyTemplate string = "legacy/recovery_ready_legacy.html" RecoveryReadyTrustedTemplate string = "legacy/recovery_ready_trusted.html" + RecoveryRejectedTemplate string = "legacy/recovery_rejected.html" + RecoveryReminderTemplate string = "legacy/recovery_reminder.html" + RecoveryStartedTemplate string = "legacy/recovery_started.html" + HappyHeaderImage = "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" SadHeaderImage = "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" ) @@ -132,76 +134,115 @@ func (c *Controller) sendContactNotification(ctx context.Context, legacyUserID i return nil } -func (c *Controller) sendRecoveryNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.RecoveryStatus) error { - legacyUser, err := c.UserRepo.Get(legacyUserID) - if err != nil { - return stacktrace.Propagate(err, "") - } - trustedUser, err := c.UserRepo.Get(trustedUserID) - if err != nil { - return stacktrace.Propagate(err, "") - } +func (c *Controller) createRecoveryEmailData(legacyUser, trustedUser ente.User, newStatus ente.RecoveryStatus) ([]emailData, error) { templateData := map[string]interface{}{ "LegacyUser": legacyUser.Email, "TrustedUser": trustedUser.Email, } - var templateName, emailTo, title string - var inlineImages []map[string]interface{} - inlineImage := make(map[string]interface{}) - inlineImage["mime_type"] = "image/png" - inlineImage["cid"] = "header-image" + var emailDatas []emailData + inlineImage := map[string]interface{}{ + "mime_type": "image/png", + "cid": "header-image", + } - if newStatus == ente.RecoveryStatusInitiated { - templateName = RecoveryStartedTemplate - title = fmt.Sprintf("CRITICAL: %s has initiated recovery process for your account", trustedUser.Email) - emailTo = legacyUser.Email + switch newStatus { + case ente.RecoveryStatusInitiated: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("CRITICAL: %s has initiated recovery process for your account", trustedUser.Email), + templateName: RecoveryStartedTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = HappyHeaderImage - } else if newStatus == ente.RecoveryStatusRecovered { - emailTo = legacyUser.Email - templateName = RecoveryCompletedTemplate - title = fmt.Sprintf("Your account has been successfully recovered by %s", trustedUser.Email) + case ente.RecoveryStatusRecovered: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("Your account has been successfully recovered by %s", trustedUser.Email), + templateName: RecoveryCompletedTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.RecoveryStatusStopped { - emailTo = legacyUser.Email - templateName = RecoveryCancelledTemplate - title = fmt.Sprintf("%s has cancelled recovery process", trustedUser.Email) + case ente.RecoveryStatusStopped: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("%s has cancelled recovery process", trustedUser.Email), + templateName: RecoveryCancelledTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.RecoveryStatusRejected { - emailTo = trustedUser.Email - templateName = RecoveryRejectedTemplate - title = fmt.Sprintf("%s has declined your recovery attempt", legacyUser.Email) + case ente.RecoveryStatusRejected: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("%s has declined your recovery attempt", legacyUser.Email), + templateName: RecoveryRejectedTemplate, + emailTo: trustedUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = SadHeaderImage - } else if newStatus == ente.RecoveryStatusWaiting { - emailTo = legacyUser.Email - templateName = RecoveryReminderTemplate - title = fmt.Sprintf("%s is recoverying your account!", trustedUser.Email) + case ente.RecoveryStatusWaiting: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("%s is recovering your account!", trustedUser.Email), + templateName: RecoveryReminderTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = HappyHeaderImage - } else if newStatus == ente.RecoveryStatusReady { - emailTo = trustedUser.Email - templateName = RecoveryReadyTrustedTemplate - title = fmt.Sprintf("You can now change password for %s", legacyUser.Email) + case ente.RecoveryStatusReady: + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("You can now change password for %s", legacyUser.Email), + templateName: RecoveryReadyTrustedTemplate, + emailTo: trustedUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) + emailDatas = append(emailDatas, emailData{ + title: fmt.Sprintf("%s can now change password for your account", trustedUser.Email), + templateName: RecoveryReadyLegacyTemplate, + emailTo: legacyUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) inlineImage["content"] = HappyHeaderImage - } else { - return stacktrace.Propagate(fmt.Errorf("unsupported status %s", newStatus), "") + default: + return nil, fmt.Errorf("unsupported status %s", newStatus) + } + + return emailDatas, nil +} + +func (c *Controller) sendRecoveryNotification(ctx context.Context, legacyUserID int64, trustedUserID int64, newStatus ente.RecoveryStatus) error { + legacyUser, err := c.UserRepo.Get(legacyUserID) + if err != nil { + return stacktrace.Propagate(err, "") } - inlineImages = append(inlineImages, inlineImage) - err = emailUtil.SendTemplatedEmailV2([]string{emailTo}, "Ente", "legacy@ente.io", - title, BaseTemplate, templateName, templateData, inlineImages) + trustedUser, err := c.UserRepo.Get(trustedUserID) + if err != nil { + return stacktrace.Propagate(err, "") + } + + emailDatas, err := c.createRecoveryEmailData(legacyUser, trustedUser, newStatus) if err != nil { - log.WithError(err).WithField("state", newStatus).Error("failed to send email") return stacktrace.Propagate(err, "") } - if newStatus == ente.RecoveryStatusReady { - emailTo = legacyUser.Email - templateName = RecoveryReadyLegacyTemplate - title = fmt.Sprintf("%s can now change password for your account", trustedUser.Email) - err = emailUtil.SendTemplatedEmailV2([]string{emailTo}, "Ente", "legacy@ente.io", - title, BaseTemplate, templateName, templateData, inlineImages) + + for _, data := range emailDatas { + content := data + err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "legacy@ente.io", + content.title, BaseTemplate, content.templateName, content.templateData, content.inlineImages) if err != nil { - log.WithError(err).WithField("state", newStatus).Error("failed to send email") + log.WithError(err).WithFields(log.Fields{ + "state": newStatus, + "to": content.emailTo, + "template": content.templateName, + }).Error("failed to send email") return stacktrace.Propagate(err, "") } } + return nil } diff --git a/server/pkg/utils/email/email.go b/server/pkg/utils/email/email.go index ca6d825a19..ca90fd48b4 100644 --- a/server/pkg/utils/email/email.go +++ b/server/pkg/utils/email/email.go @@ -12,6 +12,7 @@ import ( "html/template" "net/http" "net/smtp" + "path" "strings" "github.com/ente-io/museum/ente" From c648127ff876cae7a3750ec5cfadfa87bdcacddf Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:53:08 +0530 Subject: [PATCH 08/12] [server] Reject/Stop active recovery when contact is removed --- server/ente/emergency.go | 1 - server/pkg/controller/emergency/controller.go | 51 ++++++++++--------- server/pkg/repo/emergency/recovery.go | 19 +++++++ server/pkg/repo/emergency/repository.go | 3 +- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/server/ente/emergency.go b/server/ente/emergency.go index 57207e97dc..0dcab7ab17 100644 --- a/server/ente/emergency.go +++ b/server/ente/emergency.go @@ -33,7 +33,6 @@ const ( UserInvitedContact ContactState = "INVITED" UserRevokedContact ContactState = "REVOKED" ContactAccepted ContactState = "ACCEPTED" - ContactDeleted ContactState = "DELETED" ContactLeft ContactState = "CONTACT_LEFT" ContactDenied ContactState = "CONTACT_DENIED" ) diff --git a/server/pkg/controller/emergency/controller.go b/server/pkg/controller/emergency/controller.go index ed7c5cb396..1cf399f86e 100644 --- a/server/pkg/controller/emergency/controller.go +++ b/server/pkg/controller/emergency/controller.go @@ -2,6 +2,7 @@ package emergency import ( "fmt" + "github.com/ente-io/museum/ente" "github.com/ente-io/museum/pkg/controller/user" "github.com/ente-io/museum/pkg/repo" @@ -23,6 +24,33 @@ func (c *Controller) UpdateContact(ctx *gin.Context, if err := validateUpdateReq(userID, req); err != nil { return stacktrace.Propagate(err, "") } + if req.State == ente.ContactDenied || req.State == ente.ContactLeft || req.State == ente.UserRevokedContact { + activeSessions, sessionErr := c.Repo.GetActiveSessions(ctx, req.UserID, req.EmergencyContactID) + if sessionErr != nil { + return stacktrace.Propagate(sessionErr, "") + } + for _, session := range activeSessions { + if req.State == ente.UserRevokedContact { + rejErr := c.RejectRecovery(ctx, userID, ente.RecoveryIdentifier{ + ID: session.ID, + UserID: session.UserID, + EmergencyContactID: session.EmergencyContactID, + }) + if rejErr != nil { + return stacktrace.Propagate(rejErr, "failed to reject recovery") + } + } else { + stopErr := c.StopRecovery(ctx, userID, ente.RecoveryIdentifier{ + ID: session.ID, + UserID: session.UserID, + EmergencyContactID: session.EmergencyContactID, + }) + if stopErr != nil { + return stacktrace.Propagate(stopErr, "failed to stop recovery") + } + } + } + } hasUpdate, err := c.Repo.UpdateState(ctx, req.UserID, req.EmergencyContactID, req.State) if !hasUpdate { log.WithField("userID", userID).WithField("req", req). @@ -30,12 +58,6 @@ func (c *Controller) UpdateContact(ctx *gin.Context, } else { go c.sendContactNotification(ctx, req.UserID, req.EmergencyContactID, req.State) } - recoverStatus := getNextRecoveryStatusFromContactState(req.State) - if recoverStatus != nil { - if err := c.Repo.UpdateRecoveryStatus(ctx, req.UserID, req.EmergencyContactID, *recoverStatus); err != nil { - return stacktrace.Propagate(err, "") - } - } if err != nil { return stacktrace.Propagate(err, "") } @@ -66,20 +88,3 @@ func validateUpdateReq(userID int64, req ente.UpdateContact) error { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("Can not update state to %s", req.State)), "") } } - -// When a user contact state is update, we need to update the recovery status for any ongoing recovery -func getNextRecoveryStatusFromContactState(state ente.ContactState) *ente.RecoveryStatus { - switch state { - case ente.ContactAccepted: - return nil - case ente.UserInvitedContact: - return nil - case ente.ContactLeft: - return ente.RecoveryStatusStopped.Ptr() - case ente.ContactDenied: - return ente.RecoveryStatusStopped.Ptr() - case ente.UserRevokedContact: - return ente.RecoveryStatusRejected.Ptr() - } - return nil -} diff --git a/server/pkg/repo/emergency/recovery.go b/server/pkg/repo/emergency/recovery.go index 9d5a363b0f..dc41c3d5ff 100644 --- a/server/pkg/repo/emergency/recovery.go +++ b/server/pkg/repo/emergency/recovery.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "github.com/ente-io/museum/ente" "github.com/ente-io/museum/pkg/utils/time" "github.com/ente-io/stacktrace" @@ -65,6 +66,24 @@ FROM emergency_recovery WHERE (user_id=$1 OR emergency_contact_id=$1) AND statu return sessions, nil } +func (repo *Repository) GetActiveSessions(ctx *gin.Context, userID int64, emergencyContactID int64) ([]*RecoverRow, error) { + rows, err := repo.DB.QueryContext(ctx, `SELECT id, user_id, emergency_contact_id, status, wait_till, next_reminder_at, created_at +FROM emergency_recovery WHERE user_id=$1 and emergency_contact_id=$2 AND status= ANY($3)`, userID, emergencyContactID, pq.Array([]ente.RecoveryStatus{ente.RecoveryStatusWaiting, ente.RecoveryStatusReady})) + if err != nil { + return nil, stacktrace.Propagate(err, "") + } + defer rows.Close() + var sessions []*RecoverRow + for rows.Next() { + var row RecoverRow + if err := rows.Scan(&row.ID, &row.UserID, &row.EmergencyContactID, &row.Status, &row.WaitTill, &row.NextReminderAt, &row.CreatedAt); err != nil { + return nil, stacktrace.Propagate(err, "") + } + sessions = append(sessions, &row) + } + return sessions, nil +} + func (repo *Repository) UpdateRecoveryStatusForID(ctx context.Context, sessionID uuid.UUID, status ente.RecoveryStatus) (bool, error) { validPrevStatus := validPreviousStatus(status) var result sql.Result diff --git a/server/pkg/repo/emergency/repository.go b/server/pkg/repo/emergency/repository.go index eb64a3d275..5bf4eba9c3 100644 --- a/server/pkg/repo/emergency/repository.go +++ b/server/pkg/repo/emergency/repository.go @@ -118,8 +118,7 @@ func getValidPreviousState(cs ente.ContactState) []ente.ContactState { return []ente.ContactState{ente.UserInvitedContact} case ente.UserRevokedContact: return []ente.ContactState{ente.UserInvitedContact, ente.ContactAccepted} - case ente.ContactDeleted: - return []ente.ContactState{ente.UserInvitedContact, ente.ContactAccepted} + } panic("invalid state") } From 38d679f57462cecc283552cf3a7c6232daa00ad0 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:32:10 +0530 Subject: [PATCH 09/12] [server] Clean up emergency contacts on account deletion --- server/cmd/museum/main.go | 14 ++++---- server/pkg/api/admin.go | 9 +++++ server/pkg/api/user.go | 15 +++++++- server/pkg/controller/emergency/controller.go | 35 +++++++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index 74b9d75e66..8d8bb00002 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -461,8 +461,14 @@ func main() { privateAPI.POST("/trash/delete", trashHandler.Delete) privateAPI.POST("/trash/empty", trashHandler.Empty) + emergencyCtrl := &emergency.Controller{ + Repo: &emergencyRepo.Repository{DB: db}, + UserRepo: userRepo, + UserCtrl: userController, + } userHandler := &api.UserHandler{ - UserController: userController, + UserController: userController, + EmergencyController: emergencyCtrl, } publicAPI.POST("/users/ott", userHandler.SendOTT) publicAPI.POST("/users/verify-email", userHandler.VerifyEmail) @@ -606,11 +612,6 @@ func main() { familiesJwtAuthAPI.DELETE("/family/remove-member/:id", familyHandler.RemoveMember) familiesJwtAuthAPI.DELETE("/family/revoke-invite/:id", familyHandler.RevokeInvite) - emergencyCtrl := &emergency.Controller{ - Repo: &emergencyRepo.Repository{DB: db}, - UserRepo: userRepo, - UserCtrl: userController, - } emergencyHandler := &api.EmergencyHandler{ Controller: emergencyCtrl, } @@ -665,6 +666,7 @@ func main() { UserAuthRepo: userAuthRepo, UserController: userController, FamilyController: familyController, + EmergencyController: emergencyCtrl, RemoteStoreController: remoteStoreController, FileRepo: fileRepo, StorageBonusRepo: storagBonusRepo, diff --git a/server/pkg/api/admin.go b/server/pkg/api/admin.go index 4e91321777..124d21078f 100644 --- a/server/pkg/api/admin.go +++ b/server/pkg/api/admin.go @@ -3,6 +3,7 @@ package api import ( "errors" "fmt" + "github.com/ente-io/museum/pkg/controller/emergency" "github.com/ente-io/museum/pkg/controller/remotestore" "github.com/ente-io/museum/pkg/repo/authenticator" "net/http" @@ -47,6 +48,7 @@ type AdminHandler struct { StorageBonusRepo *storagebonus.Repository BillingController *controller.BillingController UserController *user.UserController + EmergencyController *emergency.Controller FamilyController *family.Controller RemoteStoreController *remotestore.Controller ObjectCleanupController *controller.ObjectCleanupController @@ -182,6 +184,13 @@ func (h *AdminHandler) DeleteUser(c *gin.Context) { "req_id": requestid.Get(c), "req_ctx": "account_deletion", }) + + // todo: (neeraj) refactor this part, currently there's a circular dependency between user and emergency controllers + removeLegacyErr := h.EmergencyController.HandleAccountDeletion(c, user.ID, logger) + if removeLegacyErr != nil { + handler.Error(c, stacktrace.Propagate(removeLegacyErr, "")) + return + } response, err := h.UserController.HandleAccountDeletion(c, user.ID, logger) if err != nil { handler.Error(c, stacktrace.Propagate(err, "")) diff --git a/server/pkg/api/user.go b/server/pkg/api/user.go index f0ede26f2f..930ea6ec8c 100644 --- a/server/pkg/api/user.go +++ b/server/pkg/api/user.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "fmt" + "github.com/ente-io/museum/pkg/controller/emergency" "github.com/gin-contrib/requestid" "github.com/sirupsen/logrus" "net/http" @@ -22,7 +23,8 @@ import ( // UserHandler exposes request handlers for all user related requests type UserHandler struct { - UserController *user.UserController + UserController *user.UserController + EmergencyController *emergency.Controller } // SendOTT generates and sends an OTT to the provided email address @@ -529,6 +531,17 @@ func (h *UserHandler) DeleteUser(c *gin.Context) { handler.Error(c, stacktrace.Propagate(err, "Could not bind request params")) return } + // todo: (neeraj) refactor this part, currently there's a circular dependency between user and emergency controllers + removeLegacyErr := h.EmergencyController.HandleAccountDeletion(c, auth.GetUserID(c.Request.Header), + logrus.WithFields(logrus.Fields{ + "user_id": auth.GetUserID(c.Request.Header), + "req_id": requestid.Get(c), + "req_ctx": "self_account_deletion", + })) + if removeLegacyErr != nil { + handler.Error(c, stacktrace.Propagate(removeLegacyErr, "")) + return + } response, err := h.UserController.SelfDeleteAccount(c, request) if err != nil { handler.Error(c, stacktrace.Propagate(err, "")) diff --git a/server/pkg/controller/emergency/controller.go b/server/pkg/controller/emergency/controller.go index 1cf399f86e..fb590bb984 100644 --- a/server/pkg/controller/emergency/controller.go +++ b/server/pkg/controller/emergency/controller.go @@ -64,6 +64,41 @@ func (c *Controller) UpdateContact(ctx *gin.Context, return nil } +func (c *Controller) HandleAccountDeletion(ctx *gin.Context, userID int64, logger *log.Entry) error { + logger.Info("Clean up emergency contacts on account deletion") + contacts, err := c.Repo.GetActiveContactForUser(ctx, userID) + if err != nil { + return stacktrace.Propagate(err, "") + } + if len(contacts) == 0 { + return nil + } + for _, contact := range contacts { + if contact.UserID == userID { + logger.Info("Removing emergency contact from user side") + removeErr := c.UpdateContact(ctx, userID, ente.UpdateContact{ + UserID: userID, + EmergencyContactID: contact.EmergencyContactID, + State: ente.UserRevokedContact, + }) + if removeErr != nil { + return stacktrace.Propagate(removeErr, "") + } + } else { + logger.Info("Removing user from emergency contact side") + leaveErr := c.UpdateContact(ctx, userID, ente.UpdateContact{ + UserID: contact.UserID, + EmergencyContactID: userID, + State: ente.ContactLeft, + }) + if leaveErr != nil { + return stacktrace.Propagate(leaveErr, "") + } + } + } + return nil +} + func validateUpdateReq(userID int64, req ente.UpdateContact) error { if req.EmergencyContactID == req.UserID { return stacktrace.Propagate(ente.NewBadRequestWithMessage("contact and user can not be same"), "") From 3be7c4a60fcafa3cca76e1243b57bf8bfb034c7c Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:49:26 +0530 Subject: [PATCH 10/12] Update templates --- server/mail-templates/legacy/legacy_invite.html | 2 +- .../mail-templates/legacy/legacy_invite_accepted.html | 10 +++++----- .../mail-templates/legacy/legacy_invite_rejected.html | 8 ++++---- server/mail-templates/legacy/legacy_invite_sent.html | 10 +++++----- server/mail-templates/legacy/legacy_left.html | 8 ++++---- server/mail-templates/legacy/legacy_removed.html | 8 ++++---- server/mail-templates/legacy/recovery_cancelled.html | 8 ++++---- .../legacy/recovery_completed_legacy.html | 8 ++++---- .../legacy/recovery_completed_trusted.html | 9 ++++----- .../mail-templates/legacy/recovery_ready_legacy.html | 8 ++++---- .../mail-templates/legacy/recovery_ready_trusted.html | 10 +++++----- server/mail-templates/legacy/recovery_rejected.html | 8 ++++---- server/mail-templates/legacy/recovery_reminder.html | 10 +++++----- server/mail-templates/legacy/recovery_started.html | 10 ++++++---- 14 files changed, 59 insertions(+), 58 deletions(-) diff --git a/server/mail-templates/legacy/legacy_invite.html b/server/mail-templates/legacy/legacy_invite.html index 2f5f597657..d033271173 100644 --- a/server/mail-templates/legacy/legacy_invite.html +++ b/server/mail-templates/legacy/legacy_invite.html @@ -7,4 +7,4 @@To accept the invite, please open the Ente Photos app, and navigate to Settings > Account > Legacy.
If you need help, please reply to this email.
-{{end}} \ No newline at end of file +{{end}} diff --git a/server/mail-templates/legacy/legacy_invite_accepted.html b/server/mail-templates/legacy/legacy_invite_accepted.html index 95624b8810..5044df609e 100644 --- a/server/mail-templates/legacy/legacy_invite_accepted.html +++ b/server/mail-templates/legacy/legacy_invite_accepted.html @@ -1,9 +1,9 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has accepted your request to be your trusted contact.
+{{.TrustedContact}} has accepted your request to be your trusted contact.
-As a trusted contact, {{.TrustedUser}} can recover your account in your absence.
+As a trusted contact, {{.TrustedContact}} can recover your account in your absence.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/legacy_invite_rejected.html b/server/mail-templates/legacy/legacy_invite_rejected.html index 971e4c853c..e770025781 100644 --- a/server/mail-templates/legacy/legacy_invite_rejected.html +++ b/server/mail-templates/legacy/legacy_invite_rejected.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has rejected your request to be your trusted contact.
+{{.TrustedContact}} has rejected your request to be a trusted contact.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email
+{{end}} diff --git a/server/mail-templates/legacy/legacy_invite_sent.html b/server/mail-templates/legacy/legacy_invite_sent.html index baf38445f8..16f2f2db42 100644 --- a/server/mail-templates/legacy/legacy_invite_sent.html +++ b/server/mail-templates/legacy/legacy_invite_sent.html @@ -1,10 +1,10 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-You have invited {{.TrustedyUser}} to be your trusted contact.
+You have invited {{.TrustedContact}} to be your trusted contact.
-As a trusted contact, {{.TrustedyUser}} can recover your account in your absence.
+As a trusted contact, {{.TrustedContact}} can recover your account in your absence.
If you want to cancel the invite, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/legacy_left.html b/server/mail-templates/legacy/legacy_left.html index 72d03dd95b..4f065f6253 100644 --- a/server/mail-templates/legacy/legacy_left.html +++ b/server/mail-templates/legacy/legacy_left.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has removed themselves from being your trusted contact.
+{{.TrustedContact}} has removed themselves from being your trusted contact.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/legacy_removed.html b/server/mail-templates/legacy/legacy_removed.html index 160dba288d..8b907a14d0 100644 --- a/server/mail-templates/legacy/legacy_removed.html +++ b/server/mail-templates/legacy/legacy_removed.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.TrustedUser}}!
+Hello,
-{{.LegacyUser}} has removed you as their trusted contact.
+{{.LegacyContact}} has removed you as their trusted contact.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_cancelled.html b/server/mail-templates/legacy/recovery_cancelled.html index 0e2a2828fd..6dce235043 100644 --- a/server/mail-templates/legacy/recovery_cancelled.html +++ b/server/mail-templates/legacy/recovery_cancelled.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has cancelled the process of recovering your account
+{{.TrustedUser}} has cancelled the process of recovering your account.
-If you need help with anything, please write back!
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_completed_legacy.html b/server/mail-templates/legacy/recovery_completed_legacy.html index 4623393409..17d6f08f2b 100644 --- a/server/mail-templates/legacy/recovery_completed_legacy.html +++ b/server/mail-templates/legacy/recovery_completed_legacy.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has successfully changed password of your account.
+{{.TrustedContact}} has changed the password and can now access your account. -
If you need help with anything, please write back!
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_completed_trusted.html b/server/mail-templates/legacy/recovery_completed_trusted.html index 1dc9a9bf9b..268569f4f6 100644 --- a/server/mail-templates/legacy/recovery_completed_trusted.html +++ b/server/mail-templates/legacy/recovery_completed_trusted.html @@ -1,8 +1,7 @@ {{define "content"}} -Hey {{.TrustedContact}}!
+Hello,
-You can now access {{.LegacyContact}}'s account with the new password you setup on the Ente Photos app.
-Please save the new password so that you can access the account in the future.
+You can now access {{.LegacyContact}}'s account with the new password you setup on Ente.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need any help, please let us know by responding to this email, we'll be around.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_ready_legacy.html b/server/mail-templates/legacy/recovery_ready_legacy.html index 0b32083511..b521f69ab1 100644 --- a/server/mail-templates/legacy/recovery_ready_legacy.html +++ b/server/mail-templates/legacy/recovery_ready_legacy.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} can now recover your account by changing the password. +
{{.TrustedContact}} can now recover your account by changing the password. -
If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_ready_trusted.html b/server/mail-templates/legacy/recovery_ready_trusted.html index 2eef93d54c..e688f8a605 100644 --- a/server/mail-templates/legacy/recovery_ready_trusted.html +++ b/server/mail-templates/legacy/recovery_ready_trusted.html @@ -1,8 +1,8 @@ {{define "content"}} -Hey {{.TrustedUser}}!
+Hello,
-You can now recover {{.LegacyUser}}'s account.
-To change the password to {{.LegacyUser}}'s account, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
+You can now recover {{.LegacyContact}}'s account.
+To change the password to {{.LegacyContact}}'s account, please navigate to Settings > Account > Legacy in the Ente Photos app.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_rejected.html b/server/mail-templates/legacy/recovery_rejected.html index c2d7e62ebf..ecb56dd963 100644 --- a/server/mail-templates/legacy/recovery_rejected.html +++ b/server/mail-templates/legacy/recovery_rejected.html @@ -1,7 +1,7 @@ {{define "content"}} -Hey {{.TrustedUser}}!
+Hello,
-{{.LegacyUser}} has blocked your request to recover their account as their trusted contact.
+{{.LegacyContact}} has blocked your request to recover their account.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email
+{{end}} diff --git a/server/mail-templates/legacy/recovery_reminder.html b/server/mail-templates/legacy/recovery_reminder.html index dc74893bae..3c7a35c496 100644 --- a/server/mail-templates/legacy/recovery_reminder.html +++ b/server/mail-templates/legacy/recovery_reminder.html @@ -1,9 +1,9 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has initiated recovery on your account. After 2 days, they would be able to change the password and access your account.
+{{.TrustedContact}} has initiated recovery on your account. After 2 days, they will be able to change the password and access your account.
-If you want to block the recovery, please navigate to Settings -> Account -> Legacy in the Ente Photos app.
+If you want to block the recovery, please navigate to Settings > Account > Legacy in the Ente Photos app.
-If you need any help, please reply to this email or write to support@ente.io.
-{{end}} \ No newline at end of file +If you need help, please reply to this email.
+{{end}} diff --git a/server/mail-templates/legacy/recovery_started.html b/server/mail-templates/legacy/recovery_started.html index 5136abc455..13a12c5ff8 100644 --- a/server/mail-templates/legacy/recovery_started.html +++ b/server/mail-templates/legacy/recovery_started.html @@ -1,7 +1,9 @@ {{define "content"}} -Hey {{.LegacyUser}}!
+Hello,
-{{.TrustedUser}} has started the process to recover your account.
+{{.TrustedContact}} has initiated recovery on your account. After 30 days, they will be able to change the password and access your account.
-If you need help with anything, please write back!
-{{end}} \ No newline at end of file +If you want to block the recovery, please navigate to Settings > Account > Legacy in the Ente Photos app.
+ +If you need help, please reply to this email.
+{{end}} From eaee515e174478d60ee08ae923d26f79bdea191f Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:01:06 +0530 Subject: [PATCH 11/12] Update templates --- server/pkg/controller/emergency/email.go | 59 ++++++++++++------------ 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index 5cdc71daf4..dea69ea248 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -18,9 +18,9 @@ const ( LeftTemplate string = "legacy/legacy_left.html" RemovedTemplate string = "legacy/legacy_removed.html" - RecoveryCancelledTemplate string = "legacy/recovery_cancelled.html" - RecoveryCompletedTemplate string = "legacy/recovery_completed_trusted.html" - RecoveryCompletedLegacyTemplate string = "legacy/recovery_completed_legacy.html" + RecoveryCancelledTemplate string = "legacy/recovery_cancelled.html" + RecoveryCompletedTrustedTemplate string = "legacy/recovery_completed_trusted.html" + RecoveryCompletedLegacyTemplate string = "legacy/recovery_completed_legacy.html" RecoveryReadyLegacyTemplate string = "legacy/recovery_ready_legacy.html" RecoveryReadyTrustedTemplate string = "legacy/recovery_ready_trusted.html" @@ -28,9 +28,6 @@ const ( RecoveryRejectedTemplate string = "legacy/recovery_rejected.html" RecoveryReminderTemplate string = "legacy/recovery_reminder.html" RecoveryStartedTemplate string = "legacy/recovery_started.html" - - HappyHeaderImage = "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" - SadHeaderImage = "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" ) type emailData struct { @@ -43,22 +40,22 @@ type emailData struct { func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatus ente.ContactState) ([]emailData, error) { templateData := map[string]interface{}{ - "LegacyUser": legacyUser.Email, - "TrustedUser": trustedUser.Email, + "LegacyContact": legacyUser.Email, + "TrustedContact": trustedUser.Email, } var emailContent []emailData switch newStatus { case ente.UserInvitedContact: emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("%s has added you as a Trusted Contact", legacyUser.Email), + title: "You have been added as a trusted contact", templateName: InviteTemplate, emailTo: trustedUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{}, }) emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("You have added %s as a Trusted Contact", trustedUser.Email), + title: "Trusted contact invited", templateName: InviteSentTemplate, emailTo: legacyUser.Email, templateData: templateData, @@ -66,7 +63,7 @@ func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatu }) case ente.UserRevokedContact: emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("%s has removed you as a Trusted Contact", legacyUser.Email), + title: "Legacy account access removed", templateName: RemovedTemplate, emailTo: trustedUser.Email, templateData: templateData, @@ -74,7 +71,7 @@ func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatu }) case ente.ContactLeft: emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("%s has removed themselves as a Trusted Contact", trustedUser.Email), + title: "Trusted contact removed", templateName: LeftTemplate, emailTo: legacyUser.Email, templateData: templateData, @@ -82,7 +79,7 @@ func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatu }) case ente.ContactDenied: emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("%s has rejected your request to be a Trusted Contact", trustedUser.Email), + title: "Legacy invite rejected", templateName: RejectedInviteTemplate, emailTo: legacyUser.Email, templateData: templateData, @@ -90,7 +87,7 @@ func (c *Controller) createEmailData(legacyUser, trustedUser ente.User, newStatu }) case ente.ContactAccepted: emailContent = append(emailContent, emailData{ - title: fmt.Sprintf("%s has accepted your request to be a Trusted Contact", trustedUser.Email), + title: "Trusted contact added", templateName: AcceptedTemplate, emailTo: legacyUser.Email, templateData: templateData, @@ -136,8 +133,8 @@ func (c *Controller) sendContactNotification(ctx context.Context, legacyUserID i func (c *Controller) createRecoveryEmailData(legacyUser, trustedUser ente.User, newStatus ente.RecoveryStatus) ([]emailData, error) { templateData := map[string]interface{}{ - "LegacyUser": legacyUser.Email, - "TrustedUser": trustedUser.Email, + "LegacyContact": legacyUser.Email, + "TrustedContact": trustedUser.Email, } var emailDatas []emailData @@ -149,65 +146,67 @@ func (c *Controller) createRecoveryEmailData(legacyUser, trustedUser ente.User, switch newStatus { case ente.RecoveryStatusInitiated: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("CRITICAL: %s has initiated recovery process for your account", trustedUser.Email), + title: "Ente account recovery initiated", templateName: RecoveryStartedTemplate, emailTo: legacyUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = HappyHeaderImage case ente.RecoveryStatusRecovered: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("Your account has been successfully recovered by %s", trustedUser.Email), - templateName: RecoveryCompletedTemplate, + title: "Ente account password reset", + templateName: RecoveryCompletedLegacyTemplate, emailTo: legacyUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = SadHeaderImage + emailDatas = append(emailDatas, emailData{ + title: "Ente account recovery successful", + templateName: RecoveryCompletedTrustedTemplate, + emailTo: trustedUser.Email, + templateData: templateData, + inlineImages: []map[string]interface{}{inlineImage}, + }) + case ente.RecoveryStatusStopped: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("%s has cancelled recovery process", trustedUser.Email), + title: "Ente account recovery cancelled", templateName: RecoveryCancelledTemplate, emailTo: legacyUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = SadHeaderImage case ente.RecoveryStatusRejected: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("%s has declined your recovery attempt", legacyUser.Email), + title: "Ente account recovery blocked", templateName: RecoveryRejectedTemplate, emailTo: trustedUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = SadHeaderImage case ente.RecoveryStatusWaiting: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("%s is recovering your account!", trustedUser.Email), + title: "Ente account recovery due", templateName: RecoveryReminderTemplate, emailTo: legacyUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = HappyHeaderImage case ente.RecoveryStatusReady: emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("You can now change password for %s", legacyUser.Email), + title: "Ente account recoverable", templateName: RecoveryReadyTrustedTemplate, emailTo: trustedUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) emailDatas = append(emailDatas, emailData{ - title: fmt.Sprintf("%s can now change password for your account", trustedUser.Email), + title: "Ente account recoverable", templateName: RecoveryReadyLegacyTemplate, emailTo: legacyUser.Email, templateData: templateData, inlineImages: []map[string]interface{}{inlineImage}, }) - inlineImage["content"] = HappyHeaderImage default: return nil, fmt.Errorf("unsupported status %s", newStatus) } From 58ae5eee329f3acba940b7006af407d9f6729777 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:01:35 +0530 Subject: [PATCH 12/12] Update from address --- server/pkg/controller/emergency/email.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/pkg/controller/emergency/email.go b/server/pkg/controller/emergency/email.go index dea69ea248..d6db9cc837 100644 --- a/server/pkg/controller/emergency/email.go +++ b/server/pkg/controller/emergency/email.go @@ -116,7 +116,7 @@ func (c *Controller) sendContactNotification(ctx context.Context, legacyUserID i for _, data := range emailDatas { content := data - err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "legacy@ente.io", + err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "team@ente.io", content.title, BaseTemplate, content.templateName, content.templateData, content.inlineImages) if err != nil { log.WithError(err).WithFields(log.Fields{ @@ -231,7 +231,7 @@ func (c *Controller) sendRecoveryNotification(ctx context.Context, legacyUserID for _, data := range emailDatas { content := data - err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "legacy@ente.io", + err = emailUtil.SendTemplatedEmailV2([]string{content.emailTo}, "Ente", "team@ente.io", content.title, BaseTemplate, content.templateName, content.templateData, content.inlineImages) if err != nil { log.WithError(err).WithFields(log.Fields{