From f480ce4ee3be264f40be8c9b4d2ddb8a7215119c Mon Sep 17 00:00:00 2001 From: urko Date: Wed, 19 Feb 2025 18:47:11 +0100 Subject: [PATCH] feat; add notify by email script --- cmd/collaborator/main.go | 2 +- cmd/comments/main.go | 2 +- cmd/gogscsv/main.go | 2 +- cmd/issue/main.go | 2 +- cmd/notify/main.go | 151 +++++++++++++++++++++++++++++++ cmd/org/main.go | 2 +- cmd/users/main.go | 4 +- config/app.test.yml | 19 ++++ {kit/config => config}/config.go | 42 +++++++-- go.mod | 17 +++- go.sum | 34 ++++++- internal/mail/mail.go | 22 +++++ internal/mail/mail_test.go | 28 ++++++ 13 files changed, 308 insertions(+), 19 deletions(-) create mode 100644 cmd/notify/main.go create mode 100644 config/app.test.yml rename {kit/config => config}/config.go (54%) create mode 100755 internal/mail/mail.go create mode 100644 internal/mail/mail_test.go diff --git a/cmd/collaborator/main.go b/cmd/collaborator/main.go index 5ed5af0..a364786 100644 --- a/cmd/collaborator/main.go +++ b/cmd/collaborator/main.go @@ -17,8 +17,8 @@ import ( "slices" "syscall" + "gitea.urkob.com/mcr-swiss/gogstea/config" "gitea.urkob.com/mcr-swiss/gogstea/internal/domain" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" ) var okStatuses = []int{ diff --git a/cmd/comments/main.go b/cmd/comments/main.go index 8ab3d5d..e36f0a9 100644 --- a/cmd/comments/main.go +++ b/cmd/comments/main.go @@ -16,8 +16,8 @@ import ( "runtime" "syscall" + "gitea.urkob.com/mcr-swiss/gogstea/config" "gitea.urkob.com/mcr-swiss/gogstea/internal/domain" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" ) func main() { diff --git a/cmd/gogscsv/main.go b/cmd/gogscsv/main.go index b02fd4b..3e727be 100644 --- a/cmd/gogscsv/main.go +++ b/cmd/gogscsv/main.go @@ -11,7 +11,7 @@ import ( "syscall" "time" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" + "gitea.urkob.com/mcr-swiss/gogstea/config" _ "github.com/go-sql-driver/mysql" ) diff --git a/cmd/issue/main.go b/cmd/issue/main.go index ade1e5d..308419f 100644 --- a/cmd/issue/main.go +++ b/cmd/issue/main.go @@ -18,8 +18,8 @@ import ( "syscall" "time" + "gitea.urkob.com/mcr-swiss/gogstea/config" "gitea.urkob.com/mcr-swiss/gogstea/internal/domain" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" ) func main() { diff --git a/cmd/notify/main.go b/cmd/notify/main.go new file mode 100644 index 0000000..dfece6c --- /dev/null +++ b/cmd/notify/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/smtp" + "net/url" + "os" + "os/signal" + "path" + "runtime" + "syscall" + "time" + + "gitea.urkob.com/mcr-swiss/gogstea/config" + "gitea.urkob.com/mcr-swiss/gogstea/internal/mail" + emailsender "gitea.urkob.com/urko/emailsender/pkg/email" +) + +func main() { + ctx, cancel := context.WithCancel(signalContext(context.Background())) + defer cancel() + + cfgFile := os.Getenv("CONFIG_FILE") + if cfgFile == "" { + // Get root path + _, filename, _, _ := runtime.Caller(0) + cfgFile = path.Join(path.Dir(filename), "configs", "app.yml") + } + cfg, err := config.LoadConfig(cfgFile) + if err != nil { + panic(err) + } + + ms := mail.NewMailService( + cfg.Email.From, + emailsender.NewSecure(emailsender.MailServiceConfig{ + Auth: smtp.PlainAuth("", cfg.Email.User, cfg.Email.Password, cfg.Email.Host), + Host: cfg.Email.Host, + Port: fmt.Sprint(cfg.Email.Port), + From: cfg.Email.From, + }), + ) + + wd, err := os.Getwd() + if err != nil { + panic(err) + } + outputFile, err := os.Create(wd + "/notification-results.txt") + if err != nil { + panic(err) + } + defer outputFile.Close() + + // http req + cli := http.DefaultClient + + parsedURL, err := url.Parse(cfg.Gitea.URL + "/admin/users") + if err != nil { + panic(err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, parsedURL.String(), http.NoBody) + if err != nil { + panic(err) + } + + req.Header.Add("Authorization", "token "+cfg.Gitea.ApiKey) + req.Header.Add("Content-Type", "application/json") + resp, err := cli.Do(req) + if err != nil { + panic(err) + } + + if resp.StatusCode != http.StatusOK { + bts, _ := io.ReadAll(resp.Body) + + if _, err := outputFile.WriteString(fmt.Sprintf("couln't get users | %d: %s\n", resp.StatusCode, string(bts))); err != nil { + panic(err) + } + } + + bts, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + + var users []struct { + ID int `json:"id"` + Login string `json:"login"` + LoginName string `json:"login_name"` + FullName string `json:"full_name"` + Email string `json:"email"` + AvatarURL string `json:"avatar_url"` + Language string `json:"language"` + IsAdmin bool `json:"is_admin"` + LastLogin time.Time `json:"last_login"` + Created time.Time `json:"created"` + Restricted bool `json:"restricted"` + Active bool `json:"active"` + ProhibitLogin bool `json:"prohibit_login"` + Location string `json:"location"` + Website string `json:"website"` + Description string `json:"description"` + Visibility string `json:"visibility"` + FollowersCount int `json:"followers_count"` + FollowingCount int `json:"following_count"` + StarredReposCount int `json:"starred_repos_count"` + Username string `json:"username"` + } + if err := json.Unmarshal(bts, &users); err != nil { + panic(err) + } + + // Loop over users and notify + for _, u := range users { + emailBody := "HTML MESSAGE" + // Send the new password via email. + // if err := ms.SendMsg(email, []byte(fmt.Sprintf("Esta esta es tu nueva contraseƱa: %s", newPassword))); err != nil { + if err := ms.Send( + u.Email, + "Password changed", + emailBody, + ); err != nil { + if _, err := outputFile.WriteString(fmt.Sprintf("Error sending email for user %s: %v", u.Email, err)); err != nil { + panic(err) + } + continue + } + } + +} + +func signalContext(ctx context.Context) context.Context { + ctx, cancel := context.WithCancel(ctx) + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigs + signal.Stop(sigs) + close(sigs) + cancel() + }() + + return ctx +} diff --git a/cmd/org/main.go b/cmd/org/main.go index e1a5b36..aaa54e7 100644 --- a/cmd/org/main.go +++ b/cmd/org/main.go @@ -16,8 +16,8 @@ import ( "runtime" "syscall" + "gitea.urkob.com/mcr-swiss/gogstea/config" "gitea.urkob.com/mcr-swiss/gogstea/internal/domain" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" ) func main() { diff --git a/cmd/users/main.go b/cmd/users/main.go index 63771f8..8619786 100644 --- a/cmd/users/main.go +++ b/cmd/users/main.go @@ -16,8 +16,8 @@ import ( "runtime" "syscall" + "gitea.urkob.com/mcr-swiss/gogstea/config" "gitea.urkob.com/mcr-swiss/gogstea/internal/domain" - "gitea.urkob.com/mcr-swiss/gogstea/kit/config" ) func main() { @@ -70,7 +70,7 @@ func main() { cli := http.DefaultClient - parsedURL, err := url.Parse(fmt.Sprintf(cfg.Gitea.URL + "/admin/users")) + parsedURL, err := url.Parse(cfg.Gitea.URL + "/admin/users") if err != nil { panic(err) } diff --git a/config/app.test.yml b/config/app.test.yml new file mode 100644 index 0000000..12d1d10 --- /dev/null +++ b/config/app.test.yml @@ -0,0 +1,19 @@ +gitea: + url: "https://gitea.imep.net/api/v1" + api_key: "257e1a0a9616d665b473999aad0b36cce995ea61" +users: + default_password: "D3f4ultCh4ng3Plz#" + csv_path: "/projects/work/mcr/code/gogstea/.notes/user.csv" +organizations: + csv_path: "/projects/work/mcr/code/gogstea/.notes/organizations.csv" +issues: + csv_path: "/projects/work/mcr/code/gogstea/.notes/issues.csv" +collaborators: + csv_path: "/projects/work/mcr/code/gogstea/.notes/collaborators.csv" +email: + from: no-reply@mcr-solutions.com + user: d0fb307c46b467f0532a49a1d91b1117 + host: in-v3.mailjet.com + password: "87fe4c4118f66969f681382df04cd54e" + port: 587 + \ No newline at end of file diff --git a/kit/config/config.go b/config/config.go similarity index 54% rename from kit/config/config.go rename to config/config.go index cd1a49d..9b5df84 100644 --- a/kit/config/config.go +++ b/config/config.go @@ -1,10 +1,13 @@ package config import ( - "os" + "flag" + "path" + "runtime" + "github.com/go-playground/validator/v10" + "github.com/jinzhu/configor" "go.uber.org/zap" - "gopkg.in/yaml.v3" ) type Config struct { @@ -28,20 +31,41 @@ type Config struct { Collaborators struct { CSVPath string `yaml:"csv_path"` } `yaml:"collaborators"` + Email struct { + Host string + From string + User string + Password string + Port uint + } } -func LoadConfig(path string) (*Config, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, err +// LoadConfig +func LoadConfig(c ...string) (Config, error) { + cfg := "app.yml" + test := flag.Lookup("test.v") != nil + if test { + cfg = "app.test.yml" } + if len(c) > 0 { + cfg = c[0] + } + + // Get root path + _, filename, _, _ := runtime.Caller(0) + var config Config - if err := yaml.Unmarshal(data, &config); err != nil { - return nil, err + if err := configor.Load(&config, path.Join(path.Dir(filename), cfg)); err != nil { + return config, err } - return &config, nil + v := validator.New() + if err := v.Struct(config); err != nil { + return config, err + } + + return config, nil } func NewZapCfg(dev bool) zap.Config { diff --git a/go.mod b/go.mod index be53f67..2d4140a 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,27 @@ module gitea.urkob.com/mcr-swiss/gogstea go 1.23.0 require ( + gitea.urkob.com/urko/emailsender v0.0.0-20240822131142-dca0236b06af + github.com/go-playground/validator/v10 v10.25.0 github.com/go-sql-driver/mysql v1.8.1 + github.com/jinzhu/configor v1.2.2 + github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.27.0 - gopkg.in/yaml.v3 v3.0.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 69b781a..850f58c 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,49 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +gitea.urkob.com/urko/emailsender v0.0.0-20240822131142-dca0236b06af h1:eb6YvnvawZ824BCAhD281Rwobx0+z5pEIAclsw8nrQc= +gitea.urkob.com/urko/emailsender v0.0.0-20240822131142-dca0236b06af/go.mod h1:E8lpo+yp+634C25vMCfz14bslbfkJyfzlgFsXimUu1g= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= +github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/jinzhu/configor v1.2.2 h1:sLgh6KMzpCmaQB4e+9Fu/29VErtBUqsS2t8C9BNIVsA= +github.com/jinzhu/configor v1.2.2/go.mod h1:iFFSfOBKP3kC2Dku0ZGB3t3aulfQgTGJknodhFavsU8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/mail/mail.go b/internal/mail/mail.go new file mode 100755 index 0000000..e1a164f --- /dev/null +++ b/internal/mail/mail.go @@ -0,0 +1,22 @@ +package mail + +import ( + "gitea.urkob.com/urko/emailsender/pkg/email" +) + +type EmailService struct { + emailService *email.EmailService + from string +} + +func NewMailService(from string, emailService *email.EmailService) EmailService { + return EmailService{from: from, emailService: emailService} +} + +func (srv EmailService) Send(to, subject, body string) error { + return srv.emailService.SendEmail(email.EmailMessage{ + To: to, + Subject: subject, + Body: body, + }) +} diff --git a/internal/mail/mail_test.go b/internal/mail/mail_test.go new file mode 100644 index 0000000..0fc9a2e --- /dev/null +++ b/internal/mail/mail_test.go @@ -0,0 +1,28 @@ +package mail + +import ( + "fmt" + "net/smtp" + "testing" + + "gitea.urkob.com/mcr-swiss/gogstea/config" + emailsender "gitea.urkob.com/urko/emailsender/pkg/email" + "github.com/stretchr/testify/require" +) + +func TestSend(t *testing.T) { + cfg, err := config.LoadConfig() + require.NoError(t, err) + + ms := NewMailService( + cfg.Email.From, + emailsender.NewSecure(emailsender.MailServiceConfig{ + Auth: smtp.PlainAuth("", cfg.Email.User, cfg.Email.Password, cfg.Email.Host), + Host: cfg.Email.Host, + Port: fmt.Sprint(cfg.Email.Port), + From: cfg.Email.From, + }), + ) + + require.NoError(t, ms.Send("urko@fungimail.com", "ss", "asdfsd")) +}