feat(worker): integrate GeoIP functionality into email notifications and enhance email template with region information

This commit is contained in:
keven1024
2026-05-23 21:25:58 +08:00
parent 0cb30b6293
commit 9a9418c564
11 changed files with 3161 additions and 2065 deletions

View File

@@ -2,6 +2,9 @@ go 1.25.5
use (
./backend
./pkg/geoip
./pkg/i18n
./pkg/mail
./pkg/models
./pkg/services
./pkg/utils

View File

@@ -7,7 +7,7 @@
"dev": "concurrently -n front,backend,worker -c blue,green,yellow 'pnpm:dev:front' 'pnpm:dev:backend' 'pnpm:dev:worker'",
"dev:front": "cd front && pnpm run dev",
"dev:backend": "cd backend && air",
"dev:worker": "cd worker && air",
"dev:worker": "pnpm gen:mail && cd worker && air",
"lint": "concurrently -n front,backend,worker -c blue,green,yellow 'pnpm:lint:front' 'pnpm:lint:backend' 'pnpm:lint:worker'",
"lint:front": "cd front && pnpm nuxt typecheck",
"lint:backend": "cd backend && golangci-lint run",
@@ -20,7 +20,8 @@
"up-deps:backend": "cd backend && go get -u",
"up-deps:worker": "cd worker && go get -u",
"up-deps:models": "cd pkg/models && go get -u",
"up-deps:utils": "cd pkg/utils && go get -u"
"up-deps:utils": "cd pkg/utils && go get -u",
"gen:mail": "cd pkg/mail && pnpm export"
},
"workspaces": [
"front"
@@ -41,7 +42,7 @@
"concurrently": "^9.2.1",
"husky": "^9.1.7",
"lint-staged": "^15.5.2",
"prettier": "^3.8.1"
"prettier": "^3.8.3"
},
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017"
}

View File

@@ -3,8 +3,11 @@ module pkg/geoip
go 1.25.5
require (
github.com/enescakir/emoji v1.0.0 // indirect
github.com/oschwald/geoip2-golang/v2 v2.1.0 // indirect
github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
golang.org/x/sys v0.38.0 // indirect
github.com/enescakir/emoji v1.0.0
github.com/oschwald/geoip2-golang/v2 v2.1.0
)
require (
github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect
golang.org/x/sys v0.42.0 // indirect
)

View File

@@ -1,8 +1,11 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo=
github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc=
github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc=
github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

5136
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
packages:
# all packages in direct subdirs of packages/
- 'front'
- 'pkg/*'
# - '*'

View File

@@ -6,6 +6,7 @@ require (
github.com/go-resty/resty/v2 v2.16.5
github.com/google/uuid v1.6.0
github.com/hibiken/asynq v0.26.0
github.com/mocktools/go-smtp-mock/v2 v2.5.4
github.com/openai/openai-go/v3 v3.30.0
github.com/samber/lo v1.53.0
github.com/spf13/cast v1.10.0
@@ -18,7 +19,6 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/mocktools/go-smtp-mock/v2 v2.5.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/redis/go-redis/v9 v9.18.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect

View File

@@ -40,6 +40,8 @@ github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=

View File

@@ -2,7 +2,9 @@ package services
import (
"fmt"
"net/url"
"pkg/i18n"
pkgmail "pkg/mail"
"pkg/models"
u "pkg/utils"
"strings"
@@ -44,8 +46,9 @@ func SendWebhook(webhook models.NotifyWebhook) error {
type EmailTemplateData struct {
Locale string
IP string
ShareType models.ShareType
Region string
FileName string
ShareType models.ShareType
}
func SendEmail(to string, emailTemplateData EmailTemplateData, options ...mail.Option) error {
@@ -63,16 +66,27 @@ func SendEmail(to string, emailTemplateData EmailTemplateData, options ...mail.O
if username == "" {
return fmt.Errorf("smtp.username is required")
}
port := lo.Ternary(cast.ToInt(smtp["port"]) != 0, cast.ToInt(smtp["port"]), mail.DefaultPortSSL)
templateData := map[string]any{
"IP": emailTemplateData.IP,
"SiteURL": u.GetEnv("site.url"),
"ShareType": i18n.T(emailTemplateData.Locale, lo.Ternary(emailTemplateData.ShareType == models.ShareTypeText, "share_type_text", "share_type_file")),
"FileName": emailTemplateData.FileName,
port := mail.DefaultPortSSL
if smtpPort := cast.ToInt(smtp["port"]); smtpPort != 0 {
port = smtpPort
}
p, err := url.Parse(u.GetEnv("site.url"))
subject := i18n.TWithData(emailTemplateData.Locale, "notify_email_subject", map[string]any{
"SiteURL": p.Host,
})
htmlBody, err := pkgmail.RenderMailTemplate("pull-notify", map[string]string{
"EMAIL-TITLE": subject,
"EMAIL-INTRO": i18n.T(emailTemplateData.Locale, "notify_email_intro"),
"EMAIL-FILEICON": lo.Ternary(emailTemplateData.ShareType == models.ShareTypeText, "spiral_notepad", "file_folder"),
"EMAIL-FILENAME": emailTemplateData.FileName,
"EMAIL-IP": emailTemplateData.IP,
"EMAIL-REGION": emailTemplateData.Region,
"EMAIL-LABEL-REGION": i18n.T(emailTemplateData.Locale, "notify_email_label_region"),
})
if err != nil {
return err
}
subject := i18n.TWithData(emailTemplateData.Locale, "notify_email_subject", templateData)
body := i18n.TWithData(emailTemplateData.Locale, "notify_email_body", templateData)
message := mail.NewMsg()
if err := message.From(username); err != nil {
return err
@@ -81,7 +95,7 @@ func SendEmail(to string, emailTemplateData EmailTemplateData, options ...mail.O
return err
}
message.Subject(subject)
message.SetBodyString(mail.TypeTextPlain, body)
message.SetBodyString(mail.TypeTextHTML, htmlBody)
options = append([]mail.Option{
mail.WithPort(port),

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"pkg/geoip"
"pkg/models"
u "pkg/utils"
"worker/internal/services"
@@ -77,12 +78,18 @@ func ShareNotify(ctx context.Context, task *asynq.Task) error {
successCount++
}
region := "-"
if info := geoip.GetIpGeoInfo(payload.IP); info != nil {
region = info.Emoji + " " + info.Country.Country.Names.English
}
for _, email := range shareInfo.NotifyEmails {
if err := services.SendEmail(email, services.EmailTemplateData{
Locale: shareInfo.Locale,
ShareType: shareInfo.Type,
FileName: shareInfo.FileName,
FileName: lo.Ternary(shareInfo.Type == models.ShareTypeFile, shareInfo.FileName, lo.Substring(shareInfo.Data, 0, 7)+"..."),
IP: payload.IP,
Region: region,
ShareType: shareInfo.Type,
}); err != nil {
errs = append(errs, err)
continue

View File

@@ -2,6 +2,7 @@ package main
import (
"log"
"pkg/geoip"
"pkg/i18n"
"pkg/utils"
"worker/internal/tasks"
@@ -26,6 +27,9 @@ func main() {
if err := i18n.Init(); err != nil {
log.Fatalf("failed to init i18n: %v", err)
}
if err := geoip.Init(); err != nil {
log.Fatalf("failed to init geoip: %v", err)
}
srv := asynq.NewServer(
utils.RedisURI2AsynqOpt(utils.GetEnv("redis.url")),